/** * The FunctionDescriptor describes a particular function instance enough to invoke the function. */ public class FunctionDescriptor implements Serializable, Cloneable { private static final long serialVersionUID = 5374103983118037242L; private static final boolean ALLOW_NAN_INFINITY = PropertiesUtils.getBooleanProperty( System.getProperties(), "org.teiid.allowNanInfinity", false); // $NON-NLS-1$ private Class<?>[] types; private Class<?> returnType; private boolean requiresContext; private FunctionMethod method; private String schema; // TODO: remove me - we need to create a proper schema for udf and system functions private boolean hasWrappedArgs; private boolean calledWithVarArgArrayParam; // TODO: could store this on the function and pass to invoke // This is transient as it would be useless to invoke this method in // a different VM. This function descriptor can be used to look up // the real VM descriptor for execution. private transient Method invocationMethod; FunctionDescriptor() {} FunctionDescriptor( FunctionMethod method, Class<?>[] types, Class<?> outputType, Method invocationMethod, boolean requiresContext) { this.types = types; this.returnType = outputType; this.invocationMethod = invocationMethod; this.requiresContext = requiresContext; this.method = method; } public Object newInstance() { try { return invocationMethod.getDeclaringClass().newInstance(); } catch (InstantiationException e) { throw new TeiidRuntimeException( QueryPlugin.Event.TEIID30602, QueryPlugin.Util.gs( QueryPlugin.Event.TEIID30602, method.getName(), method.getInvocationClass())); } catch (IllegalAccessException e) { throw new TeiidRuntimeException( QueryPlugin.Event.TEIID30602, QueryPlugin.Util.gs( QueryPlugin.Event.TEIID30602, method.getName(), method.getInvocationClass())); } } public void setHasWrappedArgs(boolean hasWrappedArgs) { this.hasWrappedArgs = hasWrappedArgs; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getName() { return this.method.getName(); } public String getFullName() { if (CoreConstants.SYSTEM_MODEL.equals(this.schema)) { return getName(); } return this.schema + AbstractMetadataRecord.NAME_DELIM_CHAR + getName(); } public PushDown getPushdown() { return this.method.getPushdown(); } public Class<?>[] getTypes() { return this.types; } public Class<?> getReturnType() { return this.returnType; } Method getInvocationMethod() { return this.invocationMethod; } public boolean requiresContext() { return this.requiresContext; } @Override public String toString() { StringBuffer str = new StringBuffer(this.method.getName()); str.append("("); // $NON-NLS-1$ for (int i = 0; i < types.length; i++) { if (types[i] != null) { str.append(types[i].getName()); } else { str.append("null"); // $NON-NLS-1$ } if (i < (types.length - 1)) { str.append(", "); // $NON-NLS-1$ } } str.append(") : "); // $NON-NLS-1$ if (returnType == null) { str.append("null"); // $NON-NLS-1$ } else { str.append(returnType.getName()); } return str.toString(); } public boolean isNullDependent() { return !this.method.isNullOnNull(); } public Determinism getDeterministic() { return this.method.getDeterminism(); } @Override public FunctionDescriptor clone() { try { return (FunctionDescriptor) super.clone(); } catch (CloneNotSupportedException e) { throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30381, e); } } public FunctionMethod getMethod() { return method; } void setReturnType(Class<?> returnType) { this.returnType = returnType; } /** * Invoke the function described in the function descriptor, using the values provided. Return the * result of the function. * * @param values Values that should match 1-to-1 with the types described in the function * descriptor * @param context * @param functionTarget TODO * @param fd Function descriptor describing the name and types of the arguments * @return Result of invoking the function */ public Object invokeFunction(Object[] values, CommandContext context, Object functionTarget) throws FunctionExecutionException, BlockedException { if (!isNullDependent()) { for (int i = requiresContext ? 1 : 0; i < values.length; i++) { if (values[i] == null) { return null; } } } // If descriptor is missing invokable method, find this VM's descriptor // give name and types from fd if (invocationMethod == null) { throw new FunctionExecutionException( QueryPlugin.Event.TEIID30382, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30382, getFullName())); } // Invoke the method and return the result try { if (hasWrappedArgs) { for (int i = 0; i < values.length; i++) { Object val = values[i]; if (val != null && types[i] == DataTypeManager.DefaultDataClasses.VARBINARY) { values[i] = ((BinaryType) val).getBytesDirect(); } } } if (method.isVarArgs()) { if (calledWithVarArgArrayParam) { ArrayImpl av = (ArrayImpl) values[values.length - 1]; if (av != null) { Object[] vals = av.getValues(); values[values.length - 1] = vals; if (hasWrappedArgs && types[types.length - 1] == DataTypeManager.DefaultDataClasses.VARBINARY) { vals = Arrays.copyOf(vals, vals.length); for (int i = 0; i < vals.length; i++) { if (vals[i] != null) { vals[i] = ((BinaryType) vals[i]).getBytesDirect(); } } values[values.length - 1] = vals; } Class<?> arrayType = invocationMethod.getParameterTypes()[types.length - 1]; if (arrayType.getComponentType() != Object.class && vals.getClass() != arrayType) { Object varArgs = Array.newInstance(arrayType.getComponentType(), vals.length); for (int i = 0; i < vals.length; i++) { Array.set(varArgs, i, vals[i]); } values[values.length - 1] = varArgs; } } } else { int i = invocationMethod.getParameterTypes().length; Object[] newValues = Arrays.copyOf(values, i); Object varArgs = null; if (invocationMethod.getParameterTypes()[i - 1].getComponentType() != Object.class) { int varArgCount = values.length - i + 1; varArgs = Array.newInstance( invocationMethod.getParameterTypes()[i - 1].getComponentType(), varArgCount); for (int j = 0; j < varArgCount; j++) { Array.set(varArgs, j, values[i - 1 + j]); } } else { varArgs = Arrays.copyOfRange(values, i - 1, values.length); } newValues[i - 1] = varArgs; values = newValues; } } Object result = invocationMethod.invoke(functionTarget, values); if (context != null && getDeterministic().ordinal() <= Determinism.USER_DETERMINISTIC.ordinal()) { context.setDeterminismLevel(getDeterministic()); } return importValue(result, getReturnType()); } catch (ArithmeticException e) { throw new FunctionExecutionException( QueryPlugin.Event.TEIID30384, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, getFullName())); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof BlockedException) { throw (BlockedException) e.getTargetException(); } throw new FunctionExecutionException( QueryPlugin.Event.TEIID30384, e.getTargetException(), QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, getFullName())); } catch (IllegalAccessException e) { throw new FunctionExecutionException( QueryPlugin.Event.TEIID30385, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30385, method.toString())); } catch (TransformationException e) { throw new FunctionExecutionException(e); } } public static Object importValue(Object result, Class<?> expectedType) throws ArithmeticException, TransformationException { if (!ALLOW_NAN_INFINITY) { if (result instanceof Double) { Double floatVal = (Double) result; if (Double.isInfinite(floatVal) || Double.isNaN(floatVal)) { throw new ArithmeticException("Infinite or invalid result"); // $NON-NLS-1$ } } else if (result instanceof Float) { Float floatVal = (Float) result; if (Float.isInfinite(floatVal) || Float.isNaN(floatVal)) { throw new ArithmeticException("Infinite or invalid result"); // $NON-NLS-1$ } } } result = DataTypeManager.convertToRuntimeType( result, expectedType != DataTypeManager.DefaultDataClasses.OBJECT); if (expectedType.isArray() && result instanceof ArrayImpl) { return result; } result = DataTypeManager.transformValue(result, expectedType); if (result instanceof String) { String s = (String) result; if (s.length() > DataTypeManager.MAX_STRING_LENGTH) { return s.substring(0, DataTypeManager.MAX_STRING_LENGTH); } } return result; } public boolean isCalledWithVarArgArrayParam() { return calledWithVarArgArrayParam; } public void setCalledWithVarArgArrayParam(boolean calledWithVarArgArrayParam) { this.calledWithVarArgArrayParam = calledWithVarArgArrayParam; } public boolean isSystemFunction(String name) { return this.getName().equalsIgnoreCase(name) && CoreConstants.SYSTEM_MODEL.equals(this.getSchema()); } }
/** Represents the runtime state of a vdb that may aggregate several vdbs. */ public class CompositeVDB { private static final boolean WIDEN_COMPARISON_TO_STRING = PropertiesUtils.getBooleanProperty( System.getProperties(), "org.teiid.widenComparisonToString", true); // $NON-NLS-1$ private VDBMetaData vdb; private MetadataStore store; private LinkedHashMap<String, VDBResources.Resource> visibilityMap; private UDFMetaData udf; LinkedHashMap<VDBKey, CompositeVDB> children; private MetadataStore[] additionalStores; private ConnectorManagerRepository cmr; private FunctionTree systemFunctions; private boolean metadataloadFinished = false; private VDBMetaData mergedVDB; private VDBMetaData originalVDB; private Collection<Future<?>> tasks = Collections.synchronizedSet(new HashSet<Future<?>>()); public CompositeVDB( VDBMetaData vdb, MetadataStore metadataStore, LinkedHashMap<String, VDBResources.Resource> visibilityMap, UDFMetaData udf, FunctionTree systemFunctions, ConnectorManagerRepository cmr, VDBRepository vdbRepository, MetadataStore... additionalStores) throws VirtualDatabaseException { this.vdb = vdb; this.store = metadataStore; this.visibilityMap = visibilityMap; this.udf = udf; this.systemFunctions = systemFunctions; this.cmr = cmr; this.additionalStores = additionalStores; this.mergedVDB = vdb; this.originalVDB = vdb; buildCompositeState(vdbRepository); } private static TransformationMetadata buildTransformationMetaData( VDBMetaData vdb, LinkedHashMap<String, VDBResources.Resource> visibilityMap, MetadataStore store, UDFMetaData udf, FunctionTree systemFunctions, MetadataStore[] additionalStores) { Collection<FunctionTree> udfs = new ArrayList<FunctionTree>(); if (udf != null) { for (Map.Entry<String, UDFSource> entry : udf.getFunctions().entrySet()) { udfs.add(new FunctionTree(entry.getKey(), entry.getValue(), true)); } } CompositeMetadataStore compositeStore = new CompositeMetadataStore(store); for (MetadataStore s : additionalStores) { compositeStore.merge(s); for (Schema schema : s.getSchemas().values()) { if (!schema.getFunctions().isEmpty()) { UDFSource source = new UDFSource(schema.getFunctions().values()); if (udf != null) { source.setClassLoader(udf.getClassLoader()); } udfs.add(new FunctionTree(schema.getName(), source, true)); } if (!schema.getProcedures().isEmpty()) { FunctionTree ft = FunctionTree.getFunctionProcedures(schema); if (ft != null) { udfs.add(ft); } } } } TransformationMetadata metadata = new TransformationMetadata(vdb, compositeStore, visibilityMap, systemFunctions, udfs); metadata.setUseOutputNames(false); metadata.setWidenComparisonToString(WIDEN_COMPARISON_TO_STRING); return metadata; } public VDBMetaData getVDB() { return this.mergedVDB; } private void buildCompositeState(VDBRepository vdbRepository) throws VirtualDatabaseException { if (vdb.getVDBImports().isEmpty()) { this.vdb.addAttchment(ConnectorManagerRepository.class, this.cmr); return; } VDBMetaData newMergedVDB = this.vdb.clone(); ConnectorManagerRepository mergedRepo = this.cmr; if (!this.cmr.isShared()) { mergedRepo = new ConnectorManagerRepository(); mergedRepo.getConnectorManagers().putAll(this.cmr.getConnectorManagers()); } newMergedVDB.addAttchment(ConnectorManagerRepository.class, mergedRepo); ClassLoader[] toSearch = new ClassLoader[vdb.getVDBImports().size() + 1]; toSearch[0] = this.vdb.getAttachment(ClassLoader.class); this.children = new LinkedHashMap<VDBKey, CompositeVDB>(); newMergedVDB.setImportedModels(new TreeSet<String>(String.CASE_INSENSITIVE_ORDER)); int i = 1; for (VDBImport vdbImport : vdb.getVDBImports()) { CompositeVDB importedVDB = vdbRepository.getCompositeVDB(new VDBKey(vdbImport.getName(), vdbImport.getVersion())); if (importedVDB == null) { throw new VirtualDatabaseException( RuntimePlugin.Event.TEIID40083, RuntimePlugin.Util.gs( RuntimePlugin.Event.TEIID40083, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion())); } VDBMetaData childVDB = importedVDB.getVDB(); newMergedVDB.getVisibilityOverrides().putAll(childVDB.getVisibilityOverrides()); toSearch[i++] = childVDB.getAttachment(ClassLoader.class); this.children.put(new VDBKey(childVDB.getName(), childVDB.getVersion()), importedVDB); if (vdbImport.isImportDataPolicies()) { for (DataPolicy dp : importedVDB.getVDB().getDataPolicies()) { DataPolicyMetadata role = (DataPolicyMetadata) dp; if (newMergedVDB.addDataPolicy(role) != null) { throw new VirtualDatabaseException( RuntimePlugin.Event.TEIID40084, RuntimePlugin.Util.gs( RuntimePlugin.Event.TEIID40084, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), role.getName())); } if (role.isGrantAll()) { role.setSchemas(childVDB.getModelMetaDatas().keySet()); } } } // add models for (ModelMetaData m : childVDB.getModelMetaDatas().values()) { if (newMergedVDB.addModel(m) != null) { throw new VirtualDatabaseException( RuntimePlugin.Event.TEIID40085, RuntimePlugin.Util.gs( RuntimePlugin.Event.TEIID40085, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), m.getName())); } newMergedVDB.getImportedModels().add(m.getName()); String visibilityOverride = newMergedVDB.getPropertyValue(m.getName() + ".visible"); // $NON-NLS-1$ if (visibilityOverride != null) { boolean visible = Boolean.valueOf(visibilityOverride); newMergedVDB.setVisibilityOverride(m.getName(), visible); } } ConnectorManagerRepository childCmr = childVDB.getAttachment(ConnectorManagerRepository.class); if (childCmr == null) { throw new AssertionError("childVdb had not connector manager repository"); // $NON-NLS-1$ } if (!this.cmr.isShared()) { for (Map.Entry<String, ConnectorManager> entry : childCmr.getConnectorManagers().entrySet()) { if (mergedRepo.getConnectorManagers().put(entry.getKey(), entry.getValue()) != null) { throw new VirtualDatabaseException( RuntimePlugin.Event.TEIID40086, RuntimePlugin.Util.gs( RuntimePlugin.Event.TEIID40086, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), entry.getKey())); } } } } if (toSearch[0] != null) { CombinedClassLoader ccl = new CombinedClassLoader(toSearch[0].getParent(), toSearch); this.mergedVDB.addAttchment(ClassLoader.class, ccl); } this.mergedVDB = newMergedVDB; } private UDFMetaData getUDF() { UDFMetaData mergedUDF = new UDFMetaData(); if (this.udf != null) { mergedUDF.addFunctions(this.udf); } for (Schema schema : store.getSchemas().values()) { Collection<FunctionMethod> funcs = schema.getFunctions().values(); mergedUDF.addFunctions(schema.getName(), funcs); } if (this.cmr != null) { // system scoped common source functions for (ConnectorManager cm : this.cmr.getConnectorManagers().values()) { List<FunctionMethod> funcs = cm.getPushDownFunctions(); mergedUDF.addFunctions(CoreConstants.SYSTEM_MODEL, funcs); } } if (this.children != null) { // udf model functions - also scoped to the model for (CompositeVDB child : this.children.values()) { UDFMetaData funcs = child.getUDF(); if (funcs != null) { mergedUDF.addFunctions(funcs); } } } return mergedUDF; } /** TODO: we are not checking for collisions here. */ private LinkedHashMap<String, VDBResources.Resource> getVisibilityMap() { if (this.children == null || this.children.isEmpty()) { return this.visibilityMap; } LinkedHashMap<String, VDBResources.Resource> mergedvisibilityMap = new LinkedHashMap<String, VDBResources.Resource>(); for (CompositeVDB child : this.children.values()) { LinkedHashMap<String, VDBResources.Resource> vm = child.getVisibilityMap(); if (vm != null) { mergedvisibilityMap.putAll(vm); } } if (this.visibilityMap != null) { mergedvisibilityMap.putAll(this.visibilityMap); } return mergedvisibilityMap; } private MetadataStore getMetadataStore() { return this.store; } VDBMetaData getOriginalVDB() { return originalVDB; } public void metadataLoadFinished() { if (this.metadataloadFinished) { return; } this.metadataloadFinished = true; MetadataStore mergedStore = getMetadataStore(); // the order of the models is important for resolving ddl // TODO we might consider not using the intermediate MetadataStore List<Schema> schemas = mergedStore.getSchemaList(); schemas.clear(); for (ModelMetaData model : this.vdb.getModelMetaDatas().values()) { Schema s = mergedStore.getSchema(model.getName()); if (s != null) { schemas.add(s); } else { mergedStore.getSchemas().remove(model.getName()); } } if (this.children != null && !this.children.isEmpty()) { for (CompositeVDB child : this.children.values()) { MetadataStore childStore = child.getMetadataStore(); if (childStore != null) { mergedStore.merge(childStore); } } } TransformationMetadata metadata = buildTransformationMetaData( mergedVDB, getVisibilityMap(), mergedStore, getUDF(), systemFunctions, this.additionalStores); QueryMetadataInterface qmi = metadata; Map<String, String> multiSourceModels = MultiSourceMetadataWrapper.getMultiSourceModels(mergedVDB); if (multiSourceModels != null && !multiSourceModels.isEmpty()) { qmi = new MultiSourceMetadataWrapper(metadata, multiSourceModels); } mergedVDB.addAttchment(QueryMetadataInterface.class, qmi); mergedVDB.addAttchment(TransformationMetadata.class, metadata); mergedVDB.addAttchment(MetadataStore.class, mergedStore); } LinkedHashMap<VDBKey, CompositeVDB> getChildren() { return children; } public Collection<Future<?>> clearTasks() { ArrayList<Future<?>> copy = new ArrayList<Future<?>>(tasks); tasks.clear(); return copy; } public void removeTask(Future<?> future) { tasks.remove(future); } public void addTask(Future<?> future) { tasks.add(future); } }