/* * 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.query.parser; import java.io.Reader; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; 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.annotation.Updated; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.language.SQLConstants; import org.teiid.metadata.AbstractMetadataRecord; import org.teiid.metadata.BaseColumn; import org.teiid.metadata.Column; import org.teiid.metadata.Column.SearchType; import org.teiid.metadata.Datatype; import org.teiid.metadata.FunctionMethod; import org.teiid.metadata.FunctionMethod.PushDown; import org.teiid.metadata.FunctionParameter; import org.teiid.metadata.KeyRecord; import org.teiid.metadata.MetadataFactory; import org.teiid.metadata.Procedure; import org.teiid.metadata.ProcedureParameter; import org.teiid.metadata.ProcedureParameter.Type; import org.teiid.metadata.Table; import org.teiid.query.metadata.DDLConstants; import org.teiid.query.metadata.SystemMetadata; import org.teiid.query.parser.TeiidNodeFactory.ASTNodes; import org.teiid.query.sql.lang.AlterTrigger; import org.teiid.query.sql.lang.CacheHint; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Comment; import org.teiid.query.sql.lang.LanguageObject; import org.teiid.query.sql.lang.SPParameter; import org.teiid.query.sql.lang.SetQuery; import org.teiid.query.sql.lang.SourceHint; import org.teiid.query.sql.lang.StoredProcedure; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.runtime.client.Messages; import org.teiid.runtime.client.TeiidClientException; public abstract class AbstractTeiidParser implements TeiidParserSPI { protected static final Pattern udtPattern = Pattern.compile("(\\w+)\\s*\\(\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); //$NON-NLS-1$ @Since(Version.TEIID_8_12_4) protected static final Pattern FROM_CLAUSE_HINT_PATTERN = Pattern.compile("\\s*(\\w+(?:\\(\\s*(max:\\d+)?\\s*((?:no)?\\s*join)\\s*\\))?)\\s*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ protected static final Pattern SOURCE_HINT = Pattern.compile("\\s*sh(\\s+KEEP ALIASES)?\\s*(?::((?:'[^']*')+))?\\s*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$ protected static final Pattern SOURCE_HINT_ARG = Pattern.compile("\\s*([^: ]+)(\\s+KEEP ALIASES)?\\s*:((?:'[^']*')+)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$ private static final Pattern CACHE_HINT_PRE_811 = Pattern.compile("/\\*\\+?\\s*cache(\\(\\s*(pref_mem)?\\s*(ttl:\\d{1,19})?\\s*(updatable)?\\s*(scope:(session|vdb|user))?[^\\)]*\\))?[^\\*]*\\*\\/.*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$ private static final Pattern CACHE_HINT = Pattern.compile("/\\*\\+?\\s*cache(\\(\\s*(pref_mem)?\\s*(ttl:\\d{1,19})?\\s*(updatable)?\\s*(scope:(session|vdb|user))?\\s*(min:\\d{1,19})?[^\\)]*\\))?[^\\*]*\\*\\/.*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$ private static String COMMENT_START = "/*"; //$NON-NLS-1$ private static String COMMENT_END = "*/"; //$NON-NLS-1$ protected ITeiidServerVersion version; private MetadataFactory metadataFactory; private LanguageObject currentNode; private Set<Comment> commentCache; /** * Access to the javacc-generated Reinit method * * @param stream */ protected abstract void ReInit(Reader stream); /** * @param sql */ @Override public void reset(Reader sql) { this.ReInit(sql); if (commentCache != null) { // // Do not clear as the language objects created during parsing // have references to the same instance. This decouples this // parser from that instance // commentCache = null; } } /** * @return teiid instance version */ @Override public ITeiidServerVersion getVersion() { return version; } /** * @param teiidVersion */ @Override public void setVersion(ITeiidServerVersion teiidVersion) { this.version = teiidVersion; } /** * The version of this parser must be greater than * or equal to the given version. * * @param requiredVersionEnum * @return true if version less than required */ public boolean versionLessThan(Version requiredVersionEnum) { return getVersion().isLessThan(requiredVersionEnum.get()); } /** * The version of this parser must be than * or equal to the given version. * * @param requiredVersionEnum * @return true if version at least required */ public boolean versionAtLeast(Version requiredVersionEnum) { return ! versionLessThan(requiredVersionEnum); } @Override public DataTypeManagerService getDataTypeService() { return DataTypeManagerService.getInstance(getVersion()); } protected <T extends LanguageObject> T createASTNode(ASTNodes nodeType) { return TeiidNodeFactory.jjtCreate(this, nodeType); } /** * @param comment */ public void addComment(Comment comment) { if (commentCache == null) commentCache = new LinkedHashSet<Comment>(); commentCache.add(comment); if (currentNode != null) { // Have a current node so set the comments collection currentNode.setComments(commentCache); } } @Override public void setCurrentNode(LanguageObject currentNode) { if (currentNode == null) return; this.currentNode = currentNode; if (commentCache == null) commentCache = new LinkedHashSet<Comment>(); this.currentNode.setComments(commentCache); } protected String prependSign(String sign, String literal) { if (sign != null && sign.charAt(0) == '-') { return sign + literal; } return literal; } @Since(Version.TEIID_8_0) protected void convertToParameters(List<Expression> values, StoredProcedure storedProcedure, int paramIndex) { for (Expression value : values) { SPParameter parameter = new SPParameter(getVersion(), paramIndex++, value); parameter.setParameterType(SPParameter.IN); storedProcedure.setParameter(parameter); } } private static int parseNumericValue(CharSequence string, StringBuilder sb, int i, int value, int possibleDigits, int radixExp) { for (int j = 0; j < possibleDigits; j++) { if (i + 1 == string.length()) { break; } char digit = string.charAt(i + 1); int val = Character.digit(digit, 1 << radixExp); if (val == -1) { break; } i++; value = (value << radixExp) + val; } sb.append((char)value); return i; } /** * Unescape the given string * @param string * @param quoteChar * @param useAsciiExcapes * @param sb a scratch buffer to use * @return */ private static String unescape(CharSequence string, int quoteChar, boolean useAsciiEscapes, StringBuilder sb) { boolean escaped = false; for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (escaped) { switch (c) { case 'b': sb.append('\b'); break; case 't': sb.append('\t'); break; case 'n': sb.append('\n'); break; case 'f': sb.append('\f'); break; case 'r': sb.append('\r'); break; case 'u': i = parseNumericValue(string, sb, i, 0, 4, 4); //TODO: this should probably be strict about needing 4 digits break; default: if (c == quoteChar) { sb.append(quoteChar); } else if (useAsciiEscapes) { int value = Character.digit(c, 8); if (value == -1) { sb.append(c); } else { int possibleDigits = value < 3 ? 2:1; int radixExp = 3; i = parseNumericValue(string, sb, i, value, possibleDigits, radixExp); } } } escaped = false; } else { if (c == '\\') { escaped = true; } else if (c == quoteChar) { break; } else { sb.append(c); } } } return sb.toString(); } private static String unescape(String string) { return unescape(string, -1, true, new StringBuilder()); } private static String removeEscapeChars(String str, String tickChar) { return StringUtil.replaceAll(str, tickChar + tickChar, tickChar); } /** * @param s * @return normalized version of s */ public static String normalizeStringLiteral(String s) { int start = 1; boolean unescape = false; if (s.charAt(0) == 'N') { start++; } else if (s.charAt(0) == 'E') { start++; unescape = true; } char tickChar = s.charAt(start - 1); s = s.substring(start, s.length() - 1); String result = removeEscapeChars(s, String.valueOf(tickChar)); if (unescape) { result = unescape(result); } return result; } /** * @param s * @return normalized string id */ protected String normalizeId(String s) { if (s.indexOf('"') == -1) { return s; } List<String> nameParts = new LinkedList<String>(); while (s.length() > 0) { if (s.charAt(0) == '"') { boolean escape = false; for (int i = 1; i < s.length(); i++) { if (s.charAt(i) != '"') { continue; } escape = !escape; boolean end = i == s.length() - 1; if (end || (escape && s.charAt(i + 1) == '.')) { String part = s.substring(1, i); s = s.substring(i + (end?1:2)); nameParts.add(removeEscapeChars(part, "\"")); //$NON-NLS-1$ break; } } } else { int index = s.indexOf('.'); if (index == -1) { nameParts.add(s); break; } nameParts.add(s.substring(0, index)); s = s.substring(index + 1); } } StringBuilder sb = new StringBuilder(); for (Iterator<String> i = nameParts.iterator(); i.hasNext();) { sb.append(i.next()); if (i.hasNext()) { sb.append('.'); } } return sb.toString(); } /** * Check if this is a valid string literal * @param id Possible string literal */ protected boolean isStringLiteral(String str, ParseInfo info) { if (info.useAnsiQuotedIdentifiers() || str.charAt(0) != '"' || str.charAt(str.length() - 1) != '"') { return false; } int index = 1; while (index < str.length() - 1) { index = str.indexOf('"', index); if (index == -1 || index + 1 == str.length()) { return true; } if (str.charAt(index + 1) != '"') { return false; } index += 2; } return true; } protected String validateName(String id, boolean nonAlias) throws Exception { if(id.indexOf('.') != -1) { Messages.TeiidParser key = Messages.TeiidParser.Invalid_alias; if (nonAlias) { key = Messages.TeiidParser.Invalid_short_name; } throw new TeiidClientException(Messages.getString(key, id)); } return id; } @Override public SourceHint getSourceHint(String comment) { Matcher matcher = SOURCE_HINT.matcher(comment); if (!matcher.find()) { return null; } SourceHint sourceHint = new SourceHint(getVersion()); if (matcher.group(1) != null) { sourceHint.setUseAliases(true); } String generalHint = matcher.group(2); if (generalHint != null) { sourceHint.setGeneralHint(normalizeStringLiteral(generalHint)); } int end = matcher.end(); matcher = SOURCE_HINT_ARG.matcher(comment); while (matcher.find(end)) { end = matcher.end(); sourceHint.setSourceHint(matcher.group(1), normalizeStringLiteral(matcher.group(3)), matcher.group(2) != null); } return sourceHint; } @Override public CacheHint getQueryCacheOption(String query) { Pattern cacheHint = CACHE_HINT; if (getVersion().isLessThan(Version.TEIID_8_11)) cacheHint = CACHE_HINT_PRE_811; Matcher match = cacheHint.matcher(query); if (match.matches()) { CacheHint hint = new CacheHint(getVersion()); if (match.group(2) !=null) { hint.setPrefersMemory(true); } String ttl = match.group(3); if (ttl != null) { hint.setTtl(Long.valueOf(ttl.substring(4))); } if (match.group(4) != null) { hint.setUpdatable(true); } String scope = match.group(5); if (scope != null) { scope = scope.substring(6); hint.setScope(scope); } if (getVersion().isGreaterThanOrEqualTo(Version.TEIID_8_11)) { String min = match.group(7); if (min != null) { hint.setMinRows(Long.valueOf(min.substring(4))); } } return hint; } return null; } @Removed(Version.TEIID_8_0) protected String matchesAny(String arg, String ... expected) { for (String string : expected) { if (string.equalsIgnoreCase(arg)) { return arg; } } return null; } @Override public Command procedureBodyCommand(ParseInfo parseInfo) throws Exception { throw new UnsupportedOperationException("Not supported in Teiid Version " + getVersion()); //$NON-NLS-1$ } /** * @param factory * @throws Exception */ @Override public void parseMetadata(MetadataFactory factory) throws Exception { throw new UnsupportedOperationException("Not supported in Teiid Version " + getVersion()); //$NON-NLS-1$ } @Since(Version.TEIID_8_0) protected void setColumnOptions(BaseColumn c) { Map<String, String> props = c.getProperties(); setCommonProperties(c, props); String v = props.remove(DDLConstants.RADIX); if (v != null) { c.setRadix(Integer.parseInt(v)); } if (c instanceof Column) { setColumnOptions((Column)c, props); } } @Since(Version.TEIID_8_0) protected void removeColumnOption(String key, BaseColumn c) { if (c.getProperty(key, false) != null) { c.setProperty(key, null); } removeCommonProperty(key, c); if (key.equals(DDLConstants.RADIX)) { c.setRadix(0); } if (c instanceof Column) { removeColumnOption(key, (Column)c); } } @Since(Version.TEIID_8_0) protected void removeColumnOption(String key, Column c) { if (key.equals(DDLConstants.CASE_SENSITIVE)) { c.setCaseSensitive(false); } if (key.equals(DDLConstants.SELECTABLE)) { c.setSelectable(true); } if (key.equals(DDLConstants.UPDATABLE)) { c.setUpdatable(false); } if (key.equals(DDLConstants.SIGNED)) { c.setSigned(false); } if (key.equals(DDLConstants.CURRENCY)) { c.setSigned(false); } if (key.equals(DDLConstants.FIXED_LENGTH)) { c.setFixedLength(false); } if (key.equals(DDLConstants.SEARCHABLE)) { c.setSearchType(null); } if (key.equals(DDLConstants.MIN_VALUE)) { c.setMinimumValue(null); } if (key.equals(DDLConstants.MAX_VALUE)) { c.setMaximumValue(null); } if (key.equals(DDLConstants.CHAR_OCTET_LENGTH)) { c.setCharOctetLength(0); } if (key.equals(DDLConstants.NATIVE_TYPE)) { c.setNativeType(null); } if (key.equals(DDLConstants.NULL_VALUE_COUNT)) { c.setNullValues(-1); } if (key.equals(DDLConstants.DISTINCT_VALUES)) { c.setDistinctValues(-1); } if (key.equals(DDLConstants.UDT)) { c.setDatatype(null); c.setLength(0); c.setPrecision(0); c.setScale(0); } } @Since(Version.TEIID_8_0) private void setColumnOptions(Column c, Map<String, String> props) { String v = props.remove(DDLConstants.CASE_SENSITIVE); if (v != null) { c.setCaseSensitive(isTrue(v)); } v = props.remove(DDLConstants.SELECTABLE); if (v != null) { c.setSelectable(isTrue(v)); } v = props.remove(DDLConstants.UPDATABLE); if (v != null) { c.setUpdatable(isTrue(v)); } v = props.remove(DDLConstants.SIGNED); if (v != null) { c.setSigned(isTrue(v)); } v = props.remove(DDLConstants.CURRENCY); if (v != null) { c.setSigned(isTrue(v)); } v = props.remove(DDLConstants.FIXED_LENGTH); if (v != null) { c.setFixedLength(isTrue(v)); } v = props.remove(DDLConstants.SEARCHABLE); if (v != null) { c.setSearchType(StringUtil.caseInsensitiveValueOf(SearchType.class, v)); } v = props.remove(DDLConstants.MIN_VALUE); if (v != null) { c.setMinimumValue(v); } v = props.remove(DDLConstants.MAX_VALUE); if (v != null) { c.setMaximumValue(v); } v = props.remove(DDLConstants.CHAR_OCTET_LENGTH); if (v != null) { c.setCharOctetLength(Integer.parseInt(v)); } v = props.remove(DDLConstants.NATIVE_TYPE); if (v != null) { c.setNativeType(v); } v = props.remove(DDLConstants.NULL_VALUE_COUNT); if (v != null) { c.setNullValues(Integer.parseInt(v)); } v = props.remove(DDLConstants.DISTINCT_VALUES); if (v != null) { c.setDistinctValues(Integer.parseInt(v)); } v = props.remove(DDLConstants.UDT); if (v != null) { Matcher matcher = udtPattern.matcher(v); Map<String, Datatype> datatypes = SystemMetadata.getInstance(getVersion()).getSystemStore().getDatatypes(); if (matcher.matches() && datatypes.get(matcher.group(1)) != null) { c.setDatatype(datatypes.get(matcher.group(1))); c.setLength(Integer.parseInt(matcher.group(2))); c.setPrecision(Integer.parseInt(matcher.group(3))); c.setScale(Integer.parseInt(matcher.group(4))); } else { throw new RuntimeException(Messages.getString(Messages.TeiidParser.udt_format_wrong, c.getName())); } } } @Since(Version.TEIID_8_0) protected void setCommonProperties(AbstractMetadataRecord c, Map<String, String> props) { String v = props.remove(DDLConstants.UUID); if (v != null) { c.setUUID(v); } v = props.remove(DDLConstants.ANNOTATION); if (v != null) { c.setAnnotation(v); } v = props.remove(DDLConstants.NAMEINSOURCE); if (v != null) { c.setNameInSource(v); } } @Since(Version.TEIID_8_0) protected void removeCommonProperty(String key, AbstractMetadataRecord c) { if (key.equals(DDLConstants.UUID)) { c.setUUID(null); } if (key.equals(DDLConstants.ANNOTATION)) { c.setAnnotation(null); } if (key.equals(DDLConstants.NAMEINSOURCE)) { c.setNameInSource(null); } } @Since(Version.TEIID_8_0) protected void setTableOptions(Table table) { Map<String, String> props = table.getProperties(); setCommonProperties(table, props); String value = props.remove(DDLConstants.MATERIALIZED); if (value != null) { table.setMaterialized(isTrue(value)); } value = props.remove(DDLConstants.MATERIALIZED_TABLE); if (value != null) { Table mattable = new Table(); mattable.setName(value); table.setMaterializedTable(mattable); } value = props.remove(DDLConstants.UPDATABLE); if (value != null) { table.setSupportsUpdate(isTrue(value)); } value = props.remove(DDLConstants.CARDINALITY); if (value != null) { table.setCardinality(Integer.parseInt(value)); } } @Since(Version.TEIID_8_0) protected void removeTableOption(String key, Table table) { if (table.getProperty(key, false) != null) { table.setProperty(key, null); } removeCommonProperty(key, table); if (key.equals(DDLConstants.MATERIALIZED)) { table.setMaterialized(false); } if (key.equals(DDLConstants.MATERIALIZED_TABLE)) { table.setMaterializedTable(null); } if (key.equals(DDLConstants.UPDATABLE)) { table.setSupportsUpdate(false); } if (key.equals(DDLConstants.CARDINALITY)) { table.setCardinality(-1); } } @Since(Version.TEIID_8_0) @Updated(version=Version.TEIID_8_11) protected void replaceProcedureWithFunction(MetadataFactory factory, Procedure proc) { if (proc.isFunction() && proc.getQueryPlan() != null) { return; } FunctionMethod method = createFunctionMethod(getVersion(), proc); //remove the old proc factory.getSchema().getResolvingOrder().remove(factory.getSchema().getResolvingOrder().size() - 1); factory.getSchema().getProcedures().remove(proc.getName()); factory.getSchema().addFunction(method); } /** * @param teiidVersion * @param proc * @return a function method from given procedure */ public static FunctionMethod createFunctionMethod(ITeiidServerVersion teiidVersion, Procedure proc) { FunctionMethod method = new FunctionMethod(); method.setName(proc.getName()); method.setPushdown(proc.isVirtual()?FunctionMethod.PushDown.CAN_PUSHDOWN:FunctionMethod.PushDown.MUST_PUSHDOWN); ArrayList<FunctionParameter> ins = new ArrayList<FunctionParameter>(); for (ProcedureParameter pp:proc.getParameters()) { if (pp.getType() == ProcedureParameter.Type.InOut || pp.getType() == ProcedureParameter.Type.Out) { throw new RuntimeException(Messages.getString(Messages.TeiidParser.function_in, proc.getName())); } // New functionality for Teiid 8.9+ if (teiidVersion.isGreaterThanOrEqualTo(Version.TEIID_8_9)) { //copy the metadata FunctionParameter fp = new FunctionParameter(teiidVersion, pp.getName(), pp.getRuntimeType(), pp.getAnnotation()); fp.setDatatype(pp.getDatatype(), true, pp.getArrayDimensions()); fp.setLength(pp.getLength()); fp.setNameInSource(pp.getNameInSource()); fp.setNativeType(pp.getNativeType()); fp.setNullType(pp.getNullType()); fp.setProperties(pp.getProperties()); fp.setRadix(pp.getRadix()); fp.setScale(pp.getScale()); fp.setUUID(pp.getUUID()); if (pp.getType() == ProcedureParameter.Type.In) { fp.setVarArg(pp.isVarArg()); ins.add(fp); fp.setPosition(ins.size()); } else { method.setOutputParameter(fp); fp.setPosition(0); } } else { // Functionality for older parsers 8.8 and below FunctionParameter fp = new FunctionParameter(teiidVersion, pp.getName(), pp.getRuntimeType(), pp.getAnnotation()); if (pp.getType() == ProcedureParameter.Type.In) { fp.setVarArg(pp.isVarArg()); ins.add(fp); } else { method.setOutputParameter(fp); } } } method.setInputParameters(ins); if (proc.getResultSet() != null || method.getOutputParameter() == null) { throw new RuntimeException(Messages.getString(Messages.TeiidParser.function_return, proc.getName())); } method.setAnnotation(proc.getAnnotation()); method.setNameInSource(proc.getNameInSource()); method.setUUID(proc.getUUID()); Map<String, String> props = proc.getProperties(); String value = props.remove(DDLConstants.CATEGORY); method.setCategory(value); value = props.remove(DDLConstants.DETERMINISM); if (value != null) { method.setDeterminism(FunctionMethod.Determinism.valueOf(value.toUpperCase())); } value = props.remove(DDLConstants.JAVA_CLASS); method.setInvocationClass(value); value = props.remove(DDLConstants.JAVA_METHOD); method.setInvocationMethod(value); for (String key:props.keySet()) { value = props.get(key); method.setProperty(key, value); } FunctionMethod.convertExtensionMetadata(proc, method); if (method.getInvocationMethod() != null) { method.setPushdown(PushDown.CAN_PUSHDOWN); } return method; } @Since(Version.TEIID_8_0) protected void setProcedureOptions(Procedure proc) { Map<String, String> props = proc.getProperties(); setCommonProperties(proc, props); String value = props.remove("UPDATECOUNT"); //$NON-NLS-1$ if (value != null) { proc.setUpdateCount(Integer.parseInt(value)); } } @Since(Version.TEIID_8_0) protected void removeOption(String option, AbstractMetadataRecord record) { if (record instanceof Table) { removeTableOption(option, (Table)record); } if (record instanceof Procedure) { removeProcedureOption(option, (Procedure)record); } if (record instanceof BaseColumn) { removeColumnOption(option, (BaseColumn)record); } } @Since(Version.TEIID_8_0) protected void setOptions(AbstractMetadataRecord record) { if (record instanceof Table) { setTableOptions((Table)record); } if (record instanceof Procedure) { setProcedureOptions((Procedure)record); } if (record instanceof BaseColumn) { setColumnOptions((BaseColumn)record); } } @Since(Version.TEIID_8_0) protected void removeProcedureOption(String key, Procedure proc) { if (proc.getProperty(key, false) != null) { proc.setProperty(key, null); } removeCommonProperty(key, proc); if (key.equals("UPDATECOUNT")) { //$NON-NLS-1$ proc.setUpdateCount(1); } } @Since(Version.TEIID_8_0) protected boolean isTrue(final String text) { return Boolean.valueOf(text); } @Since(Version.TEIID_8_0) protected AbstractMetadataRecord getChild(String name, AbstractMetadataRecord record, boolean parameter) { if (record instanceof Table) { if (parameter) { throw new RuntimeException(Messages.getString(Messages.TeiidParser.alter_table_param, name, record.getName())); } return getColumn(name, (Table)record); } return getColumn(name, (Procedure)record, parameter); //TODO: function is not supported yet because we store by uid, which should instead be a more friendly "unique name" } @Since(Version.TEIID_8_0) protected Column getColumn(String columnName, Table table) { Column c = table.getColumnByName(columnName); if (c != null) { return c; } throw new RuntimeException(Messages.getString(Messages.TeiidParser.no_column, columnName, table.getName())); } @Since(Version.TEIID_8_0) protected AbstractMetadataRecord getColumn(String paramName, Procedure proc, boolean parameter) { if (proc.getResultSet() != null) { Column result = proc.getResultSet().getColumnByName(paramName); if (result != null) { return result; } } if (parameter) { List<ProcedureParameter> params = proc.getParameters(); for (ProcedureParameter param:params) { if (param.getName().equalsIgnoreCase(paramName)) { return param; } } } throw new RuntimeException(Messages.getString(Messages.TeiidParser.alter_procedure_param_doesnot_exist, paramName, proc.getName())); } @Since(Version.TEIID_8_0) protected FunctionParameter getParameter(String paramName, FunctionMethod func) { List<FunctionParameter> params = func.getInputParameters(); for (FunctionParameter param:params) { if (param.getName().equalsIgnoreCase(paramName)) { return param; } } throw new RuntimeException(Messages.getString(Messages.TeiidParser.alter_function_param_doesnot_exist, paramName, func.getName())); } @Since(Version.TEIID_8_0) protected void createDDLTrigger(MetadataFactory schema, AlterTrigger trigger) { GroupSymbol group = trigger.getTarget(); Table table = schema.getSchema().getTable(group.getName()); if (trigger.getEvent().equals(Table.TriggerEvent.INSERT)) { table.setInsertPlan(trigger.getDefinition().toString()); } else if (trigger.getEvent().equals(Table.TriggerEvent.UPDATE)) { table.setUpdatePlan(trigger.getDefinition().toString()); } else if (trigger.getEvent().equals(Table.TriggerEvent.DELETE)) { table.setDeletePlan(trigger.getDefinition().toString()); } } @Since(Version.TEIID_8_0) protected BaseColumn addProcColumn(MetadataFactory factory, Procedure proc, String name, ParsedDataType type, boolean rs) { BaseColumn column = null; if (rs) { column = factory.addProcedureResultSetColumn(name, type.getType(), proc); } else { boolean added = false; for (ProcedureParameter pp : proc.getParameters()) { if (pp.getType() == Type.ReturnValue) { added = true; if (pp.getDatatype() != factory.getDataTypes().get(type.getType())) { throw new RuntimeException(Messages.getString(Messages.TeiidParser.proc_type_conflict, proc.getName(), pp.getDatatype(), type.getType())); } } } if (!added) { column = factory.addProcedureParameter(name, type.getType(), ProcedureParameter.Type.ReturnValue, proc); } } setTypeInfo(type, column); return column; } @Since(Version.TEIID_8_0) protected void setTypeInfo(ParsedDataType type, BaseColumn column) { if (type.getLength() != null){ column.setLength(type.getLength()); } if (type.getScale() != null){ column.setScale(type.getScale()); } if (type.getPrecision() != null){ column.setPrecision(type.getPrecision()); } } @Since(Version.TEIID_8_0) protected KeyRecord addFBI(MetadataFactory factory, List<Expression> expressions, Table table, String name) { List<String> columnNames = new ArrayList<String>(expressions.size()); List<Boolean> nonColumnExpressions = new ArrayList<Boolean>(expressions.size()); boolean fbi = false; for (int i = 0; i < expressions.size(); i++) { Expression ex = expressions.get(i); if (ex instanceof ElementSymbol) { columnNames.add(((ElementSymbol)ex).getName()); nonColumnExpressions.add(Boolean.FALSE); } else { columnNames.add(ex.toString()); nonColumnExpressions.add(Boolean.TRUE); fbi = true; } } return factory.addFunctionBasedIndex(name != null?name:(SQLConstants.NonReserved.INDEX+(fbi?table.getFunctionBasedIndexes().size():table.getIndexes().size())), columnNames, nonColumnExpressions, table); } @Since(Version.TEIID_8_0) protected MetadataFactory getTempMetadataFactory() { if (this.metadataFactory == null) { this.metadataFactory = new MetadataFactory(version, "temp", 1, "temp", //$NON-NLS-1$ //$NON-NLS-2$ SystemMetadata.getInstance(getVersion()).getRuntimeTypeMap(), null, null); } return this.metadataFactory; } @Since(Version.TEIID_8_5) protected void setSourceHint(SourceHint sourceHint, Command command) { if (sourceHint == null) return; if (command instanceof SetQuery) { ((SetQuery)command).getProjectedQuery().setSourceHint(sourceHint); } else { command.setSourceHint(sourceHint); } } @Since(Version.TEIID_8_5) protected List<Expression> arrayExpressions(List<Expression> expressions, Expression expr) { if (expressions == null) { expressions = new ArrayList<Expression>(); } if (expr != null) { expressions.add(expr); } return expressions; } }