/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.olingo.service; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.olingo.commons.api.data.Parameter; import org.apache.olingo.commons.api.edm.EdmOperation; import org.apache.olingo.commons.api.edm.EdmParameter; import org.apache.olingo.commons.api.edm.EdmReturnType; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.uri.UriParameter; import org.apache.olingo.server.core.requests.ActionRequest; import org.teiid.core.TeiidProcessingException; import org.teiid.core.types.BlobImpl; import org.teiid.core.types.BlobType; import org.teiid.core.types.ClobImpl; import org.teiid.core.types.ClobType; import org.teiid.core.types.DataTypeManager; import org.teiid.core.types.InputStreamFactory; import org.teiid.core.types.JDBCSQLTypeInfo; import org.teiid.core.types.SQLXMLImpl; import org.teiid.core.types.XMLType; import org.teiid.metadata.MetadataStore; import org.teiid.metadata.Procedure; import org.teiid.metadata.ProcedureParameter; import org.teiid.metadata.Schema; import org.teiid.odata.api.ProcedureReturnType; import org.teiid.odata.api.SQLParameter; import org.teiid.olingo.common.ODataTypeManager; import org.teiid.olingo.service.TeiidServiceHandler.OperationParameterValueProvider; import org.teiid.query.sql.visitor.SQLStringVisitor; public class ProcedureSQLBuilder { final private List<SQLParameter> sqlParameters; private Procedure procedure; private ProcedureReturn procedureReturn; private OperationParameterValueProvider parameterValueProvider; static class ProcedureReturn implements ProcedureReturnType { private EdmReturnType type; private Integer sqlType = null; private boolean hasResultSet; public ProcedureReturn(EdmReturnType type, Integer sqlType, boolean hasResultSet) { this.type = type; this.sqlType = sqlType; this.hasResultSet = hasResultSet; } public EdmReturnType getReturnType() { return type; } public boolean hasResultSet() { return this.hasResultSet; } public Integer getSqlType() { return sqlType; } } public ProcedureSQLBuilder(MetadataStore metadata, EdmOperation edmOperation, OperationParameterValueProvider parameterProvider, ArrayList<SQLParameter> params) throws TeiidProcessingException { FullQualifiedName fqn = edmOperation.getFullQualifiedName(); String withoutVDB = fqn.getNamespace().substring(fqn.getNamespace().lastIndexOf('.')+1); Schema schema = metadata.getSchema(withoutVDB); this.procedure = schema.getProcedure(edmOperation.getName()); this.parameterValueProvider = parameterProvider; this.sqlParameters = params; visit(edmOperation); } public ProcedureReturn getReturn() { return this.procedureReturn; } private void visit(EdmOperation edmOperation) throws TeiidProcessingException { if (edmOperation.getReturnType() != null) { visit(edmOperation.getReturnType()); } for (String parameterName : edmOperation.getParameterNames()) { visit(edmOperation.getParameter(parameterName)); } } private boolean hasResultSet() { return this.procedure.getResultSet() != null; } private void visit(EdmReturnType returnType) { if (hasResultSet()) { // this is complex type this.procedureReturn = new ProcedureReturn(returnType, null, true); } else { // must not be null ProcedureParameter parameter = getReturnParameter(); Class<?> teiidType = DataTypeManager.getDataTypeClass(parameter.getRuntimeType()); Integer sqlType = JDBCSQLTypeInfo.getSQLType(DataTypeManager.getDataTypeName(teiidType)); this.procedureReturn = new ProcedureReturn(returnType, sqlType, false); } } private ProcedureParameter getReturnParameter() { for (ProcedureParameter parameter: this.procedure.getParameters()) { if (parameter.getType().equals(ProcedureParameter.Type.ReturnValue)) { return parameter; } } return null; } private void visit(EdmParameter edmParameter) throws TeiidProcessingException { Class<?> runtimeType = resolveParameterType(this.procedure.getName(), edmParameter.getName()); Integer sqlType = JDBCSQLTypeInfo.getSQLType(DataTypeManager.getDataTypeName(runtimeType)); Object value = this.parameterValueProvider.getValue(edmParameter, runtimeType); this.sqlParameters.add(new SQLParameter(edmParameter.getName(), value, sqlType)); } public String buildProcedureSQL() { StringBuilder sql = new StringBuilder(); // fully qualify the procedure name if (getReturn().hasResultSet()) { sql.append("{"); //$NON-NLS-1$ } else { sql.append("{? = "); //$NON-NLS-1$ } sql.append("call ").append(SQLStringVisitor.escapeSinglePart(this.procedure.getFullName())); //$NON-NLS-1$ sql.append("("); //$NON-NLS-1$ boolean first = true; for (SQLParameter parameter:this.sqlParameters) { if (!first) { sql.append(","); //$NON-NLS-1$ } first = false; sql.append(SQLStringVisitor.escapeSinglePart(parameter.getName())).append("=>?"); //$NON-NLS-1$ } sql.append(")"); //$NON-NLS-1$ sql.append("}"); //$NON-NLS-1$ return sql.toString(); } private Class<?> resolveParameterType(String procedureName, String parameterName) { for (ProcedureParameter pp : this.procedure.getParameters()) { if (pp.getName().equalsIgnoreCase(parameterName)) { return DataTypeManager.getDataTypeClass(pp.getRuntimeType()); } } return null; } static class ActionParameterValueProvider implements OperationParameterValueProvider { private InputStream payload; private boolean alreadyConsumed; private ActionRequest actionRequest; private List<Parameter> parameters; public ActionParameterValueProvider(InputStream payload, ActionRequest actionRequest) { this.payload = payload; this.actionRequest = actionRequest; } @Override public Object getValue(EdmParameter edmParameter, Class<?> runtimeType) throws TeiidProcessingException { if (!this.alreadyConsumed) { this.alreadyConsumed = true; if (DataTypeManager.isLOB(runtimeType)) { InputStreamFactory isf = new InputStreamFactory() { @Override public InputStream getInputStream() throws IOException { return payload; } }; if (runtimeType.isAssignableFrom(XMLType.class)) { return new SQLXMLImpl(isf); } else if (runtimeType.isAssignableFrom(ClobType.class)) { return new ClobImpl(isf, -1); } else if (runtimeType.isAssignableFrom(BlobType.class)) { return new BlobImpl(isf); } } else { try { this.parameters = this.actionRequest.getParameters(); } catch (DeserializerException e) { throw new TeiidProcessingException(e); } } } if (this.parameters != null && !this.parameters.isEmpty()) { for (Parameter parameter : this.parameters) { if (parameter.getName().equals(edmParameter.getName())) { // In Teiid one can only pass simple literal values, not complex // types, no complex parsing required. And LOBs can not be inlined // for Function return parameter.getValue(); } } } return null; } } static class FunctionParameterValueProvider implements OperationParameterValueProvider { private List<UriParameter> parameters; public FunctionParameterValueProvider(List<UriParameter> parameters) { this.parameters = parameters; } @Override public Object getValue(EdmParameter edmParameter, Class<?> runtimeType) throws TeiidProcessingException { for (UriParameter parameter : this.parameters) { if (parameter.getName().equals(edmParameter.getName())) { // In Teiid one can only pass simple literal values, not complex // types, no complex parsing required. And LOBs can not be inlined // for Function return ODataTypeManager.parseLiteral(edmParameter, runtimeType, parameter.getText()); } } return null; } } }