/* * 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.metadata; import java.io.Reader; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.TreeMap; import org.teiid.UserDefinedAggregate; import org.teiid.adminapi.Model; import org.teiid.adminapi.impl.DataPolicyMetadata.PermissionMetaData; import org.teiid.core.types.DataTypeManagerService; import org.teiid.core.util.StringUtil; import org.teiid.designer.annotation.Removed; import org.teiid.designer.annotation.Since; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.metadata.FunctionMethod.PushDown; import org.teiid.metadata.MetadataStore.Grant; import org.teiid.query.parser.QueryParser; import org.teiid.query.util.CommandContext; import org.teiid.runtime.client.Messages; /** * Allows connectors to build metadata for use by the engine. * * TODO: add support for datatype import * TODO: add support for unique constraints */ public class MetadataFactory implements Serializable { private static final String TEIID_RESERVED = "teiid_"; //$NON-NLS-1$ private static final String TEIID_SF = "teiid_sf"; //$NON-NLS-1$ private static final String TEIID_RELATIONAL = "teiid_rel"; //$NON-NLS-1$ private static final String TEIID_WS = "teiid_ws"; //$NON-NLS-1$ private static final String TEIID_MONGO = "teiid_mongo"; //$NON-NLS-1$ private static final String TEIID_ODATA = "teiid_odata"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) private static final String TEIID_ACCUMULO = "teiid_accumulo"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) private static final String TEIID_EXCEL = "teiid_excel"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) private static final String TEIID_JPA = "teiid_jpa"; //$NON-NLS-1$ @Since(Version.TEIID_8_10) private static final String TEIID_HBASE = "teiid_hbase"; //$NON-NLS-1$ @Since(Version.TEIID_8_10) private static final String TEIID_SPATIAL = "teiid_spatial"; //$NON-NLS-1$ @Since(Version.TEIID_8_12_4) private static final String TEIID_LDAP = "teiid_ldap"; //$NON-NLS-1$ private static final long serialVersionUID = 8590341087771685630L; private final ITeiidServerVersion teiidVersion; private String vdbName; private int vdbVersion; private Map<String, Datatype> enterpriseTypes; private Map<String, Datatype> dataTypes; private Map<String, Datatype> builtinDataTypes; private boolean autoCorrectColumnNames = true; private Map<String, String> namespaces; private String rawMetadata; private Properties modelProperties; private Schema schema = new Schema(); private String idPrefix; protected int count; private transient QueryParser parser; private transient Model model; private transient Map<String, ? extends VDBResource> vdbResources; private List<Grant> grants; public static final String SF_URI = "{http://www.teiid.org/translator/salesforce/2012}"; //$NON-NLS-1$ public static final String WS_URI = "{http://www.teiid.org/translator/ws/2012}"; //$NON-NLS-1$ public static final String MONGO_URI = "{http://www.teiid.org/translator/mongodb/2013}"; //$NON-NLS-1$ public static final String ODATA_URI = "{http://www.jboss.org/teiiddesigner/ext/odata/2012}"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) public static final String ACCUMULO_URI = "{http://www.teiid.org/translator/accumulo/2013}"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) public static final String EXCEL_URI = "{http://www.teiid.org/translator/excel/2014}"; //$NON-NLS-1$ @Since(Version.TEIID_8_7) public static final String JPA_URI = "{http://www.teiid.org/translator/jpa/2014}"; //$NON-NLS-1$ @Since(Version.TEIID_8_10) public static final String HBASE_URI = "{http://www.teiid.org/translator/hbase/2014}"; //$NON-NLS-1$ @Since(Version.TEIID_8_10) public static final String SPATIAL_URI = "{http://www.teiid.org/translator/spatial/2015}"; //$NON-NLS-1$ @Since(Version.TEIID_8_12_4) public static final String LDAP_URI = "{http://www.teiid.org/translator/ldap/2015}"; //$NON-NLS-1$ /* * Converted from static field to function to allow version to be checked */ @Since(Version.TEIID_8_10) public static Map<String, String> builtinNamespaces(ITeiidServerVersion version) { Map<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); map.put(TEIID_RELATIONAL, AbstractMetadataRecord.RELATIONAL_URI.substring(1, AbstractMetadataRecord.RELATIONAL_URI.length()-1)); map.put(TEIID_SF, SF_URI.substring(1, SF_URI.length()-1)); map.put(TEIID_WS, WS_URI.substring(1, WS_URI.length()-1)); map.put(TEIID_MONGO, MONGO_URI.substring(1, MONGO_URI.length()-1)); map.put(TEIID_ODATA, ODATA_URI.substring(1, ODATA_URI.length()-1)); map.put(TEIID_ACCUMULO, ACCUMULO_URI.substring(1, ACCUMULO_URI.length()-1)); map.put(TEIID_EXCEL, EXCEL_URI.substring(1, EXCEL_URI.length()-1)); map.put(TEIID_JPA, JPA_URI.substring(1, JPA_URI.length()-1)); if (version.isGreaterThanOrEqualTo(Version.TEIID_8_10)) { map.put(TEIID_HBASE, HBASE_URI.substring(1, HBASE_URI.length()-1)); map.put(TEIID_SPATIAL, SPATIAL_URI.substring(1, SPATIAL_URI.length()-1)); } if (version.isGreaterThanOrEqualTo(Version.TEIID_8_12_4)) { map.put(TEIID_LDAP, LDAP_URI.substring(1, LDAP_URI.length()-1)); } return Collections.unmodifiableMap(map); } public MetadataFactory(ITeiidServerVersion teiidVersion, String vdbName, int vdbVersion, Map<String, Datatype> runtimeTypes, Model model) { this(teiidVersion, vdbName, vdbVersion, model.getName(), runtimeTypes, model.getProperties(), model.getSchemaText()); this.model = model; } public MetadataFactory(ITeiidServerVersion teiidVersion, String vdbName, int vdbVersion, String schemaName, Map<String, Datatype> runtimeTypes, Properties modelProperties, String rawMetadata) { this.teiidVersion = teiidVersion; this.vdbName = vdbName; this.vdbVersion = vdbVersion; this.dataTypes = runtimeTypes; this.builtinDataTypes = runtimeTypes; this.schema.setName(schemaName); long msb = longHash(vdbName, 0); msb = 31*msb + vdbVersion; msb = longHash(schemaName, msb); this.idPrefix = "tid:" + hex(msb, 12); //$NON-NLS-1$ setUUID(this.schema); if (isTeiid811OrGreater()) { if (modelProperties != null) { for (Map.Entry<Object, Object> entry : modelProperties.entrySet()) { if (entry.getKey() instanceof String && entry.getValue() instanceof String) { this.schema.setProperty(resolvePropertyKey(this, (String)entry.getKey()), (String)entry.getValue()); } } } } this.modelProperties = modelProperties; this.rawMetadata = rawMetadata; } /** * @return the teiidVersion */ public ITeiidServerVersion getTeiidVersion() { return this.teiidVersion; } private boolean isTeiid811OrGreater() { return teiidVersion.isGreaterThanOrEqualTo(Version.TEIID_8_11); } private boolean isTeiid89OrGreater() { return teiidVersion.isGreaterThanOrEqualTo(Version.TEIID_8_9); } private long longHash(String s, long h) { if (s == null) { return h; } for (int i = 0; i < s.length(); i++) { h = 31*h + s.charAt(i); } return h; } public static String hex(long val, int hexLength) { long hi = 1L << (Math.min(63, hexLength * 4)); return Long.toHexString(hi | (val & (hi - 1))).substring(1); } /** * @deprecated * @return * @see #getModelProperties() */ @Deprecated public Properties getImportProperties() { return this.modelProperties; } public Properties getModelProperties() { return this.modelProperties; } /** * Get the metadata text for the first metadata element * @return */ @Deprecated public String getRawMetadata() { return this.rawMetadata; } public Model getModel() { return this.model; } protected void setUUID(AbstractMetadataRecord record) { int lsb = 0; if (record.getParent() != null) { lsb = record.getParent().getUUID().hashCode(); } lsb = 31*lsb + record.getName().hashCode(); String uuid = this.idPrefix+"-"+hex(lsb, 8) + "-" + hex(this.count++, 8); //$NON-NLS-1$ //$NON-NLS-2$ record.setUUID(uuid); } public String getName() { return this.schema.getName(); } public Schema getSchema() { return this.schema; } /** * Add a table with the given name to the model. * @param name * @return * */ public Table addTable(String name) { Table table = new Table(); table.setTableType(Table.Type.Table); table.setName(name); setUUID(table); this.schema.addTable(table); return table; } /** * Adds a column to the table with the given name and type. * @param name * @param type should be one of {@link TypeFacility.RUNTIME_NAMES} * @param table * @return column */ public Column addColumn(String name, String type, ColumnSet<?> table) { if (this.autoCorrectColumnNames) { name.replace(AbstractMetadataRecord.NAME_DELIM_CHAR, '_'); } else if (name.indexOf(AbstractMetadataRecord.NAME_DELIM_CHAR) != -1) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60008, table.getFullName(), name)); } if (table.getColumnByName(name) != null) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60016, table.getFullName() + AbstractMetadataRecord.NAME_DELIM_CHAR + name)); } Column column = new Column(teiidVersion); column.setName(name); table.addColumn(column); column.setParent(table); column.setPosition(table.getColumns().size()); //1 based indexing if (isTeiid89OrGreater()) setDataType(type, column, this.dataTypes, false); else setColumnType(type, column); setUUID(column); return column; } @Removed(Version.TEIID_8_9) private Datatype setColumnType(String type, BaseColumn column) { int arrayDimensions = 0; while (DataTypeManagerService.isArrayType(type)) { arrayDimensions++; type = type.substring(0, type.length()-2); } Datatype datatype = this.dataTypes.get(type); if (datatype == null) { //TODO: potentially we want to check the enterprise types, but at //this point we're keying them by name, not runtime name (which // is an awkward difference to start with) //so the runtime type names are considered fixed and a type system //generalization would be needed to support injecting new runtime types throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60009, type)); } column.setDatatype(datatype, true, arrayDimensions); return datatype; } @Since(Version.TEIID_8_9) public static Datatype setDataType(String type, BaseColumn column, Map<String, Datatype> dataTypes, boolean allowNull) { int arrayDimensions = 0; while (DataTypeManagerService.isArrayType(type)) { arrayDimensions++; type = type.substring(0, type.length()-2); } Datatype datatype = dataTypes.get(type); if (datatype == null && (!allowNull || !DataTypeManagerService.DefaultDataTypes.NULL.getId().equals(type))) { //TODO: potentially we want to check the enterprise types, but at //this point we're keying them by name, not runtime name (which // is an awkward difference to start with) //so the runtime type names are considered fixed and a type system //generalization would be needed to support injecting new runtime types throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60009, type)); } column.setDatatype(datatype, true, arrayDimensions); return datatype; } /** * Adds a primary key to the given table. The column names should be in key order. * @param name * @param columnNames * @param table * @return * */ public KeyRecord addPrimaryKey(String name, List<String> columnNames, Table table) { KeyRecord primaryKey = new KeyRecord(KeyRecord.Type.Primary); primaryKey.setParent(table); primaryKey.setColumns(new ArrayList<Column>(columnNames.size())); primaryKey.setName(name); setUUID(primaryKey); assignColumns(columnNames, table, primaryKey); table.setPrimaryKey(primaryKey); return primaryKey; } /** * Adds an access pattern to the given table. * @param name * @param columnNames * @param table * @return * */ public KeyRecord addAccessPattern(String name, List<String> columnNames, Table table) { KeyRecord ap = new KeyRecord(KeyRecord.Type.AccessPattern); ap.setParent(table); ap.setColumns(new ArrayList<Column>(columnNames.size())); ap.setName(name); setUUID(ap); assignColumns(columnNames, table, ap); table.getAccessPatterns().add(ap); return ap; } /** * Adds an index to the given table. * @param name * @param nonUnique true indicates that an index is being added. * @param columnNames * @param table * @return * */ public KeyRecord addIndex(String name, boolean nonUnique, List<String> columnNames, Table table) { KeyRecord index = new KeyRecord(nonUnique?KeyRecord.Type.Index:KeyRecord.Type.Unique); index.setParent(table); index.setColumns(new ArrayList<Column>(columnNames.size())); index.setName(name); assignColumns(columnNames, table, index); setUUID(index); if (nonUnique) { table.getIndexes().add(index); } else { table.getUniqueKeys().add(index); } return index; } /** * Adds a function based index on the given expressions. * @param name * @param expressions * @param nonColumnExpressions a Boolean list indicating when expressions are non-column expressions * @param table * @return * */ public KeyRecord addFunctionBasedIndex(String name, List<String> expressions, List<Boolean> nonColumnExpressions, Table table) { KeyRecord index = new KeyRecord(KeyRecord.Type.Index); index.setParent(table); index.setColumns(new ArrayList<Column>(expressions.size())); index.setName(name); setUUID(index); boolean functionBased = false; for (int i = 0; i < expressions.size(); i++) { String expr = expressions.get(i); if (nonColumnExpressions.get(i)) { Column c = new Column(teiidVersion); //TODO: we could choose a derived name at this point, but we delay that to get a single unique name across all index expressions c.setName(expr); c.setNameInSource(expr); setUUID(c); c.setParent(index); c.setPosition(i + 1); //position is temporarily relative to the index, but the validator changes this index.getColumns().add(c); functionBased = true; } else { assignColumn(table, index, expr); } } if (!functionBased) { table.getIndexes().add(index); } else { table.getFunctionBasedIndexes().add(index); } return index; } private void assignColumn(Table table, ColumnSet<?> columns, String columnName) { Column column = table.getColumnByName(columnName); if (column == null) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60011, table.getFullName(), columnName)); } columns.getColumns().add(column); } /** * Adds a foreign key to the given table. The referenced primary key must already exist. The column names should be in key order. * @param name * @param columnNames * @param referenceTable - schema qualified reference table name * @param table * @return * */ public ForeignKey addForiegnKey(String name, List<String> columnNames, String referenceTable, Table table) { return addForiegnKey(name, columnNames, null, referenceTable, table); } /** * Adds a foreign key to the given table. The referenced key may be automatically created if addUniqueConstraint is true. The column names should be in key order. * if reference table is is another schema, they will be resolved during validation. * @param name * @param columnNames * @param referencedColumnNames, may be null to indicate that the primary key should be used. * @param referenceTable - schema qualified reference table name, can be from another schema * @param table * @param addUniqueConstraint - if true, if the referenced table columns do not match with either PK, or FK then a UNIQUE index on reference table is created. * @return * */ public ForeignKey addForiegnKey(String name, List<String> columnNames, List<String> referencedColumnNames, String referenceTable, Table table) { ForeignKey foreignKey = new ForeignKey(); foreignKey.setParent(table); foreignKey.setColumns(new ArrayList<Column>(columnNames.size())); assignColumns(columnNames, table, foreignKey); foreignKey.setReferenceTableName(referenceTable); foreignKey.setReferenceColumns(referencedColumnNames); foreignKey.setName(name); setUUID(foreignKey); table.getForeignKeys().add(foreignKey); return foreignKey; } /** * Add a procedure with the given name to the model. * @param name * @return * */ public Procedure addProcedure(String name) { Procedure procedure = new Procedure(); procedure.setName(name); setUUID(procedure); procedure.setParameters(new LinkedList<ProcedureParameter>()); this.schema.addProcedure(procedure); return procedure; } /** * Add a procedure parameter. * @param name * @param type should be one of {@link TypeFacility.RUNTIME_NAMES} * @param parameterType should be one of {@link ProcedureParameter.Type} * @param procedure * @return * */ public ProcedureParameter addProcedureParameter(String name, String type, ProcedureParameter.Type parameterType, Procedure procedure) { ProcedureParameter param = new ProcedureParameter(teiidVersion); param.setName(name); setUUID(param); param.setType(parameterType); param.setProcedure(procedure); if (isTeiid89OrGreater()) setDataType(type, param, this.dataTypes, false); else setColumnType(type, param); if (parameterType == ProcedureParameter.Type.ReturnValue) { procedure.getParameters().add(0, param); for (int i = 0; i < procedure.getParameters().size(); i++) { procedure.getParameters().get(i).setPosition(i+1); //1 based indexing } } else { procedure.getParameters().add(param); param.setPosition(procedure.getParameters().size()); //1 based indexing } return param; } /** * Add a procedure resultset column to the given procedure. * @param name * @param type should be one of {@link TypeFacility.RUNTIME_NAMES} * @param procedure * @return * */ public Column addProcedureResultSetColumn(String name, String type, Procedure procedure) { if (procedure.getResultSet() == null) { ColumnSet<Procedure> resultSet = new ColumnSet<Procedure>(); resultSet.setParent(procedure); resultSet.setName("RSParam"); //$NON-NLS-1$ setUUID(resultSet); procedure.setResultSet(resultSet); } return addColumn(name, type, procedure.getResultSet()); } private void assignColumns(List<String> columnNames, Table table, ColumnSet<?> columns) { for (String columnName : columnNames) { assignColumn(table, columns, columnName); } } /** * Add a function with the given name to the model. * @param name * @return * */ public FunctionMethod addFunction(String name) { FunctionMethod function = new FunctionMethod(); function.setName(name); setUUID(function); this.schema.addFunction(function); return function; } /** * Add a function with the given name to the model. * @param name * @return * @throws MetadataException */ public FunctionMethod addFunction(String name, String returnType, String... paramTypes) { FunctionMethod function = FunctionMethod.createFunctionMethod(teiidVersion, name, null, null, returnType, paramTypes); function.setPushdown(PushDown.MUST_PUSHDOWN); setUUID(function); schema.addFunction(function); return function; } /** * Adds a non-pushdown function based upon the given {@link Method}. * @param name * @param method * @return * */ public FunctionMethod addFunction(String name, Method method) { FunctionMethod func = createFunctionFromMethod(teiidVersion, name, method); setUUID(func); getSchema().addFunction(func); return func; } /** * Convert a primitive class to the corresponding object class * * Taken from TypeFacility * * @param clazz * @return */ private static Class<?> convertPrimitiveToObject(Class<?> clazz) { if (!clazz.isPrimitive()) { return clazz; } if ( clazz == Boolean.TYPE ) clazz = Boolean.class; else if ( clazz == Character.TYPE ) clazz = Character.class; else if ( clazz == Byte.TYPE ) clazz = Byte.class; else if ( clazz == Short.TYPE ) clazz = Short.class; else if ( clazz == Integer.TYPE ) clazz = Integer.class; else if ( clazz == Long.TYPE ) clazz = Long.class; else if ( clazz == Float.TYPE ) clazz = Float.class; else if ( clazz == Double.TYPE ) clazz = Double.class; return clazz; } public static FunctionMethod createFunctionFromMethod(ITeiidServerVersion teiidVersion, String name, Method method) { DataTypeManagerService dataTypeManager = DataTypeManagerService.getInstance(teiidVersion); String returnType; AggregateAttributes aa = null; if (Version.TEIID_8_12_4.get().isLessThan(teiidVersion)) returnType = dataTypeManager.getDataTypeName(method.getReturnType()); else { Class<?> returnTypeClass = method.getReturnType(); //handle user defined aggregates if ((method.getModifiers() & Modifier.STATIC) == 0 && UserDefinedAggregate.class.isAssignableFrom(method.getDeclaringClass())) { aa = new AggregateAttributes(); Method m; try { m = method.getDeclaringClass().getMethod("getResult", CommandContext.class); //$NON-NLS-1$ } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (SecurityException e) { throw new RuntimeException(e); } returnTypeClass = m.getReturnType(); } if (returnTypeClass.isPrimitive()) { returnTypeClass = convertPrimitiveToObject(returnTypeClass); } returnType = dataTypeManager.getDataTypeName(returnTypeClass); } Class<?>[] params = method.getParameterTypes(); String[] paramTypes = new String[params.length]; boolean nullOnNull = false; for (int i = 0; i < params.length; i++) { Class<?> clazz = params[i]; if (clazz.isPrimitive()) { nullOnNull = true; clazz = convertPrimitiveToObject(clazz); } if (method.isVarArgs() && i == params.length -1) { paramTypes[i] = dataTypeManager.getDataTypeName(clazz.getComponentType()); } else { paramTypes[i] = dataTypeManager.getDataTypeName(clazz); } } if (params.length > 0 && CommandContext.class.isAssignableFrom(params[0])) { paramTypes = Arrays.copyOfRange(paramTypes, 1, paramTypes.length); } FunctionMethod func = FunctionMethod.createFunctionMethod(teiidVersion, name, null, null, returnType, paramTypes); if (aa != null) // Only applicable for teiid version 8.12+ func.setAggregateAttributes(aa); func.setInvocationMethod(method.getName()); func.setPushdown(PushDown.CANNOT_PUSHDOWN); func.setMethod(method); func.setInvocationClass(method.getDeclaringClass().getName()); func.setNullOnNull(nullOnNull); if (method.isVarArgs()) { func.setVarArgs(method.isVarArgs()); } return func; } /** * Set to false to disable correcting column and other names to be valid Teiid names. * @param autoCorrectColumnNames */ public void setAutoCorrectColumnNames(boolean autoCorrectColumnNames) { this.autoCorrectColumnNames = autoCorrectColumnNames; } public void addNamespace(String prefix, String uri) { if (uri == null || uri.indexOf('}') != -1) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60018, uri)); } if (StringUtil.startsWithIgnoreCase(prefix, TEIID_RESERVED)) { String validURI = builtinNamespaces(teiidVersion).get(prefix); if (validURI == null || !uri.equals(validURI)) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60017, prefix)); } } if (this.namespaces == null) { this.namespaces = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); } this.namespaces.put(prefix, uri); } public void mergeInto (MetadataStore store) { store.addSchema(this.schema); store.addDataTypes(this.builtinDataTypes.values()); //TODO: this is redundant if (this.enterpriseTypes != null) { store.addDataTypes(this.enterpriseTypes.values()); } store.addGrants(this.grants); } public MetadataStore asMetadataStore() { MetadataStore store = new MetadataStore(); mergeInto(store); return store; } /** * Set the {@link Schema} to a different instance. This is typically called * in special situations where the {@link MetadataFactory} logic is not used * to construct the {@link Schema}. * @param schema */ public void setSchema(Schema schema) { this.schema = schema; } /** * get runtime types keyed by runtime name, which is * a type name known to the Teiid engine * @return */ public Map<String, Datatype> getDataTypes() { return this.dataTypes; } /** * To be called if the MetadataFactory is deserialized to set the canonical system * type value. * @param dt * @param builtin */ public void correctDatatypes(Map<String, Datatype> dt, Map<String, Datatype> builtin) { this.dataTypes = dt; this.builtinDataTypes = builtin; for (Table t : this.schema.getTables().values()) { correctDataTypes(t.getColumns()); } for (Procedure p : this.schema.getProcedures().values()) { correctDataTypes(p.getParameters()); if (p.getResultSet() != null) { correctDataTypes(p.getResultSet().getColumns()); } } } private void correctDataTypes(List<? extends BaseColumn> cols) { if (cols == null) { return; } for (BaseColumn c : cols) { if (c.getDatatype() == null) { continue; } Datatype dt = this.builtinDataTypes.get(c.getDatatype().getName()); if (dt == null && this.enterpriseTypes != null) { dt = this.enterpriseTypes.get(c.getDatatype().getName()); } if (dt != null) { c.setDatatype(dt); } else { //must be an enterprise type //if it's used in a single schema, we're ok, but when used in multiple there's an issue //since the same record will exist as multiple instances // //old serialized forms do not have tracking for enterprise types, ensure that the //type is added so that it will show up in the vdb metadata addEnterpriseDatatype(c.getDatatype()); } } } /** * Add an enterprise type (typically a Designer defined type extension) - typically not called * @deprecated see addEnterpriseType * @param datatype */ @Deprecated public void addDatatype(Datatype datatype) { addEnterpriseDatatype(datatype); } /** * Add an enterprise type (typically a Designer defined type extension)- typically not called * @param datatype */ public void addEnterpriseDatatype(Datatype datatype) { //we have to hold these separately, as the built-in/runtime types should be considered //unmodifiable. // //however we still have an issue in that designer treats these as vdb scoped, while //we're treating them as schema scoped. any refinement of the type system //should correct this. // //TODO: should throw an exception if there is a conflict with a built-in type if (this.enterpriseTypes == null) { this.enterpriseTypes = new TreeMap<String, Datatype>(String.CASE_INSENSITIVE_ORDER); } this.enterpriseTypes.put(datatype.getName(), datatype); } /** * Get an enterprise type (typically a Designer defined type extension) by name. * @param name * @return */ public Datatype getEnterpriseDatatype(String name) { if (this.enterpriseTypes == null) { return null; } return this.enterpriseTypes.get(name); } public String getVdbName() { return this.vdbName; } public int getVdbVersion() { return this.vdbVersion; } /** * Get the namespace map. Will be an unmodifiable empty map if {@link #addNamespace(String, String)} * has not been called successfully. * @return */ public Map<String, String> getNamespaces() { if (this.namespaces == null) { return Collections.emptyMap(); } return this.namespaces; } public void setBuiltinDataTypes(Map<String, Datatype> builtinDataTypes) { this.builtinDataTypes = builtinDataTypes; } /** * get all built-in types, known to Designer and defined in the system metadata. * The entries are keyed by type name, which is typically the xsd type name. * @see #getDataTypes() for run-time types * @return */ public Map<String, Datatype> getBuiltinDataTypes() { return this.builtinDataTypes; } /** * Parses, but does not close, the given {@link Reader} into this {@link MetadataFactory} * @param ddl * */ public void parse(Reader ddl) throws Exception { this.parser.parseDDL(this, ddl); } public void setParser(QueryParser parser) { this.parser = parser; } public void setModel(Model model) { this.model = model; } public Map<String, ? extends VDBResource> getVDBResources() { return this.vdbResources; } public void setVdbResources(Map<String, ? extends VDBResource> vdbResources) { this.vdbResources = vdbResources; } /** * Add a permission for a {@link Table} or {@link Procedure} * @param role * @param resource * @param allowAlter * @param allowCreate * @param allowRead * @param allowUpdate * @param allowDelete * @param allowExecute * @param condition * @param constraint */ public void addPermission(String role, AbstractMetadataRecord resource, Boolean allowAlter, Boolean allowCreate, Boolean allowRead, Boolean allowUpdate, Boolean allowDelete, Boolean allowExecute, String condition, Boolean constraint) { PermissionMetaData pmd = new PermissionMetaData(); pmd.setResourceName(resource.getFullName()); pmd.setAllowAlter(allowAlter); pmd.setAllowCreate(allowCreate); pmd.setAllowDelete(allowDelete); pmd.setAllowExecute(allowExecute); pmd.setAllowRead(allowRead); pmd.setAllowUpdate(allowUpdate); pmd.setCondition(condition); pmd.setConstraint(constraint); addPermission(pmd, role); } /** * Add a permission for the current {@link Schema} which will typically act as a default for all child objects. * @param role * @param allowAlter * @param allowCreate * @param allowRead * @param allowUpdate * @param allowDelete * @param allowExecute */ public void addSchemaPermission(String role, Boolean allowAlter, Boolean allowCreate, Boolean allowRead, Boolean allowUpdate, Boolean allowDelete, Boolean allowExecute) { PermissionMetaData pmd = new PermissionMetaData(); pmd.setResourceName(this.schema.getFullName()); pmd.setAllowAlter(allowAlter); pmd.setAllowCreate(allowCreate); pmd.setAllowDelete(allowDelete); pmd.setAllowExecute(allowExecute); pmd.setAllowRead(allowRead); pmd.setAllowUpdate(allowUpdate); addPermission(pmd, role); } /** * Add a permission for a {@link Column} * @param role * @param resource * @param allowCreate * @param allowRead * @param allowUpdate * @param condition * @param mask * @param order */ public void addColumnPermission(String role, Column resource, Boolean allowCreate, Boolean allowRead, Boolean allowUpdate, String condition, String mask, Integer order) { PermissionMetaData pmd = new PermissionMetaData(); String resourceName = null; if (resource.getParent() != null && resource.getParent().getParent() instanceof Procedure) { resourceName = resource.getParent().getParent().getFullName() + '.' + resource.getName(); } else { resourceName = resource.getFullName(); } pmd.setResourceName(resourceName); pmd.setAllowCreate(allowCreate); pmd.setAllowRead(allowRead); pmd.setAllowUpdate(allowUpdate); pmd.setCondition(condition); pmd.setMask(mask); pmd.setOrder(order); addPermission(pmd, role); } private void addPermission(PermissionMetaData pmd, String role) { if (this.grants == null) { this.grants = new ArrayList<Grant>(); } this.grants.add(new Grant(role, pmd)); } public void addFunction(FunctionMethod functionMethod) { functionMethod.setParent(this.schema); setUUID(functionMethod); if(isTeiid89OrGreater()) { for (FunctionParameter param : functionMethod.getInputParameters()) { setUUID(param); } setUUID(functionMethod.getOutputParameter()); } this.schema.addFunction(functionMethod); } @Since(Version.TEIID_8_11) public static String resolvePropertyKey(MetadataFactory factory, String key) { int index = key.indexOf(':'); if (index > 0 && index < key.length() - 1) { String prefix = key.substring(0, index); String uri = builtinNamespaces(factory.getTeiidVersion()).get(prefix); if (uri == null) { uri = factory.getNamespaces().get(prefix); } if (uri != null) { key = '{' +uri + '}' + key.substring(index + 1, key.length()); } //TODO warnings or errors if not resolvable } return key; } }