package com.tesora.dve.sql.parser; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import com.tesora.dve.charset.*; import com.tesora.dve.db.DBNative; import com.tesora.dve.variables.VariableService; import com.tesora.dve.sql.infoschema.InformationSchemaService; import org.antlr.runtime.Lexer; import org.antlr.runtime.Token; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import com.tesora.dve.common.PEStringUtils; import com.tesora.dve.common.catalog.CatalogEntity; import com.tesora.dve.common.catalog.ConstraintType; import com.tesora.dve.common.catalog.Container; import com.tesora.dve.common.catalog.DynamicPolicy; import com.tesora.dve.common.catalog.ExternalService; import com.tesora.dve.common.catalog.FKMode; import com.tesora.dve.common.catalog.IndexType; import com.tesora.dve.common.catalog.Key; import com.tesora.dve.common.catalog.KeyColumn; import com.tesora.dve.common.catalog.MultitenantMode; import com.tesora.dve.common.catalog.PersistentGroup; import com.tesora.dve.common.catalog.PersistentTemplate; import com.tesora.dve.common.catalog.Provider; import com.tesora.dve.common.catalog.TableState; import com.tesora.dve.common.catalog.TemplateMode; import com.tesora.dve.common.catalog.User; import com.tesora.dve.common.catalog.UserDatabase; import com.tesora.dve.common.catalog.UserTable; import com.tesora.dve.db.DBResultConsumer; import com.tesora.dve.db.ValueConverter; import com.tesora.dve.distribution.DistributionRange; import com.tesora.dve.errmap.AvailableErrors; import com.tesora.dve.errmap.ErrorInfo; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.lockmanager.LockManager; import com.tesora.dve.queryplan.ExecutionState; import com.tesora.dve.queryplan.QueryStepGeneralOperation.AdhocOperation; import com.tesora.dve.resultset.ColumnInfo; import com.tesora.dve.resultset.ProjectionInfo; import com.tesora.dve.server.connectionmanager.UserXid; import com.tesora.dve.singleton.Singletons; import com.tesora.dve.siteprovider.SiteProviderPlugin; import com.tesora.dve.siteprovider.SiteProviderPlugin.SiteProviderFactory; import com.tesora.dve.sql.ParserException; import com.tesora.dve.sql.ParserException.Pass; import com.tesora.dve.sql.SchemaException; import com.tesora.dve.sql.expression.Scope; import com.tesora.dve.sql.expression.ScopeParsePhase; import com.tesora.dve.sql.expression.ScopeStack; import com.tesora.dve.sql.expression.SetQuantifier; import com.tesora.dve.sql.expression.TableKey; import com.tesora.dve.sql.infoschema.InformationSchemaTable; import com.tesora.dve.sql.infoschema.ShowOptions; import com.tesora.dve.sql.infoschema.ShowSchemaBehavior; import com.tesora.dve.sql.infoschema.direct.DirectShowStatusInformation; import com.tesora.dve.sql.infoschema.direct.DirectShowVariablesTable; import com.tesora.dve.sql.node.Edge; import com.tesora.dve.sql.node.EdgeName; import com.tesora.dve.sql.node.MigrationException; import com.tesora.dve.sql.node.expression.ActualLiteralExpression; import com.tesora.dve.sql.node.expression.Alias; import com.tesora.dve.sql.node.expression.CaseExpression; import com.tesora.dve.sql.node.expression.CastFunctionCall; import com.tesora.dve.sql.node.expression.CharFunctionCall; import com.tesora.dve.sql.node.expression.ColumnInstance; import com.tesora.dve.sql.node.expression.ConvertFunctionCall; import com.tesora.dve.sql.node.expression.Default; import com.tesora.dve.sql.node.expression.DelegatingLiteralExpression; import com.tesora.dve.sql.node.expression.ExpressionNode; import com.tesora.dve.sql.node.expression.ExpressionSet; import com.tesora.dve.sql.node.expression.FunctionCall; import com.tesora.dve.sql.node.expression.GroupConcatCall; import com.tesora.dve.sql.node.expression.IdentifierLiteralExpression; import com.tesora.dve.sql.node.expression.IndexHint; import com.tesora.dve.sql.node.expression.IndexHint.HintTarget; import com.tesora.dve.sql.node.expression.IndexHint.HintType; import com.tesora.dve.sql.node.expression.IntervalExpression; import com.tesora.dve.sql.node.expression.LiteralExpression; import com.tesora.dve.sql.node.expression.NameAlias; import com.tesora.dve.sql.node.expression.NameInstance; import com.tesora.dve.sql.node.expression.Parameter; import com.tesora.dve.sql.node.expression.RandFunctionCall; import com.tesora.dve.sql.node.expression.StringLiteralAlias; import com.tesora.dve.sql.node.expression.Subquery; import com.tesora.dve.sql.node.expression.TableInstance; import com.tesora.dve.sql.node.expression.TableJoin; import com.tesora.dve.sql.node.expression.TriggerTableInstance; import com.tesora.dve.sql.node.expression.TriggerTableInstance.EarlyTriggerTableCollector; import com.tesora.dve.sql.node.expression.ValueSource; import com.tesora.dve.sql.node.expression.VariableInstance; import com.tesora.dve.sql.node.expression.WhenClause; import com.tesora.dve.sql.node.expression.Wildcard; import com.tesora.dve.sql.node.structural.FromTableReference; import com.tesora.dve.sql.node.structural.JoinClauseType; import com.tesora.dve.sql.node.structural.JoinClauseType.ClauseType; import com.tesora.dve.sql.node.structural.JoinSpecification; import com.tesora.dve.sql.node.structural.JoinedTable; import com.tesora.dve.sql.node.structural.LimitSpecification; import com.tesora.dve.sql.node.structural.SortingSpecification; import com.tesora.dve.sql.node.test.EngineConstant; import com.tesora.dve.sql.parser.ParserOptions.Option; import com.tesora.dve.sql.schema.Capability; import com.tesora.dve.sql.schema.Comment; import com.tesora.dve.sql.schema.ComplexPETable; import com.tesora.dve.sql.schema.ContainerDistributionVector; import com.tesora.dve.sql.schema.ContainerPolicyContext; import com.tesora.dve.sql.schema.Database; import com.tesora.dve.sql.schema.DistributionVector; import com.tesora.dve.sql.schema.ExplainOptions; import com.tesora.dve.sql.schema.ExplainOptions.ExplainOption; import com.tesora.dve.sql.schema.FloatSizeTypeAttribute; import com.tesora.dve.sql.schema.ForeignKeyAction; import com.tesora.dve.sql.schema.FunctionName; import com.tesora.dve.sql.schema.GrantScope; import com.tesora.dve.sql.schema.LoadDataInfileColOption; import com.tesora.dve.sql.schema.LoadDataInfileLineOption; import com.tesora.dve.sql.schema.LoadDataInfileModifier; import com.tesora.dve.sql.schema.LockInfo; import com.tesora.dve.sql.schema.Name; import com.tesora.dve.sql.schema.PEAbstractTable; import com.tesora.dve.sql.schema.PEAbstractTable.TableCacheKey; import com.tesora.dve.sql.schema.PEColumn; import com.tesora.dve.sql.schema.PEContainer; import com.tesora.dve.sql.schema.PEDatabase; import com.tesora.dve.sql.schema.PEExternalService; import com.tesora.dve.sql.schema.PEForeignKey; import com.tesora.dve.sql.schema.PEForeignKeyColumn; import com.tesora.dve.sql.schema.PEForwardForeignKeyColumn; import com.tesora.dve.sql.schema.PEForwardKeyColumn; import com.tesora.dve.sql.schema.PEKey; import com.tesora.dve.sql.schema.PEKeyColumn; import com.tesora.dve.sql.schema.PEKeyColumnBase; import com.tesora.dve.sql.schema.PEPersistentGroup; import com.tesora.dve.sql.schema.PEPolicy; import com.tesora.dve.sql.schema.PEPolicyClassConfig; import com.tesora.dve.sql.schema.PEProvider; import com.tesora.dve.sql.schema.PERawPlan; import com.tesora.dve.sql.schema.PESiteInstance; import com.tesora.dve.sql.schema.PEStorageSite; import com.tesora.dve.sql.schema.PETable; import com.tesora.dve.sql.schema.PETemplate; import com.tesora.dve.sql.schema.PETrigger; import com.tesora.dve.sql.schema.PEUser; import com.tesora.dve.sql.schema.PEView; import com.tesora.dve.sql.schema.Persistable; import com.tesora.dve.sql.schema.PolicyClass; import com.tesora.dve.sql.schema.QualifiedName; import com.tesora.dve.sql.schema.RangeDistribution; import com.tesora.dve.sql.schema.SQLMode; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.schema.SizeTypeAttribute; import com.tesora.dve.sql.schema.SubqueryTable; import com.tesora.dve.sql.schema.Table; import com.tesora.dve.sql.schema.TableComponent; import com.tesora.dve.sql.schema.TableResolver; import com.tesora.dve.sql.schema.TriggerEvent; import com.tesora.dve.sql.schema.TriggerTime; import com.tesora.dve.sql.schema.UnqualifiedName; import com.tesora.dve.sql.schema.UnresolvedDistributionVector; import com.tesora.dve.sql.schema.UserScope; import com.tesora.dve.sql.schema.ValueManager; import com.tesora.dve.sql.schema.VariableScope; import com.tesora.dve.sql.schema.VariableScopeKind; import com.tesora.dve.sql.schema.cache.IAutoIncrementLiteralExpression; import com.tesora.dve.sql.schema.cache.IDelegatingLiteralExpression; import com.tesora.dve.sql.schema.cache.IParameter; import com.tesora.dve.sql.schema.modifiers.AutoincTableModifier; import com.tesora.dve.sql.schema.modifiers.CharsetCollationModifierBuilder; import com.tesora.dve.sql.schema.modifiers.ChecksumModifier; import com.tesora.dve.sql.schema.modifiers.ColumnKeyModifier; import com.tesora.dve.sql.schema.modifiers.ColumnModifier; import com.tesora.dve.sql.schema.modifiers.ColumnModifierKind; import com.tesora.dve.sql.schema.modifiers.CommentTableModifier; import com.tesora.dve.sql.schema.modifiers.DefaultValueModifier; import com.tesora.dve.sql.schema.modifiers.EngineTableModifier; import com.tesora.dve.sql.schema.modifiers.InsertModifier; import com.tesora.dve.sql.schema.modifiers.MaxRowsModifier; import com.tesora.dve.sql.schema.modifiers.RowFormatTableModifier; import com.tesora.dve.sql.schema.modifiers.StringTypeModifier; import com.tesora.dve.sql.schema.modifiers.TableModifier; import com.tesora.dve.sql.schema.modifiers.TypeModifier; import com.tesora.dve.sql.schema.modifiers.TypeModifierKind; import com.tesora.dve.sql.schema.modifiers.UnknownTableModifier; import com.tesora.dve.sql.schema.mt.PETenant; import com.tesora.dve.sql.schema.mt.TenantColumn; import com.tesora.dve.sql.schema.types.BasicType; import com.tesora.dve.sql.schema.types.DBEnumType; import com.tesora.dve.sql.schema.types.TempColumnType; import com.tesora.dve.sql.schema.types.Type; import com.tesora.dve.sql.statement.EmptyStatement; import com.tesora.dve.sql.statement.Statement; import com.tesora.dve.sql.statement.StatementTraits; import com.tesora.dve.sql.statement.StatementType; import com.tesora.dve.sql.statement.ddl.AddGlobalVariableStatement; import com.tesora.dve.sql.statement.ddl.AddStorageSiteStatement; import com.tesora.dve.sql.statement.ddl.AlterDatabaseStatement; import com.tesora.dve.sql.statement.ddl.AlterDatabaseTemplateStatement; import com.tesora.dve.sql.statement.ddl.GrantStatement; import com.tesora.dve.sql.statement.ddl.PEAlterExternalServiceStatement; import com.tesora.dve.sql.statement.ddl.PEAlterGroupProviderStatement; import com.tesora.dve.sql.statement.ddl.PEAlterPersistentSite; import com.tesora.dve.sql.statement.ddl.PEAlterPolicyStatement; import com.tesora.dve.sql.statement.ddl.PEAlterRawPlanStatement; import com.tesora.dve.sql.statement.ddl.PEAlterSiteInstanceStatement; import com.tesora.dve.sql.statement.ddl.PEAlterStatement; import com.tesora.dve.sql.statement.ddl.PEAlterTableStatement; import com.tesora.dve.sql.statement.ddl.PEAlterTemplateStatement; import com.tesora.dve.sql.statement.ddl.PECreateDatabaseStatement; import com.tesora.dve.sql.statement.ddl.PECreateExternalServiceStatement; import com.tesora.dve.sql.statement.ddl.PECreateGroupProviderStatement; import com.tesora.dve.sql.statement.ddl.PECreateRawPlanStatement; import com.tesora.dve.sql.statement.ddl.PECreateSiteInstanceStatement; import com.tesora.dve.sql.statement.ddl.PECreateStatement; import com.tesora.dve.sql.statement.ddl.PECreateStorageSiteStatement; import com.tesora.dve.sql.statement.ddl.PECreateTableAsSelectStatement; import com.tesora.dve.sql.statement.ddl.PECreateTableStatement; import com.tesora.dve.sql.statement.ddl.PECreateTriggerStatement; import com.tesora.dve.sql.statement.ddl.PECreateUserStatement; import com.tesora.dve.sql.statement.ddl.PECreateViewStatement; import com.tesora.dve.sql.statement.ddl.PEDropContainerStatement; import com.tesora.dve.sql.statement.ddl.PEDropExternalServiceStatement; import com.tesora.dve.sql.statement.ddl.PEDropGroupProviderStatement; import com.tesora.dve.sql.statement.ddl.PEDropRangeStatement; import com.tesora.dve.sql.statement.ddl.PEDropRawPlanStatement; import com.tesora.dve.sql.statement.ddl.PEDropStatement; import com.tesora.dve.sql.statement.ddl.PEDropStorageGroupStatement; import com.tesora.dve.sql.statement.ddl.PEDropStorageSiteStatement; import com.tesora.dve.sql.statement.ddl.PEDropTableStatement; import com.tesora.dve.sql.statement.ddl.PEDropTriggerStatement; import com.tesora.dve.sql.statement.ddl.PEDropUserStatement; import com.tesora.dve.sql.statement.ddl.PEDropViewStatement; import com.tesora.dve.sql.statement.ddl.PEGroupProviderDDLStatement; import com.tesora.dve.sql.statement.ddl.RenameTableStatement; import com.tesora.dve.sql.statement.ddl.SchemaQueryStatement; import com.tesora.dve.sql.statement.ddl.SetPasswordStatement; import com.tesora.dve.sql.statement.ddl.ShowPlanCacheStatement; import com.tesora.dve.sql.statement.ddl.alter.AddColumnAction; import com.tesora.dve.sql.statement.ddl.alter.AddIndexAction; import com.tesora.dve.sql.statement.ddl.alter.AlterColumnAction; import com.tesora.dve.sql.statement.ddl.alter.AlterTableAction; import com.tesora.dve.sql.statement.ddl.alter.ChangeColumnAction; import com.tesora.dve.sql.statement.ddl.alter.ChangeKeysStatusAction; import com.tesora.dve.sql.statement.ddl.alter.ChangeTableDistributionAction; import com.tesora.dve.sql.statement.ddl.alter.ChangeTableModifierAction; import com.tesora.dve.sql.statement.ddl.alter.ConvertToAction; import com.tesora.dve.sql.statement.ddl.alter.DropColumnAction; import com.tesora.dve.sql.statement.ddl.alter.DropIndexAction; import com.tesora.dve.sql.statement.ddl.alter.RenameTableAction; import com.tesora.dve.sql.statement.dml.AliasInformation; import com.tesora.dve.sql.statement.dml.DMLStatement; import com.tesora.dve.sql.statement.dml.DeleteStatement; import com.tesora.dve.sql.statement.dml.InsertIntoSelectStatement; import com.tesora.dve.sql.statement.dml.InsertIntoValuesStatement; import com.tesora.dve.sql.statement.dml.MysqlSelectOption; import com.tesora.dve.sql.statement.dml.ProjectingStatement; import com.tesora.dve.sql.statement.dml.ReplaceIntoSelectStatement; import com.tesora.dve.sql.statement.dml.ReplaceIntoValuesStatement; import com.tesora.dve.sql.statement.dml.SelectStatement; import com.tesora.dve.sql.statement.dml.TruncateStatement; import com.tesora.dve.sql.statement.dml.UnionStatement; import com.tesora.dve.sql.statement.dml.UpdateStatement; import com.tesora.dve.sql.statement.dml.compound.CaseStatement; import com.tesora.dve.sql.statement.dml.compound.CompoundStatementList; import com.tesora.dve.sql.statement.dml.compound.StatementWhenClause; import com.tesora.dve.sql.statement.session.AnalyzeKeysStatement; import com.tesora.dve.sql.statement.session.AnalyzeTablesStatement; import com.tesora.dve.sql.statement.session.DeallocatePStmtStatement; import com.tesora.dve.sql.statement.session.ExecutePStmtStatement; import com.tesora.dve.sql.statement.session.ExternalServiceControlStatement; import com.tesora.dve.sql.statement.session.FlushPrivilegesStatement; import com.tesora.dve.sql.statement.session.KillStatement; import com.tesora.dve.sql.statement.session.LoadDataInfileStatement; import com.tesora.dve.sql.statement.session.LockStatement; import com.tesora.dve.sql.statement.session.LockType; import com.tesora.dve.sql.statement.session.PreparePStmtStatement; import com.tesora.dve.sql.statement.session.RollbackTransactionStatement; import com.tesora.dve.sql.statement.session.SavepointStatement; import com.tesora.dve.sql.statement.session.SessionSetVariableStatement; import com.tesora.dve.sql.statement.session.SessionStatement; import com.tesora.dve.sql.statement.session.SetExpression; import com.tesora.dve.sql.statement.session.SetTransactionIsolationExpression; import com.tesora.dve.sql.statement.session.SetVariableExpression; import com.tesora.dve.sql.statement.session.ShowErrorsWarningsStatement; import com.tesora.dve.sql.statement.session.ShowPassthroughStatement; import com.tesora.dve.sql.statement.session.ShowPassthroughStatement.PassThroughCommandType; import com.tesora.dve.sql.statement.session.ShowProcesslistStatement; import com.tesora.dve.sql.statement.session.ShowSitesStatusStatement; import com.tesora.dve.sql.statement.session.StartTransactionStatement; import com.tesora.dve.sql.statement.session.TableMaintenanceStatement; import com.tesora.dve.sql.statement.session.TableMaintenanceStatement.MaintenanceCommandType; import com.tesora.dve.sql.statement.session.TableMaintenanceStatement.MaintenanceOptionType; import com.tesora.dve.sql.statement.session.TransactionStatement; import com.tesora.dve.sql.statement.session.UseContainerStatement; import com.tesora.dve.sql.statement.session.XABeginTransactionStatement; import com.tesora.dve.sql.statement.session.XACommitTransactionStatement; import com.tesora.dve.sql.statement.session.XAEndTransactionStatement; import com.tesora.dve.sql.statement.session.XAPrepareTransactionStatement; import com.tesora.dve.sql.statement.session.XARecoverTransactionStatement; import com.tesora.dve.sql.statement.session.XARollbackTransactionStatement; import com.tesora.dve.sql.template.TemplateManager; import com.tesora.dve.sql.transform.CopyVisitor; import com.tesora.dve.sql.transform.behaviors.BehaviorConfiguration; import com.tesora.dve.sql.transform.execution.ExecutionSequence; import com.tesora.dve.sql.transform.execution.PassThroughCommand.Command; import com.tesora.dve.sql.transform.execution.TransientSessionExecutionStep; import com.tesora.dve.sql.transform.strategy.NaturalJoinRewriter; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.ListOfPairs; import com.tesora.dve.sql.util.ListSet; import com.tesora.dve.sql.util.Pair; import com.tesora.dve.sql.util.UnaryFunction; import com.tesora.dve.sql.util.UnaryProcedure; import com.tesora.dve.variable.VariableConstants; import com.tesora.dve.variables.KnownVariables; import com.tesora.dve.variables.VariableHandler; import com.tesora.dve.worker.SiteManagerCommand; import com.tesora.dve.worker.WorkerGroup; // holds the bridge methods from antlr tree nodes to our nodes public class TranslatorUtils extends Utils implements ValueSource { static Logger logger = Logger.getLogger(TranslatorUtils.class); static public final String PERSISTENT_GROUP_TAG = "PERSISTENT GROUP"; static public final String PERSISTENT_SITE_TAG = "PERSISTENT SITE"; static public final String PERSISTENT_INSTANCE = "PERSISTENT INSTANCE"; static public final String DYNAMIC_SITE_PROVIDER = "DYNAMIC SITE PROVIDER"; static public final String DYNAMIC_SITE_PROVIDER_SITES = DYNAMIC_SITE_PROVIDER + " SITES"; static public final String DYNAMIC_SITE_POLICY_TAG = "DYNAMIC SITE POLICY"; private final String MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG = "Command was not parsed correctly " + "(can occur if certain keywords are used as identifiers)"; private static final String UPDATABLE_VIEWS = "No support for updatable views"; SchemaContext pc; private ParserOptions opts; private boolean resolveColumnsAsIdentifiers; private ScopeStack scope; private ListOfPairs<DelegatingLiteralExpression,Object> literals; private List<Parameter> parameters; // as soon as we know the lock type, we register it private LockInfo lockInfo; // if there is a regular insert, this is the first part of it - everything except the values private InsertIntoValuesStatement insertSkeleton; // if this is a continuation, the initial offset private int initialOffset; // this is set if enough is bumped private int finalOffset; // for big inserts private final long continuationThreshold; private NativeCharSetCatalog supportedCharSets = null; private NativeCollationCatalog supportedCollations = null; private static final TableResolver basicResolver = new TableResolver().withMTChecks() .withQualifiedMissingDBFormat("No such database '%s'."); public static PEAbstractTable<?> getTable(final SchemaContext sc, final Name fullName, final LockInfo lockInfo) { TableInstance ti = basicResolver.lookupTable(sc, fullName, lockInfo); if (ti == null) return null; return ti.getAbstractTable(); } public static UnqualifiedName getDatabaseNameForObject(final SchemaContext sc, final Name objectName) { return getDatabaseForObject(sc, objectName).getName().getUnqualified(); } public static Database<?> getDatabaseForObject(final SchemaContext sc, final Name objectName) throws SchemaException { if (objectName.isQualified()) { final UnqualifiedName parentSchemaName = ((QualifiedName) objectName).getNamespace(); final Database<?> parentSchema = sc.findDatabase(parentSchemaName); if (parentSchema != null) { return parentSchema; } throw new SchemaException(new ErrorInfo(AvailableErrors.UNKNOWN_DATABASE, parentSchemaName.get())); } final Database<?> currentSchema = sc.getCurrentDatabase(); if (currentSchema != null) { return currentSchema; } throw new SchemaException(new ErrorInfo(AvailableErrors.NO_DATABASE_SELECTED)); } public TranslatorUtils(ParserOptions opts, SchemaContext pc, InputState state) { super(opts); this.pc = pc; if (this.pc == null) throw new SchemaException(Pass.FIRST, "TranslatorUtils no longer accepts null SchemaContext"); this.opts = opts; scope = new ScopeStack(); this.lockInfo = null; resolveColumnsAsIdentifiers = false; literals = new ListOfPairs<DelegatingLiteralExpression,Object>(); parameters = new ArrayList<Parameter>(); this.initialOffset = state.getCurrentPosition(); this.insertSkeleton = state.getInsertSkeleton(); finalOffset = -1; continuationThreshold = state.getThreshold(); if ((this.insertSkeleton != null) && (state instanceof ContinuationInputState)) { scope.pushScope(); scope.insertTable(this.insertSkeleton.getTableInstance()); } } public void setContext(SchemaContext sc) { pc = sc; } public String getInputSQL() { return pc.getOrigStmt(); } public void pushScope() { scope.pushScope(); } public void popScope() { scope.popScope(); } public void pushUnresolvingScope() { scope.pushUnresolvingScope(); } public void resolveProjection() { scope.resolveProjection(pc); } public void storeProjection(List<ExpressionNode> l) { scope.storeProjection(l); } public void setGroupByNamespace() { scope.setPhase(ScopeParsePhase.GROUPBY); } public void setHavingNamespace() { scope.setPhase(ScopeParsePhase.HAVING); } public void setTrailingNamespace() { scope.setPhase(ScopeParsePhase.TRAILING); } public int getLastPoppedScope() { return scope.getLastPoppedScopeID(); } public void repushScope(int id) { scope.pushScopeID(id); } public void pushUnresolving() { opts = opts.unsetResolve(); } public void popUnresolving() { opts = opts.setResolve(); } // call this during parsing to indicate that we are handling ddl public void ddl() { if (pc.getCapability() != Capability.PARSING_ONLY) pc.forceMutableSource(); if (opts == null || opts.getLockOverride() == null) lockInfo = new LockInfo(com.tesora.dve.lockmanager.LockType.EXCLUSIVE, "ddl"); else if (opts != null && opts.getLockOverride() != null) lockInfo = opts.getLockOverride(); } // call this during parsing to indicate that we are not handling ddl - used during temp table operations public void notddl() { if (pc.getCapability() != Capability.PARSING_ONLY) pc.forceImmutableSource(); if (opts == null || opts.getLockOverride() == null) lockInfo = new LockInfo(com.tesora.dve.lockmanager.LockType.EXCLUSIVE, "ddl"); else if (opts != null && opts.getLockOverride() != null) lockInfo = opts.getLockOverride(); } protected void forceUncacheable(ValueManager.CacheStatus status) { if (pc.getCapability() != Capability.PARSING_ONLY) pc.getValueManager().markUncacheable(status); } public void assignPositions() { if (pc.getCapability() == Capability.PARSING_ONLY) return; if (!parameters.isEmpty()) { TreeMap<SourceLocation, Parameter> map = new TreeMap<SourceLocation, Parameter>(); for(Parameter p : parameters) map.put(p.getSourceLocation(), p); if (map.size() != parameters.size()) throw new SchemaException(Pass.SECOND, "Lost parameters while doing position assignment"); int i = 0; for(Parameter p : map.values()) { p.setPosition(i); pc.getValueManager().registerParameter(pc, p); i++; } } if (literals.size() > KnownVariables.CACHED_PLAN_LITERALS_MAX.getValue(pc.getConnection().getVariableSource()).intValue()) { forceUncacheable(ValueManager.CacheStatus.NOCACHE_TOO_MANY_LITERALS); } else { TreeMap<SourceLocation, DelegatingLiteralExpression> map = new TreeMap<SourceLocation, DelegatingLiteralExpression>(); for(Pair<DelegatingLiteralExpression,Object> p : literals) { map.put(p.getFirst().getSourceLocation(), p.getFirst()); } if (map.size() != literals.size()) throw new SchemaException(Pass.SECOND, "Lost literals while doing position assignment"); int i = 0; for(DelegatingLiteralExpression dle : map.values()) { pc.getValueManager().addLiteralValue(pc,i, literals.get(dle.getPosition()).getSecond(), dle); dle.setPosition(i,true); i++; } } } @SuppressWarnings("unchecked") public ProjectingStatement buildSelectStatement(Map<String,Object> components, List<ExpressionNode> projection, List<Object> selectOptions, Object tree) { return buildSelectStatement((List<FromTableReference>)components.get(EdgeName.TABLES), projection, (ExpressionNode)components.get(EdgeName.WHERECLAUSE), (List<SortingSpecification>)components.get(EdgeName.ORDERBY), (LimitSpecification)components.get(EdgeName.LIMIT), selectOptions, (List<SortingSpecification>)components.get(EdgeName.GROUPBY), (ExpressionNode)components.get(EdgeName.HAVING), (Boolean)components.get(Statement.SELECT_LOCK_ATTRIBUTE), tree); } public ProjectingStatement buildSelectStatement(List<FromTableReference> tableRefs, List<ExpressionNode> projection, ExpressionNode whereClause, List<SortingSpecification> orderbys, LimitSpecification limit, List<Object> selectOptions, List<SortingSpecification> groupbys, ExpressionNode havingExpr, Boolean locking, Object tree) { // sort the set quantifier and select options SetQuantifier sq = null; List<MysqlSelectOption> options = new ArrayList<MysqlSelectOption>(); for(Object opt : selectOptions) { if (opt instanceof SetQuantifier) sq = (SetQuantifier) opt; else options.add((MysqlSelectOption) opt); } SelectStatement ss = new SelectStatement(tableRefs, projection, whereClause, orderbys, limit, sq, options, groupbys, havingExpr, locking, new AliasInformation(scope), SourceLocation.make(tree)); /* * The NATURAL [LEFT] JOIN are rewritten to an INNER JOIN or a LEFT JOIN * with a USING clause. * The rewrite must take place after ColumnInstance resolution (to * prevent premature failure on ambiguous column names), but before * USING-to-ON clause conversion and wildcard expansion which affect the * projection coalescing and ordering. */ if (tableRefs != null) { for (final FromTableReference ftr : tableRefs) { final TableInstance base = ftr.getBaseTable(); final ListSet<JoinedTable> naturalJoins = NaturalJoinRewriter.collectNaturalJoins(ftr.getTableJoins()); for (final JoinedTable join : naturalJoins) { NaturalJoinRewriter.rewriteToInnerJoin(this.pc, base, join); } convertUsingColSpecToOnSpec(base, naturalJoins); } } ss.getDerivedInfo().takeScope(scope); Scope ps = scope.getParentScope(); if (ps != null) ps.getNestedQueries().add(ss); shouldSetTimestampVariable(ss, null, null); return ss; } public Statement buildUpdateStatement(List<FromTableReference> tableRefs, List<ExpressionNode> updateExprs, ExpressionNode whereClause, List<SortingSpecification> orderbys, LimitSpecification limit, boolean ignore, Object tree) { PEAbstractTable<?> tab = tableRefs.get(0).getBaseTable().getAbstractTable(); if (tab != null && tab.isView()) throw new SchemaException(Pass.SECOND, UPDATABLE_VIEWS); UpdateStatement us = new UpdateStatement(tableRefs, updateExprs, whereClause, orderbys, limit, new AliasInformation(scope), SourceLocation.make(tree)); us.setIgnore(ignore); us.getDerivedInfo().takeScope(scope); if (tab != null) shouldSetTimestampVariable(us, tab.asTable(), updateExprs); return us; } public Statement buildDeleteStatement(List<FromTableReference> tableRefs, List<Name> explicitRefs, ExpressionNode whereClause, List<SortingSpecification> orderbys, LimitSpecification limit, Object sloc) { List<TableInstance> explicitDeletes = null; if (explicitRefs == null || explicitRefs.isEmpty()) { // ignore } else { explicitDeletes = new ArrayList<TableInstance>(); for(Name r : explicitRefs) { if (pc.getCapability() == Capability.PARSING_ONLY) { explicitDeletes.add(new TableInstance(null,r,null,false)); } else { Name actual = r; // if it has a trailing * strip that off List<UnqualifiedName> parts = r.getParts(); if (parts.size() > 1 && parts.get(parts.size() - 1).isAsterisk()) { ArrayList<UnqualifiedName> nparts = new ArrayList<UnqualifiedName>(parts); nparts.remove(nparts.size() - 1); if (nparts.size() == 1) actual = nparts.get(0); else actual = new QualifiedName(nparts); } explicitDeletes.add(scope.lookupTableInstance(pc, actual, true)); } } } DeleteStatement ds = new DeleteStatement(explicitDeletes, tableRefs, whereClause, orderbys, limit, false, new AliasInformation(scope), SourceLocation.make(sloc)); ds.getDerivedInfo().takeScope(scope); shouldSetTimestampVariable(ds, null, null); return ds; } @SuppressWarnings("unchecked") public void pushSkeletonInsert(ExpressionNode tab, boolean replace, Object tree) { TableInstance intoTable = (TableInstance) tab; InsertIntoValuesStatement is = null; SourceLocation sloc = SourceLocation.make(tree); if (replace) is = new ReplaceIntoValuesStatement(intoTable, Collections.EMPTY_LIST, Collections.EMPTY_LIST, new AliasInformation(scope), sloc); else is = new InsertIntoValuesStatement(intoTable, Collections.EMPTY_LIST, Collections.EMPTY_LIST, null, new AliasInformation(scope), sloc); is.getDerivedInfo().addLocalTable(intoTable.getTableKey()); is.getDerivedInfo().takeScope(scope); insertSkeleton = is; } public ExpressionNode pushInsertSkeletonField(ExpressionNode field) { insertSkeleton.getColumnSpecificationEdge().add(field); return field; } public InsertIntoValuesStatement buildInsertStatement(List<List<ExpressionNode>> values, boolean copy, TransactionStatement.Kind txnal, InsertModifier im, boolean ignore) { return buildInsertStatement(null,values, copy, txnal, im, ignore); } private InsertIntoValuesStatement buildInsertStatement(List<ExpressionNode> columnSpec, List<List<ExpressionNode>> values, boolean copy, TransactionStatement.Kind txnal, InsertModifier im, boolean ignore) { InsertIntoValuesStatement is = null; if (copy) is = CopyVisitor.copy(insertSkeleton); else is = insertSkeleton; if (columnSpec != null) is.setColumnSpecification(columnSpec); is.setValues(values); is.setTxnFlag(txnal); is.setIgnore(ignore); if (im != null) { if (opts.isResolve() && im.equals(InsertModifier.DELAYED)) { // replication slave always strips the DELAYED keyword if (!pc.getConnection().originatedFromReplicationSlave()) { EngineTableModifier tem = is.getTableInstance().getAbstractTable().asTable().getEngine(); if (tem.isMyISAM()) is.setModifier(im); else throw new SchemaException(Pass.SECOND, "Insert modifier '" + im.getSQL() + "' option not supported on table of type " + tem.getEngine().getSQL()); } } else { is.setModifier(im); } } shouldSetTimestampVariable(is, is.getTableInstance().getAbstractTable(), is.getColumnSpecification(), values); return is; } public Statement buildInsertStatement(List<List<ExpressionNode>> values, List<ExpressionNode> onDupKey, InsertModifier im, boolean ignore) { InsertIntoValuesStatement is = buildInsertStatement(values,false,(initialOffset > 0 ? TransactionStatement.Kind.COMMIT : null), im, ignore); is.setOnDuplicateKey(onDupKey); // clear the skeleton insertSkeleton = null; return is; } /** * @param select * @param ignore * @param onDupKey * @return */ @SuppressWarnings("unchecked") public Statement buildInsertIntoSelectStatement(ExpressionNode select, boolean ignore, List<ExpressionNode> onDupKey) { Subquery sq = (Subquery) select; ProjectingStatement selectStatement = sq.getStatement(); // copy the table, etc. out of the skeleton, then clear it InsertIntoSelectStatement iiss = null; if (insertSkeleton.isReplace()) { iiss = new ReplaceIntoSelectStatement(insertSkeleton.getTableInstance(),insertSkeleton.getColumnSpecification(), selectStatement, sq.isGrouped(), new AliasInformation(scope), insertSkeleton.getSourceLocation()); } else { iiss = new InsertIntoSelectStatement(insertSkeleton.getTableInstance(),insertSkeleton.getColumnSpecification(), selectStatement, sq.isGrouped(), onDupKey, new AliasInformation(scope), insertSkeleton.getSourceLocation()); iiss.setIgnore(ignore); } // the select is nested iiss.getDerivedInfo().addNestedStatements( Collections.singleton(selectStatement)); iiss.getDerivedInfo().addLocalTable( insertSkeleton.getTableInstance().getTableKey()); iiss.getDerivedInfo().takeScope(scope); shouldSetTimestampVariable(iiss, insertSkeleton.getTableInstance().getAbstractTable(), insertSkeleton.getColumnSpecification(), Collections.EMPTY_LIST); insertSkeleton = null; return iiss; } public void enough(PE_MySQL parser, List<List<ExpressionNode>> insertValues) { Lexer lexer = (Lexer) parser.getTokenStream().getTokenSource(); if (lexer.getCharIndex() - initialOffset > continuationThreshold) { finalOffset = lexer.getCharIndex(); throw new EnoughException(buildInsertStatement(insertValues,true, (initialOffset == 0 ? TransactionStatement.Kind.START : null),null,false)); } } public void reportContinuationOnDupKey() { throw new ParserException(Pass.FIRST, "Statement is too large. Consider increasing the '" + VariableConstants.LARGE_INSERT_THRESHOLD_NAME + "' value.", null); } public InputState getInputState(InputState in) { if (finalOffset == -1) return null; if (finalOffset > -1) { in.setCurrentPosition(finalOffset); } if (insertSkeleton != null && in.getInsertSkeleton() == null) in.setInsertSkeleton(insertSkeleton); return in; } /** * @param updateExprs * @param ignore * @param onDupKey * @param im * @return */ public Statement buildInsertIntoSetStatement(List<ExpressionNode> updateExprs, boolean ignore, List<ExpressionNode> onDupKey,InsertModifier im) { // use insert skeleton as is, since we cannot match the extended insert in the parser // but we have to unpack the update exprs to build the column spec and values List<ExpressionNode> columnSpec = new ArrayList<ExpressionNode>(); List<ExpressionNode> values = new ArrayList<ExpressionNode>(); for(ExpressionNode en : updateExprs) { FunctionCall fc = (FunctionCall) en; columnSpec.add(fc.getParametersEdge().get(0)); values.add(fc.getParametersEdge().get(1)); } return buildInsertStatement(columnSpec, Collections.singletonList(values), false, null, im, ignore); } private boolean shouldSetTimestampVariable(DMLStatement dmls, PETable tab, List<ExpressionNode> updateExprs) { // separate the update set column=value expression into columns and // values lists List<ExpressionNode> fields = new ArrayList<ExpressionNode>(); List<List<ExpressionNode>> allValues = new ArrayList<List<ExpressionNode>>(); List<ExpressionNode> rowValues = new ArrayList<ExpressionNode>(); if (updateExprs != null) { for (ExpressionNode node : updateExprs) { // update set column= are function calls FunctionCall f = (FunctionCall) node; Pair<ColumnInstance, ExpressionNode> params = decomposeUpdateAssignment(f); fields.add(params.getFirst()); rowValues.add(params.getSecond()); } } allValues.add(rowValues); return shouldSetTimestampVariable(dmls, tab, fields, allValues); } private boolean shouldSetTimestampVariable(DMLStatement dmls, PEAbstractTable<?> tab, List<ExpressionNode> fields, List<List<ExpressionNode>> values) { // check if the now or current_timestamp function is used boolean ret = TimestampVariableUtils.isNowFunctionCallSpecified(dmls .getDerivedInfo().getFunctions()); // can't do anything if petable is null if (tab == null) { // could have been set by above call so save in our statement // save the set timestamp variable flag in our statement dmls.getDerivedInfo().setSetTimestampVariable(ret); return ret; } if (!ret) { // determine which columns in the table are specified and which are // not List<PEColumn> specifiedColumns = new ArrayList<PEColumn>(); List<PEColumn> unspecifiedColumns = new ArrayList<PEColumn>( tab.getColumns(pc)); if (fields == null || fields.isEmpty()) { specifiedColumns.addAll(unspecifiedColumns); unspecifiedColumns.clear(); } else { List<PEColumn> temp = new ArrayList<PEColumn>(); for (ExpressionNode field : fields) { temp.add(((ColumnInstance) field).getPEColumn()); } unspecifiedColumns.removeAll(temp); for (ExpressionNode col : fields) { PEColumn c = ((ColumnInstance) col).getPEColumn(); specifiedColumns.add(c); } } for (PEColumn c : unspecifiedColumns) { ret = TimestampVariableUtils .setTimestampVariableForUnspecifiedColumn(pc,dmls, c); if (ret) { // set the variable so break loop break; } } // we need values to check against if (!ret && (values != null)) { // haven't set the timestamp variable yet // so check if the specified columns need to set it for (int i = 0; i < specifiedColumns.size(); ++i) { PEColumn c = specifiedColumns.get(i); for (List<ExpressionNode> v : values) { // make sure the ___mtid is skipped // by checking the specified column count doesn't // exceed the values if (i >= v.size()) { continue; } ExpressionNode e = v.get(i); ExpressionNode r = e; if (e instanceof Default) { ExpressionNode defaultValue = c.getDefaultValue(); if (defaultValue == null) { if (c.isNullable()) { r = LiteralExpression.makeNullLiteral(); } } else { r = (ExpressionNode) ((LiteralExpression) defaultValue).copy(null); } } ret = TimestampVariableUtils .setTimestampVariableForSpecifiedValue(c, r); if (ret) { break; } } if (ret) { break; } } } } // save the set timestamp variable flag in our statement dmls.getDerivedInfo().setSetTimestampVariable(ret); return ret; } private Pair<ColumnInstance, ExpressionNode> decomposeUpdateAssignment( FunctionCall fc) { ColumnInstance ci = null; ExpressionNode le = null; // for update assignment, we know exactly what we have - so just access // by index if (fc.getFunctionName().isEquals()) { List<ExpressionNode> params = fc.getParameters(); if (params.get(0) instanceof ColumnInstance) ci = (ColumnInstance) params.get(0); else return null; le = params.get(1); return new Pair<ColumnInstance, ExpressionNode>(ci, le); } return null; } public Statement buildCreateTable(Name tableName, Name oldTableName, Boolean ine) { if (( tableName == null ) || ( oldTableName == null )) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } PECreateTableStatement pecs = null; UnqualifiedName tabName = tableName.getUnqualified(); TableInstance ti = pc.getCurrentPEDatabase().getSchema().buildInstance(pc, tabName, lockInfo); if (Boolean.TRUE.equals(ine) && opts.isResolve()) { // see if the table already exists // UnqualifiedName dbName = null; if (ti != null) { if (ti.getAbstractTable().isView()) throw new SchemaException(Pass.FIRST, tabName + " is a view, cannot create like"); pecs = new PECreateTableStatement(ti.getAbstractTable().asTable(), ine, true); return pecs; } } PEAbstractTable<?> tab = null; if (ti != null) tab = ti.getAbstractTable(); // see if the table already exists if (tab == null) { Name dbName = null; // now need to determine if source or old table exists too if (oldTableName.isQualified()) { dbName = ((QualifiedName)oldTableName).getNamespace(); } if (dbName == null) { dbName = (pc.getCurrentDatabase() == null) ? null : pc.getCurrentDatabase().getName(); } PEDatabase db = pc.findPEDatabase(dbName); if (db == null) throw new SchemaException(Pass.FIRST, "No such database: '" + dbName + "'"); TableInstance otab = db.getSchema().buildInstance(pc, oldTableName.getUnqualified(), lockInfo); if (otab == null) { throw new SchemaException(Pass.FIRST, "No source table: '" + oldTableName + "'"); } if (otab.getAbstractTable().isView()) throw new SchemaException(Pass.FIRST, "Source table is a view"); PETable oldTab = otab.getAbstractTable().asTable(); String cts = oldTab.getDeclaration(); if (cts == null) throw new SchemaException(Pass.FIRST, "Unable to obtain source create table statement"); tab = oldTab.recreate(pc, cts, lockInfo); tab.setName(tabName); tab.asTable().removeForeignKeys(pc); tab.asTable().setDeclaration(pc, tab.asTable()); } pecs = new PECreateTableStatement(tab.asTable(), ine, false); return pecs; } public Statement buildCreateTable(Name tableName, List<TableComponent<?>> fieldsAndKeys, UnresolvedDistributionVector indv, Name groupName, List<TableModifier> modifiers, Boolean ine, Pair<UnqualifiedName,List<UnqualifiedName>> discriminator, ProjectingStatement ctas, boolean temporary) { if ( tableName == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } PECreateTableStatement pecs = null; if (Boolean.TRUE.equals(ine) && opts.isResolve()) { // see if the table already exists // UnqualifiedName dbName = null; TableInstance ti = new TableResolver().withMTChecks().lookupTable(pc, tableName, lockInfo); if (ti != null) { if (ti.getAbstractTable().isView()) throw new SchemaException(Pass.FIRST,tableName + " is a view (cannot recreate)"); pecs = new PECreateTableStatement(ti.getAbstractTable().asTable(), ine, true); } } PEPersistentGroup pesg = null; if (groupName != null) { pesg = pc.findStorageGroup(groupName); if (pesg == null) throw new SchemaException(Pass.SECOND, "No such persistent group: " + groupName.getSQL()); } List<TableComponent<?>> actualFieldsAndKeys = null; ListOfPairs<PEColumn,Integer> ctaProjectionOffsets = null; if (ctas != null) { ctaProjectionOffsets = new ListOfPairs<PEColumn,Integer>(); // the columns are ordered like so: // first all columns that are only explicitly declared // then all columns that are only implicitly or both declared // we will determine projection column types at runtime, and use a placeholder in the meantime ListSet<PEColumn> inproj = new ListSet<PEColumn>(); ProjectionInfo pmd = ctas.getProjectionMetadata(pc); for(int i = 1; i <= pmd.getWidth(); i++) { int offset = i - 1; ColumnInfo ci = pmd.getColumnInfo(i); UnqualifiedName cname = new UnqualifiedName(ci.getAlias()); PEColumn matching = lookupInProcessColumn(cname, true); if (matching != null) { ctaProjectionOffsets.add(matching,offset); inproj.add(matching); continue; } // declare the column with a placeholder type PEColumn viaCTA = PECreateTableAsSelectStatement.createColumnFromExpression(pc,ci,ctas.getProjections().get(0).get(offset)); scope.registerColumn(viaCTA); ctaProjectionOffsets.add(viaCTA, offset); inproj.add(viaCTA); } actualFieldsAndKeys = new ArrayList<TableComponent<?>>(); // first do the decl only bits for(Iterator<TableComponent<?>> iter = fieldsAndKeys.iterator(); iter.hasNext();) { TableComponent<?> tc = iter.next(); if (tc instanceof PEKey) continue; PEColumn pec = (PEColumn) tc; if (!inproj.contains(pec)) actualFieldsAndKeys.add(pec); iter.remove(); } // now all the projection fields actualFieldsAndKeys.addAll(inproj); // now we have all columns declared, resolve anything that was forward for(Iterator<TableComponent<?>> iter = fieldsAndKeys.iterator(); iter.hasNext();) { TableComponent<?> tc = iter.next(); if (tc instanceof PEKey) { PEKey pek = (PEKey) tc; actualFieldsAndKeys.add(pek.resolve(pc,scope)); } else { actualFieldsAndKeys.add(tc); } } } else { actualFieldsAndKeys = fieldsAndKeys; } // resolve the distribution vector DistributionVector dv = null; if (indv != null) dv = indv.resolve(pc, this); if (pecs == null) { // unpack the dbstuff PETable newTab = null; // we want to inject when both the dist vect and the discriminator are null; if either is non-null the dist info // was specified if (dv == null && discriminator == null) { newTab = buildTable(tableName, actualFieldsAndKeys, null, pesg, modifiers, ctaProjectionOffsets != null, temporary); if ((pc.getCapability() == Capability.PARSING_ONLY) || (opts != null && opts.isOmitMetadataInjection())) { dv = new DistributionVector(pc, null, DistributionVector.Model.RANDOM); newTab.setDistributionVector(pc,dv); } else try { if (!TemplateManager.inject(pc, newTab.getPEDatabase(pc), newTab)) { if (newTab.getPEDatabase(pc).hasStrictTemplateMode()) { throw new SchemaException(Pass.SECOND,"No matching template found for table " + newTab.getName().getSQL()); } dv = new DistributionVector(pc, null, DistributionVector.Model.RANDOM); newTab.setDistributionVector(pc,dv); } } catch (Exception e) { throw new SchemaException(Pass.SECOND, e); } } else if (dv != null) { newTab = buildTable(tableName, actualFieldsAndKeys, dv, pesg, modifiers, ctaProjectionOffsets != null, temporary); } else if (discriminator != null) { // newTab will be the base table on the container - so change the dist vect on it to be the container PEContainer container = pc.findContainer(discriminator.getFirst()); if (container == null) throw new SchemaException(Pass.SECOND, "No such container: " + discriminator.getFirst().getSQL()); if (container.hasBaseTable()) { throw new SchemaException(Pass.SECOND, "Cannot set table '" + tableName + "' as a base table because container '" + discriminator.getFirst().getSQL() + "' already has a base table."); } dv = new ContainerDistributionVector(pc,container,false); newTab = buildTable(tableName, fieldsAndKeys, dv, pesg, modifiers, ctaProjectionOffsets != null, temporary); // newTab is actually the container base table - so go resolve the columns now and so mark them List<UnqualifiedName> colNames = discriminator.getSecond(); for(int i = 0; i < colNames.size(); i++) { UnqualifiedName un = colNames.get(i); PEColumn pec = newTab.lookup(pc, un); if (pec == null) throw new SchemaException(Pass.SECOND, "No such column: " + un.getSQL() + " - cannot build discriminator"); pec.setContainerDistributionValuePosition(i + 1); } container.setBaseTable(pc, newTab); } else { throw new SchemaException(Pass.SECOND, "Unable to determine declared distribution for table " + tableName.getSQL()); } if (ctas == null) pecs = new PECreateTableStatement(newTab, ine, false); else pecs = new PECreateTableAsSelectStatement(newTab, ine, false, ctas, ctaProjectionOffsets); } Statement out = pecs; if (pc.getCapability() != Capability.PARSING_ONLY && !pc.getOptions().isTSchema()) { // containers don't generally have a separate policy at creation time - check for it on // the dist vect if (pecs.getCreated().get().getDistributionVector(pc).isContainer()) { out = ContainerPolicyContext.modifyCreateTable(pc, pecs); } else { out = pc.getPolicyContext().modifyCreateTable(pecs); } } return out; } public Statement buildDropTableStatement(List<Name> givenNames, Boolean ifExists, boolean tempTabs) { if ( givenNames == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } TableResolver resolver = new TableResolver().withMTChecks().withDatabaseFunction(new UnaryProcedure<Database<?>>() { @Override public void execute(Database<?> object) { if (!(object instanceof PEDatabase)) throw new SchemaException(Pass.SECOND, "Invalid database for drop table: '" + object.getName() + "'"); } }); List<TableKey> tblKeys = new ArrayList<TableKey>(); List<Name> unknownTables = new ArrayList<Name>(); for(Name givenName : givenNames) { TableInstance ti = resolver.lookupTable(pc, givenName, lockInfo); if (ti == null) { unknownTables.add(givenName); } else { if (tempTabs && !ti.getTableKey().isUserlandTemporaryTable()) throw new SchemaException(new ErrorInfo(AvailableErrors.UNKNOWN_TABLE,givenName.getUnquotedName().get())); tblKeys.add(ti.getTableKey()); } } // we can throw if there is no valid existing table if ((tblKeys.size() == 0) && unknownTables.size() > 0) { if (!Boolean.TRUE.equals(ifExists)) throw new SchemaException(Pass.SECOND, "No such table(s) '" + StringUtils.join(unknownTables, ",") + "'"); } PEDropTableStatement stmt = new PEDropTableStatement(pc,tblKeys, unknownTables, ifExists, tempTabs); return pc.getPolicyContext().modifyDropTable(stmt); } private Database<?> findDatabase(Name givenName) { Database<?> ondb = pc.getCurrentDatabase(false); if (ondb == null || givenName.isQualified()) { if (!givenName.isQualified()) pc.getCurrentDatabase(true); QualifiedName qname = (QualifiedName) givenName; UnqualifiedName dbName = qname.getNamespace(); if (ondb == null || !ondb.getName().equals(dbName)) { ondb = pc.findDatabase(dbName); if (ondb == null) return ondb; } } return ondb; } public Statement buildDropDatabaseStatement(Name dbName, Boolean ifExists, boolean dropmt, String tag) { if ( dbName == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } return pc.getPolicyContext().buildDropDatabaseStatement(dbName, ifExists, dropmt, tag); } public Statement buildShowCreateDatabaseQuery(String onInfoSchemaTable, Name objectName, Boolean ifNotExists) { ShowSchemaBehavior ist = Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName(onInfoSchemaTable)); if (ist == null) throw new MigrationException("Need to add info schema table for " + onInfoSchemaTable); ShowOptions opts = new ShowOptions(); if (Boolean.TRUE.equals(ifNotExists)) opts = opts.withIfNotExists(); return ist.buildUniqueStatement(pc, objectName, opts); } public Statement buildUseDatabaseStatement(Name firstName) { if (firstName == null) throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); if (pc.getCapability() == Capability.PARSING_ONLY) return new SessionStatement("use " + firstName.getSQL()) { @Override public boolean isPassthrough() { return false; } }; return pc.getPolicyContext().buildUseDatabaseStatement(firstName); } /** * @param projName * @return */ public Statement buildUseProjectStatement(Name projName) { // no longer supported (just for the persistent version) throw new ParserException(Pass.FIRST, "No support for use project for persistent schema"); } /** * @param jdbcURL * @return */ public Statement buildCreateCatalog(Token jdbcURL) { throw new SchemaException(Pass.SECOND, "No support for create catalog."); } @SuppressWarnings("unchecked") public Statement buildCreateDatabase(Name dbName, Boolean ifNotExists, String tag, MultitenantMode mm, List<Pair<?,?>> consolidatedDefs) { if ( dbName == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } Name charSetValue = null; Name collationValue = null; Name pgName = null; FKMode fkMode = null; Pair<Name, TemplateMode> templateDecl = null; for(Pair<?,?> p : consolidatedDefs) { if (p.getFirst() instanceof String) { String key = (String) p.getFirst(); if ("fkmode".equals(key)) { fkMode = (FKMode) p.getSecond(); continue; } Name value = (Name)p.getSecond(); if ("charset".equals(key)) { charSetValue = value; } else if ("collate".equals(key)) { collationValue = value; } else if ("pers_group".equals(key)) { pgName = value; } else { throw new SchemaException(Pass.FIRST,"Unknown create db attribute: " + key); } } else if (p.getSecond() instanceof TemplateMode) { templateDecl = (Pair<Name, TemplateMode>) p; } } if (templateDecl == null) { templateDecl = new Pair<Name, TemplateMode>(null, TemplateMode.getCurrentDefault(pc.getConnection())); } final Pair<String, String> charSetCollationPair = getCharsetCollationPair(charSetValue, collationValue); if (pc.getCapability() == Capability.PARSING_ONLY) { PEDatabase pdb = new PEDatabase(null, dbName.getUnquotedName(), null, templateDecl, mm, fkMode, charSetCollationPair.getFirst(), charSetCollationPair.getSecond()); PECreateStatement<PEDatabase, UserDatabase> cdb = new PECreateDatabaseStatement( pdb, false, ifNotExists, tag, false); return cdb; } return pc.getPolicyContext().buildCreateDatabase(dbName, pgName, templateDecl, ifNotExists, tag, mm, fkMode, charSetCollationPair.getFirst(), charSetCollationPair.getSecond()); } public Statement buildAlterDatabaseStatement(final Name dbName, final Name charSetName, final Name collationName) { if ((charSetName == null) && (collationName == null)) { throw new SchemaException(Pass.SECOND, "Can't alter database '" + dbName.getSQL() + "'; syntax error"); } final Pair<String, String> charSetCollationPair = getCharsetCollationPair(charSetName, collationName); final PEDatabase db = getAlterDatabase(dbName); return new AlterDatabaseStatement(db, charSetCollationPair.getFirst(), charSetCollationPair.getSecond()); } public Statement buildAlterDatabaseStatement(final Name dbName, final Pair<Name, TemplateMode> templateDeclaration) { final PEDatabase db = getAlterDatabase(dbName); final Pair<Name, TemplateMode> checkedTemplateDecl = TemplateManager.findTemplateForDatabase(pc, dbName, templateDeclaration.getFirst(), templateDeclaration.getSecond()); return new AlterDatabaseTemplateStatement(db, checkedTemplateDecl); } public Pair<Name, TemplateMode> buildTemplateDeclaration(final Name templateName, TemplateMode mode) { final String templateNameIdentifier = templateName.get(); if (TemplateMode.hasModeForName(templateNameIdentifier)) { if (templateNameIdentifier.equals(TemplateMode.OPTIONAL.toString())) { if (mode == null) { return new Pair<Name, TemplateMode>(null, TemplateMode.OPTIONAL); } } throw new SchemaException(Pass.SECOND, "Redundant mode specification '" + mode.toString() + "'; syntax error"); } if ((mode != null) && !mode.requiresTemplate()) { throw new SchemaException(Pass.SECOND, "Redundant template specification '" + templateName.getSQL() + "' for " + VariableConstants.TEMPLATE_MODE_NAME + " '" + mode.toString() + "'; syntax error"); } if (mode == null) { mode = TemplateMode.getCurrentDefault(pc.getConnection()); } return new Pair<Name, TemplateMode>(templateName, mode); } private PEDatabase getAlterDatabase(final Name dbName) { final Database<?> db = (dbName != null) ? pc.findDatabase(dbName) : pc.getCurrentDatabase(); if (db == null) { throw new SchemaException(Pass.SECOND, "Can't alter database '" + dbName.getSQL() + "'; database doesn't exist"); } else if (!(db instanceof PEDatabase)) { throw new SchemaException(Pass.SECOND, "Can't alter database '" + dbName.getSQL() + "'; target is not alterable"); } return (PEDatabase) db; } private Pair<String, String> getCharsetCollationPair(final Name charSetName, final Name collationName) { return CharsetCollationModifierBuilder.buildCharsetCollationNamePair(charSetName, collationName, getNativeCharSetCatalog(), getNativeCollationCatalog()); } public Statement buildCreatePersistentInstance(Name persistentInstanceName, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("create a persistent instance"); if (persistentInstanceName == null) throw new SchemaException(Pass.SECOND, "Persistent instance name must be specified."); PESiteInstance pesi = pc.findSiteInstance(persistentInstanceName); if (pesi != null) throw new SchemaException(Pass.SECOND, "Persistent instance " + persistentInstanceName + " already exists."); return PECreateSiteInstanceStatement.build(pc,persistentInstanceName,options); } public Statement buildAlterPersistentInstanceStatement(Name persistentInstanceName, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("change a persistent instance"); PESiteInstance pesi = pc.findSiteInstance(persistentInstanceName); if (pesi == null) throw new SchemaException(Pass.SECOND, "No such persistent instance: " + persistentInstanceName.getUnqualified().get()); return new PEAlterSiteInstanceStatement(pesi, options); } @SuppressWarnings({ "unchecked", "rawtypes" }) public Statement buildDropPersistentInstanceStatement(Name persistentInstanceName) { pc.getPolicyContext().checkRootPermission("drop a persistent instance"); PESiteInstance pesi = pc.findSiteInstance(persistentInstanceName); if (pesi == null) throw new SchemaException(Pass.SECOND, "No such persistent instance: " + persistentInstanceName.getUnqualified().get()); return new PEDropStatement(PESiteInstance.class, null, true, pesi, PERSISTENT_INSTANCE); } public Statement buildCreatePersistentSite(Name persistentSiteName, List<Pair<Name,LiteralExpression>> opts) { pc.getPolicyContext().checkRootPermission("create a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess != null) throw new SchemaException(Pass.SECOND, "Persistent site " + persistentSiteName.getSQL() + " already exists."); return PECreateStorageSiteStatement.build(pc, persistentSiteName,opts); } public Statement buildCreatePersistentSite(Name persistentSiteName, String url, String user, String password) { pc.getPolicyContext().checkRootPermission("create a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess != null) throw new SchemaException(Pass.SECOND, "Persistent site " + persistentSiteName.getSQL() + " already exists."); // the jdbcURL probably still has the quotes on - have to strip those String stripped = Singletons.require(DBNative.class).getValueConverter().convertStringLiteral(url); // convert it into the other format List<Pair<Name,LiteralExpression>> opts = new ArrayList<Pair<Name,LiteralExpression>>(); opts.add(buildConfigOption(new UnqualifiedName("URL"),LiteralExpression.makeStringLiteral(stripped))); opts.add(buildConfigOption(new UnqualifiedName("USER"),LiteralExpression.makeStringLiteral(user))); opts.add(buildConfigOption(new UnqualifiedName("PASSWORD"), LiteralExpression.makeStringLiteral(password))); return PECreateStorageSiteStatement.build(pc,persistentSiteName,opts); } @SuppressWarnings("unchecked") public Statement buildCreatePersistentSite(Name persistentSiteName, String haType, Name masterName, List<Name> siteInstances) { pc.getPolicyContext().checkRootPermission("create a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess != null) throw new SchemaException(Pass.SECOND, "Persistent site " + persistentSiteName.getSQL() + " already exists."); PESiteInstance masterPersistentInstance = pc.findSiteInstance(masterName); if (masterPersistentInstance == null) { throw new SchemaException(Pass.SECOND, "No such persistent instance: " + masterName.getQuoted()); } List<PESiteInstance> peSiteInstances = null; if (siteInstances == null) peSiteInstances = Collections.EMPTY_LIST; else peSiteInstances = Functional.apply(siteInstances, new UnaryFunction<PESiteInstance, Name>() { @Override public PESiteInstance evaluate(Name object) { PESiteInstance pesi = pc.findSiteInstance(object); if (pesi == null) throw new SchemaException(Pass.SECOND, "No such persistent instance: " + object.getQuoted()); return pesi; } }); return new PECreateStorageSiteStatement(new PEStorageSite(pc, persistentSiteName, haType, masterPersistentInstance, peSiteInstances)); } public Statement buildAlterPersistentSite(Name persistentSiteName, String haType) { pc.getPolicyContext().checkRootPermission("alter a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + persistentSiteName.getSQL()); return new PEAlterPersistentSite(pess, haType); } public Statement buildAlterPersistentSite(Name persistentSiteName, Name masterName) { pc.getPolicyContext().checkRootPermission("alter a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + persistentSiteName.getSQL()); PESiteInstance masterSiteInstance = pc.findSiteInstance(masterName); if (masterSiteInstance == null) throw new SchemaException(Pass.SECOND, "No such persistent instance: " + masterName.getUnqualified().get()); return new PEAlterPersistentSite(pess, masterSiteInstance); } public Statement buildAlterPersistentSite(Name persistentSiteName, Boolean addOperation, List<Name> siteInstanceNames) { pc.getPolicyContext().checkRootPermission("alter a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + persistentSiteName.getSQL()); ArrayList<PESiteInstance> siteInstances = new ArrayList<PESiteInstance>(); for (Name n : siteInstanceNames) { PESiteInstance pesi = pc.findSiteInstance(n); if (pesi == null) throw new SchemaException(Pass.SECOND, "No such persistent instance: " + n.getUnqualified().get()); siteInstances.add(pesi); } return new PEAlterPersistentSite(pess, addOperation, siteInstances); } public Statement buildDropPersistentSiteStatement(Name persistentSiteName) { pc.getPolicyContext().checkRootPermission("drop a persistent site"); PEStorageSite pess = pc.findStorageSite(persistentSiteName); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + persistentSiteName.getSQL()); PEDropStorageSiteStatement out = new PEDropStorageSiteStatement(pess); out.ensureUnreferenced(pc); return out; } @SuppressWarnings("unchecked") public Statement buildCreatePersistentGroup(Name groupName, List<Name> sites) { pc.getPolicyContext().checkRootPermission("create a persistent group"); PEPersistentGroup pesg = pc.findStorageGroup(groupName); if (pesg != null) throw new SchemaException(Pass.SECOND, "Persistent group " + groupName.getSQL() + " already exists"); List<PEStorageSite> pesites = null; if (sites == null) pesites = Collections.EMPTY_LIST; else pesites = Functional.apply(sites, new UnaryFunction<PEStorageSite, Name>() { @Override public PEStorageSite evaluate(Name object) { PEStorageSite pess = pc.findStorageSite(object); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + object.getQuoted()); return pess; } }); return new PECreateStatement<PEPersistentGroup, PersistentGroup>(new PEPersistentGroup(pc, groupName, pesites), true, PERSISTENT_GROUP_TAG, false); } public PEColumn lookupInProcessColumn(Name n, boolean missingOk) { PEColumn c = scope.lookupInProcessColumn(n); if (c == null) c = scope.lookupInProcessColumn(n.getCapitalized()); if (c == null && !missingOk) throw new SchemaException(Pass.SECOND, "No such column: " + n.getSQL()); return c; } public UnresolvedDistributionVector buildDistributionVector( DistributionVector.Model model, List<Name> columnNames, Name rangeOrContainer) { return new UnresolvedDistributionVector(model,columnNames,rangeOrContainer); } public ColumnModifier buildColumnModifier(ColumnModifierKind tag) { return new ColumnModifier(tag); } public ColumnModifier buildDefaultValue(ExpressionNode v) { return new DefaultValueModifier(v); } public ColumnModifier buildEnumDefaultValue(Type typeDef, ExpressionNode value) { final DBEnumType enumValues = (DBEnumType) typeDef; final ActualLiteralExpression defaultValue = (ActualLiteralExpression) value; /* * Integral default values are treated as 1-based indices into the ENUM. */ if (defaultValue.isIntegerLiteral()) { final int valuePositionIndex = ((Long) defaultValue.getValue()).intValue(); try { final LiteralExpression valueAtIndex = enumValues.getValueAt(valuePositionIndex); return new DefaultValueModifier(valueAtIndex); } catch (final IndexOutOfBoundsException e) { throw new SchemaException(Pass.SECOND, "No value at position " + valuePositionIndex + " in the " + enumValues.getEnumerationTypeName().toUpperCase(), e); } } return new DefaultValueModifier(value); } public TypeModifier buildCollationSpec(Name collated) { return new StringTypeModifier(TypeModifierKind.COLLATE,collated.getUnquotedName().get()); } public TypeModifier buildCharsetSpec(Name spec) { return new StringTypeModifier(TypeModifierKind.CHARSET,spec.getUnquotedName().get()); } public Set<TableModifier> buildCharsetCollationModifiers(final CharsetCollationModifierBuilder builder) { if (builder.hasValues()) { return builder.buildModifiers(getNativeCharSetCatalog(), getNativeCollationCatalog()); } return Collections.EMPTY_SET; } public ColumnModifier buildOnUpdate() { return new ColumnModifier(ColumnModifierKind.ONUPDATE); // buildIdentifierLiteral("CURRENT_TIMESTAMP")); } public Name buildKeywordName(String in) { return new UnqualifiedName(in); } public Name buildIdentifier(String in, Object tree) { Name out = new UnqualifiedName(in, SourceLocation.make(tree)); return out; } public Name buildIdentifier(Token tok) { Name out = new UnqualifiedName(tok.getText(), SourceLocation.make(tok)); return out; } public Object buildLiteralValue(String in) { return in; } public Integer buildIntegerLiteral(String in) { try { return Integer.parseInt(in); } catch (NumberFormatException nfe) { throw new ParserException(Pass.SECOND, "Bad integer value: '" + in + "': " + nfe.getMessage(), nfe); } } private LiteralExpression asLiteral(ExpressionNode e) { if (e instanceof LiteralExpression) { return (LiteralExpression) e; } else if (e == null) { return null; } else { throw new ParserException(Pass.SECOND, "Expecting literal, got: " + e); } } private Integer asIntegralLiteral(ExpressionNode e) { LiteralExpression integLit = asLiteral(e); Object lv = null; if (integLit instanceof DelegatingLiteralExpression) { DelegatingLiteralExpression dle = (DelegatingLiteralExpression) integLit; lv = literals.get(dle.getPosition()).getSecond(); } else { lv = integLit.getValue(pc.getValues()); } if (lv instanceof Long) { return ((Long) lv).intValue(); } throw new ParserException(Pass.SECOND, "Expecting integral literal, got: " + lv); } public SizeTypeAttribute buildSizeTypeAttribute(ExpressionNode a, ExpressionNode b) { // i.e. (x, y) if (b == null) return new SizeTypeAttribute(asIntegralLiteral(a)); Integer precision = asIntegralLiteral(a); Integer scale = asIntegralLiteral(b); return new FloatSizeTypeAttribute(precision, precision, scale); } @SuppressWarnings("unchecked") public BasicType buildType(List<Name> typeNames, SizeTypeAttribute sizing, List<TypeModifier> modifiers) { return BasicType.buildType(typeNames, Collections.singletonList(sizing), (modifiers == null ? Collections.EMPTY_LIST : modifiers), pc.getTypes()); } public BasicType buildEnum(boolean isSet, List<LiteralExpression> values, List<TypeModifier> modifiers) { return DBEnumType.make(isSet, values, modifiers,pc.getTypes()); } public TypeModifier buildTypeModifier(TypeModifierKind tmk) { return new TypeModifier(tmk); } public TypeModifier buildComparisonModifier(String className) { String stripped = Singletons.require(DBNative.class).getValueConverter() .convertStringLiteral(className); return new StringTypeModifier(TypeModifierKind.COMPARISON, stripped); } public List<TableComponent<?>> buildFieldDefinition(Name fieldName, Type type, List<ColumnModifier> attrs, String commentText) throws SchemaException { Comment comment = null; if (commentText != null) { comment = buildTableFieldComment(fieldName, commentText); } List<ColumnKeyModifier> inlineKeys = new ArrayList<ColumnKeyModifier>(); for(Iterator<ColumnModifier> iter = attrs.iterator(); iter.hasNext();) { ColumnModifier cm = iter.next(); if (cm.getTag() == ColumnModifierKind.INLINE_KEY) { inlineKeys.add((ColumnKeyModifier)cm); iter.remove(); } } List<TableComponent<?>> out = new ArrayList<TableComponent<?>>(); PEColumn nc = null; if (pc.getCapability() == Capability.PARSING_ONLY || !(pc.getPolicyContext().allowTenantColumnDeclaration() && TenantColumn.TENANT_COLUMN .equals(fieldName.get()))) nc = scope.registerColumn( PEColumn.buildColumn(pc, fieldName, type, attrs, comment, inlineKeys)); else nc = scope.registerColumn(new TenantColumn(pc)); out.add(nc); // collapse the case where we see UNIQUE, KEY if (inlineKeys.size() > 1) { int uniqued = -1; int keyed = -1; for(int i = 0; i < inlineKeys.size(); i++) { if (inlineKeys.get(i).getConstraint() == ConstraintType.UNIQUE && uniqued == -1) uniqued = i; else if (inlineKeys.get(i).getConstraint() == null && keyed == -1) keyed = i; } if (uniqued > -1 && keyed > uniqued) inlineKeys.remove(keyed); } for(ColumnKeyModifier ckm : inlineKeys) { // first build the key @SuppressWarnings("unchecked") PEKey pek = buildKey(null,null,Collections.singletonList((PEKeyColumnBase)new PEKeyColumn(nc,null,-1L)),Collections.EMPTY_LIST); if (ckm.getConstraint() != null) pek = withConstraint(ckm.getConstraint(), null, pek); out.add(pek); } return out; } public PEKey buildKey(IndexType type, Name name, List<PEKeyColumnBase> cols, List<Object> options) throws SchemaException { // unpack the options in case we have anything lurking IndexType postSpecifiedType = null; Comment anyComment = null; for(Object o : options) { if (o instanceof String) { anyComment = buildTableFieldComment(name, (String) o); } else if (o instanceof IndexType) { postSpecifiedType = (IndexType) o; } else { throw new SchemaException(Pass.SECOND, "Unknown key option: " + o); } } IndexType actualType = type; if (actualType == null) actualType = postSpecifiedType; if (actualType == null) actualType = IndexType.BTREE; return new PEKey(name,actualType,cols, anyComment); } public PEKey withConstraint(ConstraintType ct, Name symbolName, PEKey pek) { pek.setConstraint(ct); if (symbolName != null) pek.setSymbol(symbolName.getUnqualified()); return pek; } public ColumnModifier buildInlineKeyModifier(ConstraintType ct) { return new ColumnKeyModifier(ct); } public PEKeyColumnBase buildPEKeyColumn(Name identifier, ExpressionNode length, ExpressionNode cardinality) { PEColumn c = lookupInProcessColumn(identifier, true); Integer keyLength = (length == null ? null : asIntegralLiteral(length)); long keyCardinality = (cardinality == null ? -1L : asIntegralLiteral(cardinality)); if (c == null) return new PEForwardKeyColumn(null,identifier.getUnqualified(), keyLength, keyCardinality); else return new PEKeyColumn(c,keyLength,keyCardinality); } @SuppressWarnings("cast") public PEKey buildForeignKey(Name name, List<PEKeyColumnBase> mycols, Name targetTableName, List<UnqualifiedName> targetColumns, ForeignKeyAction deleteAction, ForeignKeyAction updateAction) { // are unknown tables ok? boolean required = (pc.getCapability() != Capability.PARSING_ONLY && KnownVariables.FOREIGN_KEY_CHECKS.getSessionValue(pc.getConnection().getVariableSource()).booleanValue()); // figure out whether the target table is known or not PETable targetTab = null; Database<?> db = null; UnqualifiedName candidateName = null; if (targetTableName.isQualified()) { QualifiedName qn = (QualifiedName) targetTableName; UnqualifiedName ofdb = qn.getNamespace(); candidateName = qn.getUnqualified(); db = pc.findPEDatabase(ofdb); if (db == null && required) throw new SchemaException(Pass.FIRST, "No such database: " + ofdb); } else { if (pc.getCapability() != Capability.PARSING_ONLY) db = pc.getCurrentDatabase(); if (db == null && required) throw new SchemaException(Pass.FIRST, "No current database"); candidateName = (UnqualifiedName) targetTableName; } if (db != null) { boolean mtchecks = (pc.getCapability() != Capability.PARSING_ONLY && !pc.getOptions().isDisableMTLookupChecks()); TableInstance ti = db.getSchema().buildInstance(pc, candidateName, lockInfo, mtchecks); if (ti == null && required && mtchecks) throw new SchemaException(Pass.FIRST, "No such table: " + targetTableName); if (ti != null) targetTab = ti.getAbstractTable().asTable(); } Name fullyQualifiedTargetName = null; if (targetTab == null) { if (targetTableName.isQualified()) fullyQualifiedTargetName = targetTableName; else if (db != null) fullyQualifiedTargetName = targetTableName.postfix(db.getName()); else if (pc.getCapability() == Capability.PARSING_ONLY) fullyQualifiedTargetName = targetTableName; else throw new SchemaException(Pass.FIRST, "No current database"); } // regardless of whether the target table is known, we need to convert the PEKeyColumns to PEForeignKeyColumns if (mycols.size() != targetColumns.size()) throw new SchemaException(Pass.FIRST,"Invalid foreign key declaration: source table names " + mycols.size() + " columns but references " + targetColumns.size()); List<PEKeyColumnBase> fkcols = new ArrayList<PEKeyColumnBase>(); for(int i = 0; i < mycols.size(); i++) { PEKeyColumnBase mine = mycols.get(i); UnqualifiedName targName = targetColumns.get(i); PEKeyColumnBase pefk = null; if (targetTab == null) { if (mine.isUnresolved()) pefk = new PEForwardForeignKeyColumn(null,mine.getName(),targName); else pefk = new PEForeignKeyColumn(mine.getColumn(), targName); } else { PEColumn targCol = targetTab.lookup(pc, targName); if (targCol == null) throw new SchemaException(Pass.SECOND, "No such column " + targName + " in " + targetTableName); if (mine.isUnresolved()) pefk = new PEForwardForeignKeyColumn(null,mine.getName(),targCol); else pefk = new PEForeignKeyColumn(mine.getColumn(), targCol); } fkcols.add(pefk); } return new PEForeignKey(pc, name, targetTab,fullyQualifiedTargetName,fkcols, updateAction == null ? ForeignKeyAction.RESTRICT : updateAction, deleteAction == null ? ForeignKeyAction.RESTRICT : deleteAction); } public PETable buildTable(Name tableName, List<TableComponent<?>> fieldsAndKeys, DistributionVector dv, PEPersistentGroup sg, List<TableModifier> modifiers, boolean nascent, boolean temporary) { if ( tableName == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } Database<?> cdb = null; Name unqualifiedTableName = tableName; if (pc.getCapability() != Capability.PARSING_ONLY) { cdb = pc.getCurrentDatabase(false); if (cdb == null || tableName.isQualified()) { if (cdb == null && !tableName.isQualified()) throw new SchemaException(Pass.SECOND, "Current database not set"); QualifiedName qname = (QualifiedName) tableName; UnqualifiedName leading = qname.getNamespace(); if (cdb == null || !cdb.getName().equals(leading)) { cdb = pc.findDatabase(leading); if (cdb == null) throw new SchemaException(Pass.SECOND, "No such database: '" + leading + "'"); } unqualifiedTableName = tableName.getUnqualified(); } if (!(cdb instanceof PEDatabase)) throw new SchemaException(Pass.SECOND, "Invalid database for table creation: '" + cdb.getName() + "'"); } // the dv should have the container here, if applicable PETable newtab = null; if (nascent || temporary) { ComplexPETable ctab = new ComplexPETable(pc, unqualifiedTableName, fieldsAndKeys, dv, modifiers, sg, (PEDatabase) cdb, TableState.SHARED); if (nascent) ctab.withCTA(); if (temporary) ctab.withTemporaryTable(pc); newtab = ctab; } else newtab = new PETable(pc, unqualifiedTableName, fieldsAndKeys, dv, modifiers, sg, (PEDatabase) cdb, TableState.SHARED); if (pc.getCapability() != Capability.PARSING_ONLY) { pc.getPolicyContext().modifyTablePart(newtab); } newtab.setDeclaration(pc,newtab); return newtab; } public ExpressionNode buildBooleanLiteral(Boolean v, Token orig) { if (opts.isActualLiterals()) { return new ActualLiteralExpression(v,orig.getType(),SourceLocation.make(orig), null); } else { DelegatingLiteralExpression litex= new DelegatingLiteralExpression(TokenTypes.BOOLEAN, SourceLocation.make(orig),this,literals.size(),null); literals.add(litex,v); return litex; } } public ExpressionNode buildColumnReference(Name parsedName) { if (resolveColumnsAsIdentifiers) return buildIdentifierLiteral(parsedName); if (opts.isResolve()) return scope.buildColumnInstance(pc,parsedName); return new ColumnInstance(parsedName, null, null); } protected ExpressionNode buildLiteral(Token o) { String t = o.getText(); int tok = o.getType(); if ((tok == TokenTypes.NULL) || (tok == TokenTypes.GLOBAL)) return new ActualLiteralExpression(t,tok, SourceLocation.make(o),null); // if it's a string literal, strip off any charset hint for later UnqualifiedName charsetHint = null; if (tok == TokenTypes.Character_String_Literal && t.length() > 0) { if (t.charAt(0) == '_') { int firstQuote = t.indexOf("'"); if (firstQuote == -1) firstQuote = t.indexOf("\""); String hint = t.substring(0,firstQuote); charsetHint = new UnqualifiedName(hint,false); t = t.substring(firstQuote); } if ((t.length() > 2) && (t.startsWith("\"") && (t.endsWith("\"")))) { t = PEStringUtils.escapeSingleQuoteIfNecessary(t); } } if (PEStringUtils.isHexNumber(t)) { tok = TokenTypes.Hex_String_Literal; } ExpressionNode ex = null; if (opts.isActualLiterals() || (pc.getCapability() != Capability.PARSING_ONLY && pc.isMutableSource())) ex = new ActualLiteralExpression(ValueConverter.INSTANCE.convertLiteral(t,tok), tok, SourceLocation.make(o),charsetHint); else { DelegatingLiteralExpression litex = new DelegatingLiteralExpression(tok, SourceLocation.make(o),this,literals.size(), charsetHint); literals.add(litex, ValueConverter.INSTANCE.convertLiteral(t,tok)); ex = litex; } return ex; } public ExpressionNode buildIdentifierLiteral(String ident) { return buildIdentifierLiteral(new UnqualifiedName(ident)); } public ExpressionNode buildIdentifierLiteral(Name ident) { return new IdentifierLiteralExpression(ident); } public ExpressionNode buildNullLiteral() { return LiteralExpression.makeNullLiteral(); } public ExpressionNode buildFunctionCall(FunctionName givenName, List<ExpressionNode> params, SetQuantifier sq, Object tree) { FunctionName unqualifiedName = givenName; if (pc.getCapability() != Capability.PARSING_ONLY) { if (unqualifiedName.isPipes()) { forceUncacheable(ValueManager.CacheStatus.NOCACHE_DYNAMIC_FUNCTION); SQLMode mode = KnownVariables.SQL_MODE.getSessionValue(pc.getConnection().getVariableSource()); if (mode.isPipesAsConcat()) { // rewrite to use the concat function call unqualifiedName = new FunctionName("CONCAT",-1,false); } else { // rewrite using or unqualifiedName = FunctionName.makeOr(); } } else if (unqualifiedName.isDoubleAmpersand()) { unqualifiedName = FunctionName.makeAnd(); } } @SuppressWarnings("unchecked") FunctionCall fc = new FunctionCall(unqualifiedName, (params == null ? Collections.EMPTY_LIST : params), sq, SourceLocation.make(tree)); if (EngineConstant.FUNCTION.has(fc, EngineConstant.DATABASE) || EngineConstant.FUNCTION.has(fc, EngineConstant.SCHEMA)) { forceUncacheable(ValueManager.CacheStatus.NOCACHE_DYNAMIC_FUNCTION); Database<?> peds = pc.getCurrentDatabase(false); if (peds == null) return LiteralExpression.makeNullLiteral(); return LiteralExpression.makeStringLiteral(peds.getName().get()); } else if (EngineConstant.FUNCTION.has(fc, EngineConstant.CURRENT_USER)) { forceUncacheable(ValueManager.CacheStatus.NOCACHE_DYNAMIC_FUNCTION); PEUser peu = pc.getCurrentUser().get(pc); if (peu == null) return LiteralExpression.makeNullLiteral(); // not entirely correct return LiteralExpression.makeStringLiteral(peu.getUserScope().getSQL()); } else if (EngineConstant.FUNCTION.has(fc, EngineConstant.LAST_INSERT_ID)) { forceUncacheable(ValueManager.CacheStatus.NOCACHE_DYNAMIC_FUNCTION); return LiteralExpression .makeLongLiteral(pc.getConnection().getLastInsertedId()); } else if (EngineConstant.FUNCTION.has(fc, EngineConstant.RAND)) { return buildRandCall(fc.getParameters()); } if (unqualifiedName.isAggregate() || isFunctionThatRequiresSetTimestampVariable(fc.getFunctionName() .getUnqualified().get(), params)) { scope.registerFunction(fc); } return fc; } private boolean isFunctionThatRequiresSetTimestampVariable(String name, List<ExpressionNode> params) { boolean ret = false; if (TimestampVariableUtils.isTimestampFunction(name)) { // unix_timestamp also takes a parameter // so we only need to set the timestamp variable if there are no // parameter(s) if (TimestampVariableUtils.isFunctionUnixTimestamp(name)) { if (params != null && params.size() > 0) { ret = false; } else { ret = true; } } else { ret = true; } } return ret; } private UnqualifiedName castAlias(Name alias) { if (alias == null) return null; if (alias instanceof UnqualifiedName) return (UnqualifiedName) alias; throw new ParserException(Pass.SECOND, "Use of qualified identifier for alias: '" + alias.getSQL() + "'"); } public ExpressionNode buildTableInstance(Name name, Name alias) { return buildTableInstance(name,alias,null); } public ExpressionNode buildTableInstance(Name name, Name alias, List<IndexHint> hints) { if ( name == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } TableInstance ti = null; if (opts.isResolve()) { ti = scope.buildTableInstance( name, castAlias(alias), pc, lockInfo); } else { ti = new TableInstance(null, name, castAlias(alias), 0, opts.isResolve()); } if (hints != null) ti.setHints(hints); return ti; } public IndexHint buildIndexHint(HintType type, boolean isKey, HintTarget targ, List<UnqualifiedName> indexNames) { return new IndexHint(type, isKey, targ, indexNames); } public ExpressionNode buildWildcard(Token orig) { return new Wildcard(SourceLocation.make(orig)); } public ExpressionNode buildQuestionMark(CommonTree orig) { return new Parameter(SourceLocation.make(orig)); } public LimitSpecification buildLimitSpecification(ExpressionNode rowcount, ExpressionNode offset) { return new LimitSpecification(rowcount, offset); } /** * @param a * @param asc * @param desc * @return */ public SortingSpecification buildSortingSpecification(ExpressionNode a, Object asc, Object desc) { // default is asc, so as long as desc is non-null it is descending boolean direction = (desc == null); return new SortingSpecification(a, direction); } public FunctionName buildOperatorName(CommonTree ct) { return new FunctionName(ct.getText(), ct.getType(), true); } public FunctionName buildOperatorName(Token t) { return new FunctionName(t.getText(), t.getType(), true); } public FunctionName buildFunctionName(String v, int tokenKind) { return new FunctionName(v, tokenKind, false); } public FunctionName buildFunctionName(Token tok, boolean op) { return new FunctionName(tok.getText(), tok.getType(), op, SourceLocation.make(tok)); } public JoinSpecification buildJoinType(String primary) { return buildJoinType(primary, null, null); } public JoinSpecification buildJoinType(String primary, String natural, String outer) { StringBuilder buf = new StringBuilder(); if (natural != null) buf.append(natural).append(" "); if (primary != null) buf.append(primary).append(" "); if (outer != null) buf.append(outer); return JoinSpecification.makeJoinSpecification(buf.toString() .toUpperCase()); } public JoinClauseType buildJoinClauseType(ExpressionNode node, List<Name> namedCols) { return new JoinClauseType(node, namedCols, ((namedCols != null) ? ClauseType.USING : ClauseType.ON)); } public JoinedTable buildJoinedTable(ExpressionNode target, JoinClauseType joinOn, JoinSpecification injoinType) { // JOIN is INNER JOIN JoinSpecification joinType = (injoinType == null ? buildJoinType("INNER") : injoinType); return new JoinedTable(target, (joinOn == null ? null : joinOn.getNode()), joinType, (joinOn == null ? null : joinOn.getColumnIdentifiers())); } public TableJoin buildTableJoin(ExpressionNode factor, List<JoinedTable> joins) { return new TableJoin(factor,joins); } public FromTableReference buildFromTableReference(ExpressionNode factor, List<JoinedTable> joins) { if (joins.isEmpty()) { return new FromTableReference(factor); } convertUsingColSpecToOnSpec(factor, joins); return new FromTableReference(new TableJoin(factor,joins)); } private void convertUsingColSpecToOnSpec(ExpressionNode factor, List<JoinedTable> joins) { if (!opts.isResolve()) return; List<UnqualifiedName> visibleAliases = new LinkedList<UnqualifiedName>(); if (factor instanceof TableInstance) visibleAliases.add(0, ((TableInstance) factor).getReferenceName(pc).getUnqualified()); for (JoinedTable jt : joins) { List<Name> usingColSpec = jt.getUsingColSpec(); if (usingColSpec != null && usingColSpec.size() > 0 ) { ExpressionNode jtTable = jt.getJoinedTo(); visibleAliases.add(0, ((TableInstance)jtTable).getReferenceName(pc).getUnqualified()); // turn USING column spec to equivalent ON t1.fieldn = t2.fieldn List<Name> unqparts = new ArrayList<Name>(); List<ExpressionNode> params = new ArrayList<ExpressionNode>(); List<ExpressionNode> equals = new ArrayList<ExpressionNode>(); // build list of t1.fieldn = t2.fieldn function(s) first List<ExpressionNode> functions = new ArrayList<ExpressionNode>(); for(Name name : usingColSpec) { params.clear(); // try to find the right table to hang the thing on int foff = -1; SchemaException any = null; int offset = -1; for(UnqualifiedName un : visibleAliases) { offset++; if (params.size() == 2) break; unqparts.clear(); unqparts.add(un); unqparts.add(name.getUnqualified()); try { ExpressionNode en = buildColumnReference(buildQualifiedName(unqparts)); if (params.isEmpty()) { params.add(en); foff = offset; } else if (foff > offset) params.add(en); else params.add(0,en); } catch (SchemaException se) { any = se; } } if (params.size() != 2) { if (any != null) throw any; throw new SchemaException(Pass.SECOND, "Failed to resolve using clause"); } equals.add(buildFunctionCall(FunctionName.makeEquals(), params, null, null)); } if (equals.size() > 0) { // build function(s) of AND'ed t1.fieldn = t2.fieldn functions.clear(); ExpressionNode current = null; ExpressionNode last = null; for(int i=0; i < equals.size(); ++i) { current = equals.get(i); if (i > 0) { params.clear(); params.add(last); params.add(current); current = buildFunctionCall(FunctionName.makeAnd(), params, null, null); } last = current; } // just set using the last function Edge<?, ExpressionNode> edge = jt.getJoinOnEdge(); edge.add(last); // clear the using column spec // usingColSpec.clear(); } } } } public Name buildQualifiedName(List<Name> parts) { if (parts.size() == 1) return parts.get(0); ArrayList<UnqualifiedName> unqparts = new ArrayList<UnqualifiedName>(); for (Name n : parts) { if (n == null) continue; unqparts.addAll(n.getParts()); } Name out = new QualifiedName(unqparts); // verify that none of the parts (other than the last), is an asterisk for (Iterator<UnqualifiedName> iter = unqparts.iterator(); iter .hasNext();) { UnqualifiedName un = iter.next(); if (un.isAsterisk()) { if (iter.hasNext()) throw new SchemaException(Pass.FIRST, "Invalid qualified name: " + out.getSQL()); } } return out; } public Name buildNameFromStringLiteral(Object in) { CommonTree tree = (CommonTree) in; return new UnqualifiedName(PEStringUtils.dequote(tree.getText())); } public Name buildName(Token tok) { return new UnqualifiedName(tok.getText(), SourceLocation.make(tok)); } public Name buildName(Object in) { CommonTree tree = (CommonTree) in; return new UnqualifiedName(tree.getText(), SourceLocation.make(tree)); } public Name buildName(String in) { return new UnqualifiedName(in, null); } public ExpressionNode addGrouping(ExpressionNode in) { in.setGrouped(); return in; } public ProjectingStatement addGrouping(ProjectingStatement in) { in.setGrouped(true); return in; } // only 1 of the first four args will be non null. /** * @param plusSign * @param minusSign * @param binary * @param tilde * @param in * @param collate * @param collation * @return */ public ExpressionNode applyExprFlags(Token plusSign, Token minusSign, Token binary, Token tilde, ExpressionNode in, Token collate, Name collation) { ExpressionNode out = in; if (collation != null) { // collation is an operator - it has higher precedence than the other stuff; hack it in FunctionName fn = new FunctionName(collate.getText(), collate.getType(),true); FunctionCall fc = new FunctionCall(fn,out,new IdentifierLiteralExpression(collation)); out = fc; } if (minusSign != null) { out.setNegated(); } else if (tilde != null) { out.setBitNegated(); } else if (binary != null) { // this is a function call FunctionName fn = new FunctionName(binary.getText(),binary.getType(),true); FunctionCall fc = new FunctionCall(fn,out); out = fc; } return out; } public ExpressionNode buildDefaultKeywordExpr(Token orig) { return new Default(SourceLocation.make(orig)); } public ExpressionNode buildUpdateExpression(FunctionName eqop, Name columnRef, ExpressionNode expr, Object in) { CommonTree tree = (CommonTree) in; // this is just a function call, but we have to wrap the column ref ArrayList<ExpressionNode> params = new ArrayList<ExpressionNode>(); params.add(buildColumnReference(columnRef)); params.add(expr); return buildFunctionCall(eqop, params, null, tree); } public VariableInstance buildLHSVariableInstance(VariableScope vs, Name n, Object tree) { return captureVariable(new VariableInstance(n.getUnqualified(), vs, SourceLocation.make(tree), false)); } public VariableInstance buildRHSVariableInstance(Object fat, Object sat, VariableScopeKind vsk, Name n, Object tree) { VariableScopeKind kind = vsk; if (kind == null) { if (fat != null) { if (sat != null) { /* * MySQL returns the session value if it exists and the * global value otherwise. */ final String varName = n.getUnqualified().getUnquotedName().get(); final VariableHandler<?> exists = Singletons.require(VariableService.class).getVariableManager().lookup(varName); if (exists != null) { if (exists.getScopes().contains(VariableScopeKind.SESSION)) { kind = VariableScopeKind.SESSION; } else { kind = VariableScopeKind.GLOBAL; } } } else { kind = VariableScopeKind.USER; } } else { kind = VariableScopeKind.SESSION; } } return captureVariable(new VariableInstance(n.getUnqualified(), new VariableScope(kind), SourceLocation.make(tree), true)); } private VariableInstance captureVariable(VariableInstance vi) { if (!scope.isEmpty()) scope.getVariables().add(vi); return vi; } public VariableScope buildVariableScope(VariableScopeKind vsk) { return new VariableScope(vsk); } public VariableScope buildVariableScope(Name scoped) { if (scoped == null) return new VariableScope(VariableScopeKind.SCOPED); return new VariableScope(scoped.getUnquotedName().get()); } public SetExpression buildSetVariableExpression(VariableInstance v, ExpressionNode en) { return buildSetVariableExpression(v,Collections.singletonList(en)); } public SetExpression buildSetVariableExpression(VariableInstance v, List<ExpressionNode> l) { if (StringUtils.endsWithIgnoreCase(v.getVariableName().get(), "NAMES")) { // validate NAME variable is one of our supported ones // should only be one item in l if (l.get(0) instanceof LiteralExpression) { LiteralExpression le = (LiteralExpression) l.get(0); if (le.isNullLiteral()) { throw new SchemaException(Pass.FIRST, "Must specify a character set"); } String value = (String)le.getValue(pc.getValues()); if (Singletons.require(CharSetNative.class).getCharSetCatalog().findCharSetByName(value) == null) { // character set not supported throw new SchemaException(Pass.FIRST, "Cannot set an unsupported character set: " + value); } } } if (l.size() == 1) return new SetVariableExpression(v, l.get(0)); return new SetVariableExpression(v, l); } public SetExpression buildSetTransactionIsolation( SetTransactionIsolationExpression.IsolationLevel il, VariableScopeKind scopeKind) { return new SetTransactionIsolationExpression(il, scopeKind); } public SessionSetVariableStatement buildSessionSetVarStatement( List<SetExpression> l) { return new SessionSetVariableStatement(l); } public SessionSetVariableStatement buildAlterPersistentVariable( VariableInstance vi, ExpressionNode expr) { ArrayList<SetExpression> sets = new ArrayList<SetExpression>(); sets.add(new SetVariableExpression(vi, expr)); return new SessionSetVariableStatement(sets); } @SuppressWarnings("rawtypes") public Statement buildAddVariable(Name newName, List<Pair<Name,LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("create a new system variable"); String varName = newName.getUnqualified().getUnquotedName().get(); VariableHandler exists = Singletons.require(VariableService.class).getVariableManager().lookup(varName); if (exists != null) throw new SchemaException(Pass.SECOND,"Variable " + newName + " already exists"); return AddGlobalVariableStatement.decode(pc, varName, options); } private Comment buildComment(String c) { return new Comment(PEStringUtils.dequote(c)); } public TableModifier buildTableModifier(List<ExpressionNode> l) { return new UnknownTableModifier(l); } public TableModifier buildTableModifier(ExpressionNode lhs, boolean hasEquals, ExpressionNode rhs) { ArrayList<ExpressionNode> out = new ArrayList<ExpressionNode>(); if (hasEquals) { out.add(buildFunctionCall(FunctionName.makeEquals(), Arrays.asList(new ExpressionNode[] { lhs, rhs }), null, null)); } else { out.add(lhs); out.add(rhs); } return new UnknownTableModifier(out); } public TableModifier buildAutoincTableModifier(ExpressionNode litval) { LiteralExpression lit = (LiteralExpression) litval; Object value = lit.getValue(pc.getValues()); if (value instanceof Number) { Number n = (Number)value; return new AutoincTableModifier(n.longValue()); } throw new SchemaException(Pass.SECOND, "Unknown autoinc value kind: " + value.getClass().getSimpleName()); } public TableModifier buildEngineTableModifier(Name name) { EngineTableModifier.EngineTag actualTag = EngineTableModifier.EngineTag.findEngine(name.getUnqualified().getUnquotedName().get()); if (actualTag == null) actualTag = EngineTableModifier.EngineTag.INNODB; return new EngineTableModifier(actualTag); } public TableModifier buildCommentTableModifier(final Name tableName, final String s) throws SchemaException { final long maxAllowedLength = Singletons.require(DBNative.class).getMaxTableCommentLength(); if (s.length() > maxAllowedLength) { throw new SchemaException(new ErrorInfo(AvailableErrors.TOO_LONG_TABLE_COMMENT, tableName.getUnqualified().getUnquotedName().get(), maxAllowedLength)); } return new CommentTableModifier(buildComment(s)); } public Comment buildTableFieldComment(final Name fieldName, final String s) throws SchemaException { final long maxAllowedLength = Singletons.require(DBNative.class).getMaxTableFieldCommentLength(); if (s.length() > maxAllowedLength) { throw new SchemaException(new ErrorInfo(AvailableErrors.TOO_LONG_TABLE_FIELD_COMMENT, fieldName.getUnquotedName().get(), maxAllowedLength)); } return buildComment(s); } public TableModifier buildRowFormatTableModifier(Name n) { return new RowFormatTableModifier(n.getUnqualified()); } public TableModifier buildMaxRowsModifier(ExpressionNode en) { LiteralExpression litex = asLiteral(en); Object v = litex.getValue(pc.getValues()); if (v instanceof Number) { Number n = (Number) v; return new MaxRowsModifier(n.longValue()); } throw new SchemaException(Pass.FIRST, "Invalid max rows value - not a number"); } public TableModifier buildChecksumModifier(ExpressionNode en) { Integer v = asIntegralLiteral(en); return new ChecksumModifier(v.intValue()); } public TableModifier buildDelayKeyWriteTableModifier(ExpressionNode en) { throw new SchemaException(Pass.FIRST, "No support for DELAY_KEY_WRITE table option"); } public Statement buildCommitTransactionStatement() { return new TransactionStatement(TransactionStatement.Kind.COMMIT); } public Statement buildStartTransactionStatement(boolean snapshot) { return new StartTransactionStatement(snapshot); } public Statement buildSavepointTransactionStatement(Name n) { return new SavepointStatement(n, false); } public Statement buildReleaseSavepointTransactionStatement(Name n) { return new SavepointStatement(n, true); } public Statement buildRollbackTransactionStatement(Name n) { return new RollbackTransactionStatement(n); } public Statement buildTruncateStatement(Name name, Object tree) { final TableInstance ti = (TableInstance) buildTableInstance(name, null); if (ti.getAbstractTable().isTable()) { final TruncateStatement ts = new TruncateStatement(ti, SourceLocation.make(tree)); ts.getDerivedInfo().takeScope(scope); return ts; } UnqualifiedName encName = null; UnqualifiedName tname = null; if (name.isQualified()) { QualifiedName qn = (QualifiedName) name; encName = qn.getNamespace(); tname = qn.getUnqualified(); } else { tname = (UnqualifiedName) name; encName = pc.getCurrentDatabase().getName().getUnqualified(); } throw new SchemaException(new ErrorInfo(AvailableErrors.TABLE_DNE,encName.getUnquotedName().get(),tname.getUnquotedName().get())); } public ExpressionNode buildSubquery(Statement in, Name alias, Object orig, boolean makeVirtualTable) { ProjectingStatement dmls = (ProjectingStatement) in; Subquery sq = new Subquery(dmls, alias,SourceLocation.make(orig)); if (makeVirtualTable) { SubqueryTable sqt = SubqueryTable.make(pc,dmls); sq.setTable(sqt); scope.pushVirtualTable(sqt, alias.getUnqualified(), pc); scope.getNestedQueries().add(dmls); } // if an alias was specified, then enter a fake table into the scope. return sq; } public ExpressionNode buildExists(Token exists, Statement ss, Object orig) { ExpressionNode param = buildSubquery(ss,null,orig,false); return buildFunctionCall(buildFunctionName(exists,false),Collections.singletonList(param),null,null); } public Statement buildCreateRangeStatement(Name rangeName, Name persistentGroup, List<BasicType> types, Boolean ifNotExists) { pc.getPolicyContext().checkRootPermission("create a range"); RangeDistribution rd = pc.findRange(rangeName, persistentGroup); if (rd != null && !getOptions().isAllowDuplicates()) { if (Boolean.TRUE.equals(ifNotExists)) { return buildEmptyStatement("Create an existing range."); } throw new SchemaException(Pass.SECOND, "Range " + rangeName.getSQL() + " already exists."); } PEPersistentGroup onGroup = pc.findStorageGroup(persistentGroup); if (onGroup == null) throw new SchemaException(Pass.SECOND, "No such persistent group: " + persistentGroup.getSQL()); RangeDistribution nrd = new RangeDistribution(pc, rangeName, types, onGroup); if (rd != null) try { // it is an error if the redeclaration doesn't match the original rd.verifySame(pc,nrd); } catch (PEException pe) { throw new SchemaException(Pass.SECOND,pe); } return new PECreateStatement<RangeDistribution, DistributionRange>((rd == null ? nrd : rd), true, "RANGE", rd != null); } public Statement buildDropRangeStatement(Name rangeName, Boolean ifExists, Name storageGroup) { pc.getPolicyContext().checkRootPermission("drop a range"); RangeDistribution rd = null; if (storageGroup == null) { List<DistributionRange> allsuch = pc.getCatalog().findDistributionRange(rangeName.getUnquotedName().get()); if (allsuch.size() > 1) throw new SchemaException(Pass.SECOND, "More than one range named " + rangeName + " please specify group (add persistent group <name>)"); else if (allsuch.isEmpty()) { if (Boolean.TRUE.equals(ifExists)) { return buildEmptyStatement("Drop a non existing range."); } throw new SchemaException(Pass.SECOND, "No such range: " + rangeName.getSQL()); } rd = pc.findRange(rangeName, new UnqualifiedName(allsuch.get(0).getStorageGroup().getName())); } else { rd = pc.findRange(rangeName, storageGroup); if (rd == null) { if (Boolean.TRUE.equals(ifExists)) { return buildEmptyStatement("Drop a non existing range."); } throw new SchemaException(Pass.SECOND, "No such range: " + rangeName.getSQL()); } } PEDropRangeStatement out = new PEDropRangeStatement(rd); out.ensureUnreferenced(pc); return out; } public Statement buildDropPersistentGroupStatement(Name groupName) { pc.getPolicyContext().checkRootPermission("drop a persistent group"); PEPersistentGroup peg = pc.findStorageGroup(groupName); if (peg == null) throw new SchemaException(Pass.SECOND, "No such persistent group: " + groupName.getSQL()); PEDropStorageGroupStatement out = new PEDropStorageGroupStatement(peg); out.ensureUnreferenced(pc); return out; } public Statement buildAddPersistentSiteStatement(Name sgName, List<Name> siteNames, boolean rebalance) { pc.getPolicyContext().checkRootPermission("add a persistent site"); PEPersistentGroup pesg = pc.findStorageGroup(sgName); if (pesg == null) throw new SchemaException(Pass.SECOND, "No such persistent group: " + sgName.getSQL()); ArrayList<PEStorageSite> sites = new ArrayList<PEStorageSite>(); for (Name n : siteNames) { PEStorageSite pess = pc.findStorageSite(n); if (pess == null) throw new SchemaException(Pass.SECOND, "No such persistent site: " + n.getSQL()); sites.add(pess); } return new AddStorageSiteStatement(pesg, sites, rebalance); } public Statement buildShowSingularQuery(String onInfoSchemaTable, Name objectName) { if (pc.getCapability() == Capability.PARSING_ONLY && !pc.getCatalog().isPersistent()) return new EmptyStatement("no catalog queries with transient execution engine"); // TODO: // handle !resolve ShowSchemaBehavior ist = Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName(onInfoSchemaTable)); if (ist == null) throw new MigrationException("Need to add info schema table for " + onInfoSchemaTable); return ist.buildUniqueStatement(pc, objectName, new ShowOptions()); } public void push_info_schema_scope(Name n) { // parsing only - just add the darn thing if (pc.getCapability() == Capability.PARSING_ONLY) { pushScope(); scope.buildTableInstance(n, new UnqualifiedName("a"), null, pc, null); } else { ShowSchemaBehavior sst = Singletons.require(InformationSchemaService.class) .lookupShowTable((UnqualifiedName) n); if (sst == null) throw new SchemaException(Pass.SECOND, "No such table: " + n.getSQL()); InformationSchemaTable ist = (InformationSchemaTable) sst; pushScope(); // we put in an alias anyways scope.buildTableInstance(ist.getName(), new UnqualifiedName("a"), Singletons.require(InformationSchemaService.class).getShowSchema(), pc, null); } } public void push_info_schema_scope(String s) { push_info_schema_scope(buildName(s)); } public Statement buildShowPluralQuery(String onInfoSchemaTable, List<Name> scoping, Pair<ExpressionNode, ExpressionNode> likeOrWhere) { return buildShowPluralQuery(onInfoSchemaTable, scoping, likeOrWhere, null); } public Statement buildShowPluralQuery(String onInfoSchemaTable, List<Name> scoping, Pair<ExpressionNode, ExpressionNode> likeOrWhere, Token full) { if (pc.getCapability() == Capability.PARSING_ONLY && !pc.getCatalog().isPersistent()) return new EmptyStatement("no catalog queries with transient execution engine"); ShowSchemaBehavior ist = Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName(onInfoSchemaTable)); if (ist == null) throw new MigrationException("Need to add info schema table for " + onInfoSchemaTable); ExpressionNode likeExpr = (likeOrWhere == null ? null : likeOrWhere .getFirst()); ExpressionNode whereExpr = (likeOrWhere == null ? null : likeOrWhere .getSecond()); ShowOptions so = new ShowOptions(); if (full != null) so.withFull(); // break up the scoping value into parts if qualified List<Name> scopingParts = new ArrayList<Name>(); if (scoping != null) { for (Name name : scoping) { if (name instanceof QualifiedName) { QualifiedName qn = (QualifiedName)name; scopingParts.add(qn.getParts().get(1)); // table name scopingParts.add(qn.getParts().get(0)); // database } else { // assumed UnqualifiedName scopingParts.add(name); } } } return ist.buildShowPlural(pc, scopingParts, likeExpr, whereExpr, so); } public Statement buildReloadLogging() { return new SessionStatement("RELOAD LOGGING") { @Override public boolean isPassthrough() { return false; } @Override public void plan(SchemaContext sc, ExecutionSequence es, BehaviorConfiguration config) throws PEException { es.append(new TransientSessionExecutionStep("RELOAD LOGGING", new AdhocOperation() { @Override public void execute(ExecutionState estate, WorkerGroup wg, DBResultConsumer resultConsumer) throws Throwable { try { // Servers should have been started with a log4j system property // e.g. -Dlog4j.configuration=<something>log4j.properties String name = System.getProperty("log4j.configuration"); if(StringUtils.isBlank(name)) throw new PEException("log4j.configuration property not set"); URL resource = TranslatorUtils.class.getClassLoader().getResource(name); if(resource == null) throw new PEException("Couldn't locate file '" + name + "' on the classpath"); PropertyConfigurator.configure(resource); logger.info("log4j configuration reloaded from '" + name + "'"); } catch (Exception e) { logger.error("Unable to reload logging configuation: " + e.getMessage(), e); throw new PEException("Unable to reload logging configuration: " + e.getMessage(),e); } } })); } }; } public PEPersistentGroup lookupPersistentGroup(Name n) { PEPersistentGroup pesg = pc.findStorageGroup(n); if (pesg == null) throw new SchemaException(Pass.SECOND, "No such persistent group " + n.getSQL()); return pesg; } public Statement setExplainFlag(Statement in, List<Pair<Name, LiteralExpression>> options) { ExplainOptions opts = ExplainOptions.NONE; if (options != null) { for(Pair<Name, LiteralExpression> p : options) { ExplainOption opt = ExplainOption.find(p.getFirst().getUnqualified()); if (opt == null) throw new SchemaException(Pass.FIRST, "No such explain option: " + p.getFirst().getSQL()); opts = opts.addSetting(opt, p.getSecond().getValue(pc.getValues())); } } in.setExplain(opts); return in; } public SetQuantifier lookupSetQuantifier(String in) { SetQuantifier sq = SetQuantifier.fromSQL(in); if (sq == null) throw new SchemaException(Pass.SECOND, "Unknown set quantifier: " + in); return sq; } public Statement buildKillStatement(ExpressionNode expr, Boolean isKillConnection) { return new KillStatement(asIntegralLiteral(expr), isKillConnection.booleanValue()); } public Statement buildEmptyStatement(String text) { return new EmptyStatement(text); } public LockType lookupLockType(String l, String r) { LockType lt = LockType.fromSQL(l, r); if (lt == null) throw new SchemaException(Pass.SECOND, "No such lock type " + (l == null ? "" : l) + " " + (r == null ? "" : r)); return lt; } public Statement buildUnlockTablesStatement() { return new LockStatement(); } public Statement buildLockTablesStatement( List<Pair<ExpressionNode, LockType>> in) { ListOfPairs<TableInstance, LockType> tabs = new ListOfPairs<TableInstance, LockType>(); for (Pair<ExpressionNode, LockType> p : in) { TableInstance ti = (TableInstance) p.getFirst(); tabs.add(ti, p.getSecond()); } return new LockStatement(tabs); } public void startSessionVarsScope() { resolveColumnsAsIdentifiers = true; } public void endSessionVarsScope() { resolveColumnsAsIdentifiers = false; } public UserScope findCurrentUser() { if (pc.getCapability() == Capability.PARSING_ONLY) return null; return pc.getCurrentUser().get(pc).getUserScope(); } public UserScope buildUserScope(String name, String scopeStr) { return new UserScope(PEStringUtils.dequote(name), PEStringUtils.dequote((scopeStr == null) ? "'%'" : scopeStr)); } public PEUser buildUserSpec(UserScope us, String pword) { return new PEUser(us, pword == null ? null : PEStringUtils.dequote(pword), false); } public Statement buildCreateUserStatement(List<PEUser> specs) { pc.getPolicyContext().checkRootPermission("create a user"); // should check to see whether the users already exist if (pc.getCapability() != Capability.PARSING_ONLY && pc.isPersistent()) { for (PEUser peu : specs) { List<User> users = pc.getCatalog().findUsers( peu.getUserScope().getUserName(), peu.getUserScope().getScope()); if (!users.isEmpty()) throw new SchemaException(Pass.SECOND, "User " + peu.getUserScope().getSQL() + " already exists"); } } return new PECreateUserStatement(specs, false); } public Statement buildSetPasswordStatement(UserScope us, String text) { pc.getPolicyContext().checkRootPermission("set a password"); PEUser exists = pc.findUser(us.getUserName(), us.getScope()); if (exists == null) throw new SchemaException(Pass.SECOND, "User " + us.getUserName() + " does not exist"); return new SetPasswordStatement(exists, PEStringUtils.dequote(text)); } public Statement buildDropUserStatement(UserScope us, Boolean ifExists) { pc.getPolicyContext().checkRootPermission("drop a user"); PEUser peu = pc.findUser(us.getUserName(), us.getScope()); if (peu == null) { if (Boolean.TRUE.equals(ifExists)) { // just do an empty statement return new EmptyStatement("drop nonexistent user"); } throw new SchemaException(new ErrorInfo(AvailableErrors.UNKNOWN_USER,us.getUserName(),us.getScope())); } return new PEDropUserStatement(peu); } public Statement buildAlterTableStatement(TableKey tab, AlterTableAction single) { return buildAlterTableStatement(tab, Collections.singletonList(single)); } public Statement buildAlterTableStatement(TableKey tab, List<AlterTableAction> actions) { if (pc.getCapability() == Capability.PARSING_ONLY) return new PEAlterTableStatement(pc, tab, actions); PEAlterStatement<PETable> single = null; AlterTableAction singleAction = null; for(AlterTableAction aa : actions) { single = aa.requiresSingleStatement(pc, tab); if (single != null) { singleAction = aa; break; } } if (single != null) { if (actions.size() > 1) throw new SchemaException(Pass.SECOND, "alter action of type " + singleAction.getClass().getSimpleName() + " must be the sole alter action"); return single; } PEAlterTableStatement orig = new PEAlterTableStatement(pc,tab, actions); return pc.getPolicyContext().modifyAlterTableStatement(orig); } public Statement buildRenameTableStatement(final List<Pair<Name, Name>> sourceTargetNamePairs) { return RenameTableStatement.buildRenameTableStatement(pc, sourceTargetNamePairs); } public Statement buildRenameDatabaseStatement(final Pair<Name, Name> sourceTargetNames) { throw new SchemaException(Pass.FIRST, "This syntax is not supported as it has been deprecated."); } private List<AlterTableAction> wrapAlterAction(AlterTableAction ata) { return Collections.singletonList(ata); } public List<AlterTableAction> buildRenameTableAction(Name newName) { return wrapAlterAction(new RenameTableAction(newName)); } /** * If you specify CONVERT TO CHARACTER SET without a collation, the default * collation for the character set is used. */ public List<AlterTableAction> buildTableConvertToAction(final Name charSet, final Name collation) { final Pair<String, String> charSetCollationPair = getCharsetCollationPair(charSet, collation); return wrapAlterAction(new ConvertToAction(charSetCollationPair.getFirst(), charSetCollationPair.getSecond())); } public List<AlterTableAction> buildChangeColumnAction(Name columnName, TableComponent<PEColumn> newDef, Pair<String, Name> firstOrAfterSpec) { return wrapAlterAction(new ChangeColumnAction(lookupAlteredColumn(columnName), (PEColumn) newDef, firstOrAfterSpec)); } public List<AlterTableAction> buildChangeColumnAction(Name columnName, List<TableComponent<?>> defs, Pair<String, Name> firstOrAfterSpec) { ArrayList<AlterTableAction> out = new ArrayList<AlterTableAction>(); for(TableComponent<?> tc : defs) { if (tc instanceof PEColumn) { PEColumn pec = (PEColumn) defs.get(0); out.add(new ChangeColumnAction(lookupAlteredColumn(columnName), pec, firstOrAfterSpec)); } else if (tc instanceof PEKey) { out.add(new AddIndexAction((PEKey)tc)); } else { throw new SchemaException(Pass.FIRST, "Unknown alter table change column target: " + tc); } } return out; } public List<AlterTableAction> buildModifyColumnAction(List<TableComponent<?>> defs, Pair<String, Name> firstOrAfterSpec) { if (defs.size() > 1) throw new SchemaException(Pass.SECOND, "No support for modifying column def with inline key def"); PEColumn newDef = (PEColumn) defs.get(0); PEColumn existing = lookupAlteredColumn(newDef.getName()); return wrapAlterAction(new ChangeColumnAction(existing, newDef, firstOrAfterSpec)); } public List<AlterTableAction> buildAlterColumnAction(Name columnName, ExpressionNode litex) { return wrapAlterAction(new AlterColumnAction(lookupAlteredColumn(columnName), (LiteralExpression) litex)); } public List<AlterTableAction> buildAddIndexAction(PEKey newIndex) { if (pc.getCapability() != Capability.PARSING_ONLY && newIndex.isUnresolved()) throw new SchemaException(Pass.FIRST, "Invalid forward key during alter"); return wrapAlterAction(new AddIndexAction((PEKey)newIndex)); } public List<AlterTableAction> buildDropIndexAction(ConstraintType kt, Name indexName) { PEKey targ = lookupAlteredKey(kt,indexName); return wrapAlterAction(new DropIndexAction(targ)); } public Statement buildExternalDropIndexStatement(Name indexName, Name tableName) { TableKey tk = lookupAlteredTable(tableName); AlterTableAction aa = buildDropIndexAction(null, indexName).get(0); return buildAlterTableStatement(tk,aa); } public List<AlterTableAction> buildDropColumnAction(Name columnName) { List<AlterTableAction> actions = new ArrayList<AlterTableAction>(); PEColumn alteredCol = lookupAlteredColumn(columnName); // cannot drop column if it is the target in an FK if (pc.getCapability() == Capability.FULL) { for (UserTable ut : pc.getCatalog().findTablesWithFKSReferencing(alteredCol.getTable().getPersistentID())) { for(Key k : ut.getKeys()) { if (k.isForeignKey() && StringUtils.equals(k.getReferencedTable().getName(), alteredCol.getTable().getName().get())) { for(KeyColumn c : k.getColumns()) { if (StringUtils.equals(c.getTargetColumnName(), alteredCol.getName().get())) { throw new SchemaException(Pass.SECOND, "Cannot drop column '" + columnName.get() + "' because it is part of foreign key '" + k.getName() + "'"); } } } } } for(PEKey key : alteredCol.getTable().getKeys(pc)) { if (key.containsColumn(alteredCol)) { if (key.isForeign()) { throw new SchemaException(Pass.SECOND, "Cannot drop column '" + columnName.get() + "' because it is part of foreign key '" + key.getName().get() + "'"); } actions.add(buildDropIndexAction(null, key.getName()).get(0)); if (key.getColumns(pc).size() > 1) { // rebuild index if multicol PEKey newPEKey = key.copy(pc, alteredCol.getTable().asTable()); newPEKey.removeColumn(alteredCol); actions.add(buildAddIndexAction(newPEKey).get(0)); } } } } actions.add(new DropColumnAction(alteredCol)); return actions; } public List<AlterTableAction> buildAlterTableOptionActions(List<TableModifier> tm) { final List<AlterTableAction> actions = new ArrayList<AlterTableAction>(tm.size()); for (final TableModifier modifier : tm) { actions.add(new ChangeTableModifierAction(modifier)); } return actions; } public List<AlterTableAction> buildModifyDistributionAction(UnresolvedDistributionVector ndv) { return wrapAlterAction(new ChangeTableDistributionAction(ndv.resolve(pc, this))); } @SuppressWarnings("rawtypes") public List<AlterTableAction> buildAddColumnAction(List<TableComponent> newColumns) { return buildAddColumnAction(newColumns, null); } @SuppressWarnings("rawtypes") public List<AlterTableAction> buildAddColumnAction(List<TableComponent> newColumns, Pair<String,Name> firstOrAfterSpec) { if (firstOrAfterSpec != null) { // only valid when adding one column. note that if we added a column & a key at the same time we should // ignore the key part (because keys doen't really have a position). int ncols = 0; for(TableComponent tc : newColumns) if (tc instanceof PEColumn) ncols++; if (ncols > 1) { // only valid when adding one column throw new SchemaException(Pass.FIRST, "Cannot specify FIRST or AFTER option with multicolumn ADD"); } } ArrayList<PEColumn> casted = new ArrayList<PEColumn>(); ArrayList<PEKey> keys = new ArrayList<PEKey>(); for(TableComponent nc : newColumns) { if (nc instanceof PEColumn) casted.add((PEColumn)nc); else if (nc instanceof PEKey) keys.add((PEKey)nc); } List<AlterTableAction> out = new ArrayList<AlterTableAction>(); if (!casted.isEmpty()) { out.add(new AddColumnAction(casted, firstOrAfterSpec)); } for(PEKey pek : keys) { out.add(new AddIndexAction(pek)); } return out; } @SuppressWarnings("unchecked") private PEColumn lookupAlteredColumn(Name columnName) { PEColumn c = null; final Table<?> parentTable = scope.getAlteredTable(); c = (PEColumn) parentTable.lookup(pc, columnName); if (c == null) { if (pc.getCapability() == Capability.PARSING_ONLY) { c = PEColumn.buildColumn(pc, columnName, TempColumnType.TEMP_TYPE, Collections.EMPTY_LIST, null,null); } else { throw new SchemaException(Pass.SECOND, "Unknown column '" + columnName + "' in '" + parentTable.getName().get() + "'"); } } return c; } /** * @param kt * @param keyName * @return */ @SuppressWarnings("unchecked") private PEKey lookupAlteredKey(ConstraintType kt, Name keyName) { PEKey k = null; if (pc.getCapability() == Capability.PARSING_ONLY) { k = new PEKey(keyName, IndexType.BTREE, Collections.EMPTY_LIST, null); } else { k = ((PETable)scope.getAlteredTable()).lookupKey(pc, keyName); if (k == null) throw new SchemaException(Pass.SECOND, "No such key: " + keyName); } return k; } @SuppressWarnings("unchecked") public TableKey lookupAlteredTable(Name tabName) { TableKey tab = null; if (pc.getCapability() == Capability.PARSING_ONLY) { tab = TableKey.make(pc,new PETable(pc, tabName, Collections.EMPTY_LIST, null, null, null),0); } else { TableResolver resolver = new TableResolver().withMTChecks() .withDatabaseFunction(new UnaryProcedure<Database<?>>() { @Override public void execute(Database<?> object) { if (!(object instanceof PEDatabase)) throw new SchemaException(Pass.SECOND, "Invalid database for table alter: '" + object.getName() + "'"); } }); TableInstance ti = resolver.lookupTable(pc, tabName, lockInfo); if (ti == null) throw new SchemaException(Pass.SECOND, "No such table: " + tabName.getSQL()); tab = ti.getTableKey(); } // now we have to push all the columns in case they are used pushScope(); scope.registerAlterColumns(pc,tab.getAbstractTable().asTable()); return tab; } public WhenClause buildWhenClause(ExpressionNode te, ExpressionNode re, Object orig) { return new WhenClause(te, re, SourceLocation.make(orig)); } public ExpressionNode buildCaseExpression(ExpressionNode te, ExpressionNode ee, List<WhenClause> whens, Object orig) { return new CaseExpression(te, ee, whens, SourceLocation.make(orig)); } public ExpressionNode buildCastCall(String castText, ExpressionNode toCast, String asText, Name tn) { return new CastFunctionCall(toCast, castText, tn, asText); } public Name buildCastToIntegralType(String signed, String integer) { if (integer == null) return new UnqualifiedName(signed); return new UnqualifiedName(signed + " " + integer); } public Name buildCastToSizedType(String theType, String theFirstSize, String theSecondSize) { if (theFirstSize == null && theSecondSize == null) return new UnqualifiedName(theType); StringBuilder buf = new StringBuilder(); buf.append(theType).append("(").append(theFirstSize); if (theSecondSize != null) buf.append(",").append(theSecondSize); buf.append(")"); return new UnqualifiedName(buf.toString()); } public Name buildCastToBinaryOrChar(String theType, String maybeFirstSize, Name maybeCharset) { StringBuilder buf = new StringBuilder(); buf.append(theType); if (maybeFirstSize != null) { buf.append("(").append(maybeFirstSize).append(")"); } else if (maybeCharset != null) { buf.append(" charset ").append(maybeCharset.getSQL()); } return new UnqualifiedName(buf.toString()); } public ExpressionNode buildCharCall(List<ExpressionNode> charCodes, Name outputEncoding) { return new CharFunctionCall(charCodes, outputEncoding); } public ExpressionNode buildRandCall(List<ExpressionNode> args) { if (args.isEmpty()) { return new RandFunctionCall(null); } else if (args.size() == 1) { return new RandFunctionCall(args.get(0)); } else { throw new SchemaException(new ErrorInfo(AvailableErrors.INCORRECT_PARAM_COUNT_FUNCTION_CALL,"RAND")); } } public ExpressionNode buildConvertCall(ExpressionNode toConvert, Name transcodingName, Type castToType) { Name rhs = transcodingName; if (castToType != null) { StringBuilder buf = new StringBuilder(); Singletons.require(DBNative.class).getEmitter().emitConvertTypeDeclaration(castToType, buf); rhs = new UnqualifiedName(buf.toString()); } return new ConvertFunctionCall(toConvert,rhs,(transcodingName != null)); } public ExpressionNode buildTimestampDiffCall(String unit, ExpressionNode param1, ExpressionNode param2) { FunctionName fn = new FunctionName("TIMESTAMPDIFF", TokenTypes.TIMESTAMPDIFF, false); ArrayList<ExpressionNode> actuals = new ArrayList<ExpressionNode>(); actuals.add(buildIdentifierLiteral(unit)); actuals.add(param1); actuals.add(param2); return buildFunctionCall(fn, actuals, null, null); } public ExpressionNode buildInterval(ExpressionNode intervalExpression, String unit) { return new IntervalExpression(intervalExpression,unit,null); } public ExpressionNode buildMultivalueExpression(final List<ExpressionNode> values) { if (values.size() == 1) { return addGrouping(values.get(0)); } return buildExpressionSet(values); } public ExpressionNode buildExpressionSet(List<ExpressionNode> values) { return new ExpressionSet(values, null); } public ExpressionNode buildGroupConcat(FunctionName fn, List<ExpressionNode> params, boolean distinct, String separator) { SetQuantifier sq = (distinct ? SetQuantifier.DISTINCT : null); return new GroupConcatCall(fn,params, sq, separator); } public Statement buildCreateTenantStatement(Name tenantName, Name onDB, String description) { PEDatabase peds = null; if (onDB == null) { peds = pc.findSingleMTDatabase(); if (peds == null) throw new SchemaException(Pass.SECOND, "No multitenant database found"); } if (peds == null) peds = pc.findPEDatabase(onDB); if (peds == null) throw new SchemaException(Pass.SECOND, "No such database: " + (onDB == null ? "(blank)" : onDB.getSQL())); if (!peds.getMTMode().isMT()) throw new SchemaException(Pass.SECOND, "Cannot create tenant on " + peds.getMTMode().describe() + " database " + (onDB == null ? "(blank)" : onDB.getSQL())); return pc.getPolicyContext() .buildCreateTenantStatement(peds, tenantName, (description == null ? null : PEStringUtils.dequote(description))); } public Statement buildSuspendTenantStatement(Name tenantName) { return pc.getPolicyContext().buildSuspendTenantStatement(tenantName); } public Statement buildResumeTenantStatement(Name tenantName) { return pc.getPolicyContext().buildResumeTenantStatement(tenantName); } public Statement buildDropTenantStatement(Name tenantName) { return pc.getPolicyContext().buildDropTenantStatement(tenantName, false); } public Name buildUnrestrictedName(List<Name> names) { ArrayList<UnqualifiedName> parts = new ArrayList<UnqualifiedName>(); for (Name n : names) { // should not happen if (n.isQualified()) throw new SchemaException(Pass.FIRST, "Qualified name found where unqualified name expected"); parts.add(n.getUnqualified()); } return new QualifiedName(parts); } public Pair<Name, LiteralExpression> buildConfigOption(Name key, ExpressionNode value) { return new Pair<Name, LiteralExpression>(key, asLiteral(value)); } private static final Name pluginKey = new UnqualifiedName("PLUGIN"); private static final Name activeKey = new UnqualifiedName("ACTIVE"); public Statement buildCreateDynamicSiteProvider(Name name, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("create a dynamic site provider"); // see if we have one already Provider extant = pc.getCatalog().findProvider(name.get()); if (extant != null) throw new SchemaException(Pass.SECOND, "Dynamic site provider " + name.getSQL() + " already exists"); String plugin = null; Boolean isActive = null; for (Iterator<Pair<Name, LiteralExpression>> iter = options.iterator(); iter .hasNext();) { Pair<Name, LiteralExpression> p = iter.next(); if (p.getFirst().getCapitalized().equals(pluginKey)) { plugin = p.getSecond().asString(pc.getValues()); iter.remove(); } else if (p.getFirst().getCapitalized().equals(activeKey)) { isActive = (Boolean) p.getSecond().getValue(pc.getValues()); iter.remove(); } } if (isActive == null) isActive = Boolean.TRUE; if (plugin == null) throw new SchemaException(Pass.SECOND, "Must specify plugin for CREATE DYNAMIC SITE PROVIDER"); PEProvider provider = new PEProvider(pc, name, plugin, isActive); return new PECreateGroupProviderStatement(provider, options); } public Statement buildAlterDynamicSiteProviderStatement(Name name, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("alter a dynamic site provider"); // see if we have one already Provider extant = pc.getCatalog().findProvider(name.get()); if (extant == null) throw new SchemaException(Pass.SECOND, "No such dynamic site provider: " + name.getSQL()); PEProvider pep = PEProvider.load(extant, pc); return new PEAlterGroupProviderStatement(pep, options); } public Statement buildShowDynamicSiteProvider(Name name, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("show a dynamic site provider"); // see if we have one already Provider extant = pc.getCatalog().findProvider(name.get()); if (extant == null) throw new SchemaException(Pass.SECOND, "No such dynamic site provider: " + name.getSQL()); List<CatalogEntity> ents = null; // build the command block, use null for the action SiteManagerCommand smc = new SiteManagerCommand(Command.SHOW, extant, PEGroupProviderDDLStatement.convertOptions(pc,options)); try { SiteProviderPlugin sm = SiteProviderFactory.getInstance(smc .getTarget().getName()); ents = sm.show(smc); } catch (PEException pe) { throw new SchemaException(Pass.SECOND, "Unable to query provider " + extant.getName(), pe); } return new SchemaQueryStatement(true, DYNAMIC_SITE_PROVIDER, ents, false, null); } public Statement buildShowDynamicSiteProvidersSites() { pc.getPolicyContext().checkRootPermission("show dynamic site providers sites"); // in this case, we just find all the providers, then route the // appropriate command to each provider, aggregate results, // etc. ListOfPairs<String, Object> options = new ListOfPairs<String, Object>(); // or whatever you like options.add("cmd", "sites"); ArrayList<CatalogEntity> results = new ArrayList<CatalogEntity>(); List<Provider> providers = pc.getCatalog().findAllProviders(); for (Provider p : providers) { try { SiteManagerCommand smc = new SiteManagerCommand(Command.SHOW, p, options); SiteProviderPlugin sm = SiteProviderFactory.getInstance(smc .getTarget().getName()); results.addAll(sm.show(smc)); } catch (PEException pe) { throw new SchemaException(Pass.SECOND, "Unable to query provider " + p.getName(), pe); } } return new SchemaQueryStatement(true, DYNAMIC_SITE_PROVIDER_SITES, results, false, null); } @SuppressWarnings("unchecked") public Statement buildDropDynamicSiteProviderStatement(Name name, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("drop a dynamic site provider"); // see if we have one already Provider extant = pc.getCatalog().findProvider(name.get()); if (extant == null) throw new SchemaException(Pass.SECOND, "No such dynamic site provider: " + name.getSQL()); PEProvider pep = PEProvider.load(extant, pc); return new PEDropGroupProviderStatement(pep, (options == null ? Collections.EMPTY_LIST : options)); } public PEPolicyClassConfig buildClassConfig(Name theClassName, ExpressionNode countLiteral, ExpressionNode providerLiteral, ExpressionNode poolLiteral) { PolicyClass theClass = PolicyClass.findSQL(theClassName.getUnqualified().getUnquotedName().get()); if (theClass == null) throw new SchemaException(Pass.SECOND, "No such policy type: " + theClassName); Integer theCount = null; String theProvider = null; String thePool = null; if (poolLiteral instanceof LiteralExpression) thePool = (String) ((LiteralExpression)poolLiteral).getValue(pc.getValues()); if (providerLiteral instanceof LiteralExpression) theProvider = (String) ((LiteralExpression)providerLiteral).getValue(pc.getValues()); if (countLiteral instanceof LiteralExpression) { LiteralExpression litex = (LiteralExpression) countLiteral; Number n = (Number) litex.getValue(pc.getValues()); theCount = n.intValue(); } return new PEPolicyClassConfig(theClass, theProvider, thePool, (theCount == null ? 0 : theCount)); } public Statement buildCreateDynamicSitePolicyStatement(Name name, boolean strict, List<PEPolicyClassConfig> classes) { if ( name == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } pc.getPolicyContext().checkRootPermission("create a dynamic site policy"); DynamicPolicy dp = pc.getCatalog().findPolicy(name.get()); if (dp != null) throw new SchemaException(Pass.SECOND, "Dynamic site policy " + name.getSQL() + " already exists"); PEPolicy pep = new PEPolicy(pc, name, strict, classes); return new PECreateStatement<PEPolicy, DynamicPolicy>(pep, true, DYNAMIC_SITE_POLICY_TAG, false); } public Statement buildAlterDynamicSitePolicyStatement(Name name, Name newName, Boolean newStrict, List<PEPolicyClassConfig> configs) { pc.getPolicyContext().checkRootPermission("alter a dynamic site policy"); DynamicPolicy dp = pc.getCatalog().findPolicy(name.get()); if (dp == null) throw new SchemaException(Pass.SECOND, "No such dynamic site policy: " + name.getSQL()); PEPolicy pep = PEPolicy.load(dp, pc); return new PEAlterPolicyStatement(pep, newName, newStrict, configs); } @SuppressWarnings({ "unchecked", "rawtypes" }) public Statement buildDropDynamicSitePolicyStatement(Name name) { pc.getPolicyContext().checkRootPermission("drop a dynamic site policy"); DynamicPolicy dp = pc.getCatalog().findPolicy(name.get()); if (dp == null) throw new SchemaException(Pass.SECOND, "Dynamic site policy " + name.getSQL() + " does not exist"); PEPolicy pep = PEPolicy.load(dp, pc); return new PEDropStatement(PEPolicy.class, null, true, pep, "DYNAMIC SITE POLICY"); } public Statement buildCreateExternalServiceStatement(Name name, List<Pair<Name, LiteralExpression>> options) { if ( name == null ) { throw new SchemaException(Pass.FIRST, MISSING_UNQUALIFIED_IDENTIFIER_ERROR_MSG); } // check permissions pc.getPolicyContext().checkRootPermission("create an external service"); ExternalService es = pc.getCatalog().findExternalService( name.getUnqualified().get()); if (es != null) throw new SchemaException(Pass.SECOND, "External Service " + name.getSQL() + " already exists."); return new PECreateExternalServiceStatement(new PEExternalService( pc, name, options), true, "EXTERNAL SERVICE", false); } public Statement buildDropExternalServiceStatement(Name name) { pc.getPolicyContext().checkRootPermission("drop an external service"); ExternalService es = pc.getCatalog().findExternalService( name.getUnqualified().get()); if (es == null) throw new SchemaException(Pass.SECOND, "No such external service: " + name.getSQL()); return new PEDropExternalServiceStatement(PEExternalService.class, null, true, new PEExternalService(pc, es), "EXTERNAL SERVICE"); } public Statement buildAlterExternalServiceStatement(Name name, List<Pair<Name, LiteralExpression>> options) { pc.getPolicyContext().checkRootPermission("alter an external service"); ExternalService es = pc.getCatalog().findExternalService( name.getUnqualified().get()); if (es == null) throw new SchemaException(Pass.SECOND, "No such external service: " + name.getSQL()); return new PEAlterExternalServiceStatement(new PEExternalService(pc, es, options)); } public Statement buildStartExternalServiceStatement(Name name) { pc.getPolicyContext().checkRootPermission("start an external service"); ExternalService es = pc.getCatalog().findExternalService( name.getUnqualified().get()); if (es == null) throw new SchemaException(Pass.SECOND, "No such external service: " + name.getSQL()); return new ExternalServiceControlStatement(ExternalServiceControlStatement.Action.START, es); } public Statement buildStopExternalServiceStatement(Name name) { pc.getPolicyContext().checkRootPermission("stop an external service"); ExternalService es = pc.getCatalog().findExternalService( name.getUnqualified().get()); if (es == null) throw new SchemaException(Pass.SECOND, "No such external service: " + name.getSQL()); return new ExternalServiceControlStatement(ExternalServiceControlStatement.Action.STOP, es); } public Statement buildCreateContainerStatement(Name containerName, Name groupName, Pair<DistributionVector.Model, UnqualifiedName> backingModel, Boolean ifNotExists) { // // check permissions // pc.getPolicyContext().checkRootPermission("create a container"); Container container = pc.getCatalog().findContainer(containerName.getUnqualified().get()); if (container != null) { if (Boolean.TRUE.equals(ifNotExists)) { // just do an empty statement return new EmptyStatement("Create existing container"); } throw new SchemaException(Pass.SECOND, "Container " + containerName.getSQL() + " already exists."); } PEPersistentGroup pesg = null; if (groupName != null) { pesg = pc.findStorageGroup(groupName); if (pesg == null) throw new SchemaException(Pass.SECOND, "No such persistent group: " + groupName.getSQL()); } if (backingModel == null) { throw new SchemaException(Pass.SECOND, "Missing distribution declaration on container " + containerName.getSQL()); } DistributionVector.Model model = backingModel.getFirst(); RangeDistribution rd = null; if (model == DistributionVector.Model.RANGE) { UnqualifiedName rangeName = backingModel.getSecond(); rd = pc.findRange(rangeName, groupName); if (rd == null) throw new SchemaException(Pass.SECOND, "No such range on group " + groupName + ": " + rangeName.getSQL()); } return new PECreateStatement<PEContainer, Container>(new PEContainer( pc, containerName, pesg, model, rd), true, "CONTAINER", false); } public Statement buildDropContainerStatement(Name name, Boolean ifExists) { pc.getPolicyContext().checkRootPermission("drop a container"); PEContainer cont = pc.findContainer(name); if (cont == null) { if (Boolean.TRUE.equals(ifExists)) { return buildEmptyStatement("Drop a non existing container."); } throw new SchemaException(Pass.SECOND, "No such container: " + name.getSQL()); } return new PEDropContainerStatement(cont); } // move this somewhere else public Statement buildUseContainerStatement(Name containerName, List<Pair<LiteralExpression,Name>> values) { for(Pair<LiteralExpression,Name> val : values) { LiteralExpression expr = val.getFirst(); if (expr.isNullLiteral()) { if (values.size() == 1) { return new UseContainerStatement(pc,true); } throw new SchemaException(Pass.SECOND, "NULL context cannot be specified with container discriminant."); } else if (expr.isGlobalLiteral()) { if (values.size() == 1) { return new UseContainerStatement(pc,false); } throw new SchemaException(Pass.SECOND, "GLOBAL context cannot be specified with container discriminant."); } } PEContainer cont = pc.findContainer(containerName); if (cont == null) throw new SchemaException(Pass.SECOND, "No such container: " + containerName.getSQL()); PETable bt = cont.getBaseTable(pc); if (bt == null) throw new SchemaException(Pass.SECOND, "Unable to use container " + containerName.getSQL() + ": no discriminator set"); List<PEColumn> discCols = cont.getDiscriminantColumns(pc); if (discCols.size() != values.size()) throw new SchemaException(Pass.SECOND, "Invalid discriminant value. Found " + values.size() + " columns but require " + discCols.size() + " columns"); return new UseContainerStatement(pc,cont,bt,discCols,values,false); } public Parameter buildParameter(Token tree) { Parameter param = new Parameter(SourceLocation.make(tree)); parameters.add(param); return param; } public Statement buildShowProcesslistStatement(Boolean full) { return new ShowProcesslistStatement(full); } public Statement buildShowPassthroughStatement(String command) { if (StringUtils.equals(command, "PLUGINS")) { return new ShowPassthroughStatement(PassThroughCommandType.PLUGINS, false, null); } else if (StringUtils.equals(command, "MASTER LOGS")) { return new ShowPassthroughStatement(PassThroughCommandType.MASTERLOGS, false, null); } else if (StringUtils.equals(command, "MASTER STATUS")) { return new ShowPassthroughStatement(PassThroughCommandType.MASTERSTATUS, false, null); } else if (StringUtils.equals(command, "SLAVE STATUS")) { return new ShowPassthroughStatement(PassThroughCommandType.SLAVESTATUS, false, null); } else if (StringUtils.equals(command, "GRANTS")) { return new ShowPassthroughStatement(PassThroughCommandType.GRANTS, false, null); } throw new SchemaException(Pass.SECOND, "No SHOW support for command '" + command + "'"); } public GrantScope buildGrantScope(Name n) { if (n == null) return new GrantScope(); // otherwise it's a db.* or db.table // the grant scope might be a tenant if (pc.getCapability() == Capability.PARSING_ONLY) return new GrantScope(n); PEDatabase peds = pc.findPEDatabase(n); PETenant ten = null; if (peds == null) ten = pc.findTenant(n); if (peds == null && ten == null) throw new SchemaException(Pass.SECOND, "No such database: " + n.getSQL()); if (peds != null) return new GrantScope(peds); return new GrantScope(ten); } /** * @param gs * @param user * @param whatToGrant * @return */ public Statement buildGrant(GrantScope gs, PEUser user, String whatToGrant) { if (pc.getCapability() == Capability.PARSING_ONLY) { return new GrantStatement(gs.buildPriviledge(pc,user)); } pc.getPolicyContext().checkRootPermission("grant privileges"); // we're going to use the PEUser bit to look up the actual user PEUser actualUser = pc.findUser(user.getUserScope().getUserName(), user .getUserScope().getScope()); if (actualUser == null) actualUser = user; // so, a grant statement always creates a priv, but optionally creates the user return new GrantStatement(gs.buildPriviledge(pc,actualUser)); } public Statement buildFlushPrivileges() { return new FlushPrivilegesStatement(); } protected DelegatingLiteralExpression replaceLiteral(DelegatingLiteralExpression oldLiteral, int valueType, SourceLocation sloc, Object value) { literals.remove(oldLiteral.getPosition()); DelegatingLiteralExpression dle = new DelegatingLiteralExpression(valueType,sloc,this,oldLiteral.getPosition(),oldLiteral.getCharsetHint()); literals.add(oldLiteral.getPosition(),new Pair<DelegatingLiteralExpression,Object>(dle,value)); return dle; } public ExpressionNode maybeNegate(Token tok, ExpressionNode rhs) { if (tok == null) return rhs; FunctionName fn = buildFunctionName(tok, true); ArrayList<ExpressionNode> parts = new ArrayList<ExpressionNode>(1); parts.add(rhs); return buildFunctionCall(fn, parts, null, null); } public ExpressionNode buildFunctionOrIdentifier(Name name, SetQuantifier sq, List<ExpressionNode> params, Token asterisk, Object loc, Token leftParen, Token rightParen) { SourceLocation sloc = SourceLocation.make(loc); if (sq == null && params == null && asterisk == null && leftParen==null && rightParen==null) { if (opts.isResolve()) return buildColumnReference(name); return new NameInstance(name, sloc); } UnqualifiedName unq = name.getUnqualified(); FunctionName fn = new FunctionName(unq, false); if (sq != null && !fn.isAggregate()) throw new SchemaException(Pass.FIRST, "Illegal use of set quantifier"); else if (asterisk != null && !fn.isCount()) throw new SchemaException(Pass.FIRST, "Illegal use of '*' in a function"); else if (asterisk != null && (params != null) && !params.isEmpty()) throw new SchemaException(Pass.FIRST, "Mixed use of parameters and '*'"); ArrayList<ExpressionNode> actuals = new ArrayList<ExpressionNode>(); if (asterisk != null) actuals.add(new Wildcard(SourceLocation.make(asterisk))); else { if (params != null) { actuals.addAll(params); } } return buildFunctionCall(fn, actuals, sq, sloc); } public ExpressionNode maybeBuildExprAlias(ExpressionNode targ, Name alias, String stringAlias, Object tree) { if (alias == null && stringAlias == null) return targ; if (targ instanceof NameInstance && alias != null) { // Don't allow an alias to be the same name as the column // This will cause a stack overflow because the column will refer to an alias // which refers back to the column. NameInstance colName = (NameInstance)targ; if (colName.getName().get().equals(alias.get())) { return targ; } } Alias a = null; if (alias != null) a = new NameAlias(castAlias(alias)); else if (stringAlias != null) a = new StringLiteralAlias(PEStringUtils.dequote(stringAlias)); return scope.buildExpressionAlias(targ, a, SourceLocation.make(tree)); } public ProjectingStatement maybeBuildUnionStatement(List<ProjectingStatement> stmts, List<Boolean> unionOps) { ProjectingStatement singleReturn = null; if (stmts.size() == 1) singleReturn = stmts.get(0); else { // working from the back of the stmts list, build union clauses until // there is only one stmt left for (int op = unionOps.size() - 1; op > -1; op--) { ProjectingStatement rhs = stmts.get(op + 1); ProjectingStatement lhs = stmts.get(op); Boolean variety = unionOps.get(op); UnionStatement nus = new UnionStatement(lhs, rhs, variety.booleanValue(),lhs.getSourceLocation()); stmts.remove(op + 1); stmts.remove(op); stmts.add(op, nus); nus.getDerivedInfo().addNestedStatements( Arrays.asList(new ProjectingStatement[] { lhs, rhs })); } singleReturn = stmts.get(0); } return singleReturn; } public Statement buildShowErrorsOrWarnings(String typeTag, LimitSpecification limit) { return new ShowErrorsWarningsStatement(typeTag, limit); } public Statement buildShowSitesStatus() { return new ShowSitesStatusStatement(); } @SuppressWarnings("unchecked") public Statement buildShowStatus(Pair<ExpressionNode, ExpressionNode> likeOrWhere) { DirectShowStatusInformation ist = (DirectShowStatusInformation) Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName("status")); ExpressionNode likeExpr = (likeOrWhere == null ? null : likeOrWhere .getFirst()); ExpressionNode whereExpr = (likeOrWhere == null ? null : likeOrWhere .getSecond()); if (pc.getCapability() == Capability.PARSING_ONLY) return new SchemaQueryStatement(false, "status", Collections.EMPTY_LIST, true, null); return ist.buildShowPlural(pc, null, likeExpr, whereExpr, null); } @SuppressWarnings("unchecked") public Statement buildShowVariables(VariableScope ivs, Pair<ExpressionNode, ExpressionNode> likeOrWhere) { DirectShowVariablesTable ist = (DirectShowVariablesTable) Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName("variables")); VariableScope vs = ivs; if (vs == null) vs = new VariableScope(VariableScopeKind.SESSION); ExpressionNode likeExpr = (likeOrWhere == null ? null : likeOrWhere .getFirst()); ExpressionNode whereExpr = (likeOrWhere == null ? null : likeOrWhere .getSecond()); if (pc.getCapability() == Capability.PARSING_ONLY) return new SchemaQueryStatement(false, "variables", Collections.EMPTY_LIST, true, null); return ist.buildShow(pc, vs, likeExpr, whereExpr); } public Statement buildShowColumns(String onInfoSchemaTable, List<Name> scoping, Pair<ExpressionNode, ExpressionNode> likeOrWhere, Token full) { if (pc.getCapability() == Capability.PARSING_ONLY && !pc.getCatalog().isPersistent()) return new EmptyStatement("no catalog queries with transient execution engine"); ShowSchemaBehavior ist = Singletons.require(InformationSchemaService.class).lookupShowTable(new UnqualifiedName(onInfoSchemaTable)); if (ist == null) throw new MigrationException("Need to add info schema table for " + onInfoSchemaTable); ExpressionNode likeExpr = (likeOrWhere == null ? null : likeOrWhere .getFirst()); ExpressionNode whereExpr = (likeOrWhere == null ? null : likeOrWhere .getSecond()); ShowOptions so = new ShowOptions(); if (full != null) so.withFull(); // break up the scoping value into parts if qualified List<Name> scopingParts = new ArrayList<Name>(); for (Name name : scoping) { if (name instanceof QualifiedName) { QualifiedName qn = (QualifiedName)name; scopingParts.add(qn.getParts().get(1)); // table name scopingParts.add(qn.getParts().get(0)); // database } else { // assumed UnqualifiedName scopingParts.add(name); } } return ist.buildShowPlural(pc, scopingParts, likeExpr, whereExpr, so); } public List<AlterTableAction> buildEnableKeysAction() { return wrapAlterAction(new ChangeKeysStatusAction(true)); } public List<AlterTableAction> buildDisableKeysAction() { return wrapAlterAction(new ChangeKeysStatusAction(false)); } public Statement buildShowMultitenantLocks() { return new SchemaQueryStatement(true,"multitenant locks", Singletons.lookup(LockManager.class).showState()); } public Statement buildMaintenanceQuery(String operation, String option, List<Name> tables) { MaintenanceCommandType maintenanceCommand = MaintenanceCommandType.valueOf(operation.toUpperCase()); if (maintenanceCommand == null) { throw new SchemaException(Pass.FIRST, "Invalid table maintenance command: " + operation); } MaintenanceOptionType maintenanceOption = MaintenanceOptionType.NONE; if (option != null) { maintenanceOption = MaintenanceOptionType.valueOf(option.toUpperCase()); if (maintenanceOption == null) { throw new SchemaException(Pass.FIRST, "Invalid table maintenance option: " + option); } } List<TableInstance> tblInstances = lookupTables(tables); // restrict maintenance commands to a single Persistent Group Name persistentGroup = tblInstances.get(0).getAbstractTable().getPersistentStorage(pc).getName(); for (TableInstance tableInstance : tblInstances) { if (! persistentGroup.equals(tableInstance.getAbstractTable().getPersistentStorage(pc).getName())) { throw new SchemaException(Pass.FIRST, "Table '" + tableInstance.getAbstractTable().getName(pc,pc.getValues()).get() + "' in maintenance command is not in Persistent Group '" + persistentGroup.get() + "'"); } } if (MaintenanceCommandType.ANALYZE == maintenanceCommand) return new AnalyzeTablesStatement(maintenanceOption, tblInstances); return new TableMaintenanceStatement(maintenanceCommand, maintenanceOption, tblInstances); } private List<TableInstance> lookupTables(List<Name> tableNames) { List<TableInstance> tblInstances = new ArrayList<TableInstance>(); TableResolver resolver = new TableResolver().withMTChecks(); // figure out whether the target table(s) are known or not for (Name targetTableName : tableNames) { /* Database<?> db = null; UnqualifiedName candidateName = null; if (targetTableName.isQualified()) { QualifiedName qn = (QualifiedName) targetTableName; UnqualifiedName ofdb = qn.getNamespace(); candidateName = qn.getUnqualified(); db = pc.findPEDatabase(ofdb); if (db == null) throw new SchemaException(Pass.FIRST, "No such database: " + ofdb); } else { db = pc.getCurrentDatabase(); if (db == null) throw new SchemaException(Pass.FIRST, "No current database"); candidateName = (UnqualifiedName) targetTableName; } TableInstance ti = db.getSchema().buildInstance(pc, candidateName, lockInfo, true); */ TableInstance ti = resolver.lookupTable(pc, targetTableName, lockInfo); if (ti == null) throw new SchemaException(Pass.FIRST, "No such table: " + targetTableName); tblInstances.add(ti.adapt(targetTableName.getUnqualified(), null, ti.getNode(), false)); } return tblInstances; } public Statement buildAnalyzeKeys(List<Name> names) { return new AnalyzeKeysStatement(lookupTables(names)); } @Override public Object getValue(IParameter p) { throw new SchemaException(Pass.SECOND, "Attempt to access parameter value from non value source"); } @Override public Object getLiteral(IDelegatingLiteralExpression dle) { return literals.get(dle.getPosition()).getSecond(); } @Override public Object getTenantID() { throw new SchemaException(Pass.SECOND, "Attempt to access tenant id value from non tenant id source"); } @Override public Object getAutoincValue(IAutoIncrementLiteralExpression exp) { throw new SchemaException(Pass.SECOND, "Attempt to access autoinc value before planning"); } public LoadDataInfileColOption buildLoadDataInfileColOption(String terminated, String enclosed, String escaped) { LoadDataInfileColOption option = new LoadDataInfileColOption(terminated, enclosed, escaped); return option; } public LoadDataInfileLineOption buildLoadDataInfileLineOption(String starting, String terminated) { LoadDataInfileLineOption option = new LoadDataInfileLineOption(starting, terminated); return option; } public Statement buildLoadDataInfileStatement( LoadDataInfileModifier modifier, boolean local, String fileName, boolean replace, boolean ignore, Name tempTableName, Name characterSet, LoadDataInfileColOption colOption, LoadDataInfileLineOption lineOption, ExpressionNode ignoredLines, List<Name> colOrVarList, List<ExpressionNode> updateExprs) { // validate this table is a qualified table or has a current database set Name dbName = null; Name tableName = tempTableName; if (tableName.isQualified()) { dbName = ((QualifiedName)tempTableName).getNamespace(); tableName = ((QualifiedName)tempTableName).getUnqualified(); } if (dbName == null) { dbName = (pc.getCurrentDatabase() == null) ? null : pc.getCurrentDatabase().getName(); } if (dbName == null) { throw new SchemaException(Pass.FIRST, "No database has been specified."); } PEDatabase db = pc.findPEDatabase(dbName); if (db == null) { throw new SchemaException(Pass.FIRST, "No such database: '" + dbName + "'"); } if (colOption != null) { if ((colOption.getEnclosed() != null) && (colOption.getEnclosed().length() > 1)) { throw new SchemaException(Pass.FIRST, "Fields enclosed by must be a single character. Value is '" + colOption.getEnclosed() + "'"); } if ((colOption.getEscaped() != null) && (colOption.getEscaped().length() > 1)) { throw new SchemaException(Pass.FIRST, "Fields escaped by must be a single character. Value is '" + colOption.getEscaped() + "'"); } } if (replace) { throw new SchemaException(Pass.FIRST, "REPLACE option is not currently supported."); } if (ignoredLines != null) { throw new SchemaException(Pass.FIRST, "Ignoring lines is not currently supported."); } if (updateExprs != null) { throw new SchemaException(Pass.FIRST, "SET option is not currently supported."); } LoadDataInfileStatement stmt = new LoadDataInfileStatement(modifier, local, PEStringUtils.dequote(fileName), replace, ignore, db, tableName, characterSet, colOption, lineOption, (ignoredLines == null) ? null : asIntegralLiteral(ignoredLines), colOrVarList, updateExprs); return stmt; } private static String[] unpackXmlParams(List<Pair<String,String>> params, String[] tags) { String[] out = new String[tags.length]; for(Pair<String,String> p : params) { String t = p.getFirst(); for(int i = 0; i < tags.length; i++) { if (tags[i].equals(t)) { if (out[i] != null) throw new SchemaException(Pass.SECOND, "Duplicate " + t + " specified"); if (p.getSecond() != null) out[i] = PEStringUtils.dequote(p.getSecond()); } } } return out; } // 0=xml, 1=match, 2=comment private String[] unpackTemplateParams(List<Pair<String,String>> params) { return unpackXmlParams(params,new String[] { "xml", "match", "comment" }); } public Statement buildCreateTemplateStatement(Name name, boolean ine, List<Pair<String,String>> params) { PETemplate pet = pc.findTemplate(name); if (pet != null) { if (ine) return buildEmptyStatement("create existing template"); throw new SchemaException(Pass.SECOND, "Template " + name.getSQL() + " already exists"); } String[] p = unpackTemplateParams(params); if (p[0] == null) throw new SchemaException(Pass.SECOND, "Must specify template body for add template"); pet = new PETemplate(pc,name.getUnqualified(),p[0],p[1],p[2]); return new PECreateStatement<PETemplate,PersistentTemplate>(pet,true,"TEMPLATE",false); } public Statement buildAlterTemplateStatement(Name name, List<Pair<String,String>> params) { PETemplate pet = pc.findTemplate(name); if (pet == null) throw new SchemaException(Pass.SECOND, "Template " + name.getSQL() + " does not exist"); String[] p = unpackTemplateParams(params); return new PEAlterTemplateStatement(pet,p[0],p[1],p[2]); } public Statement buildDropTemplateStatement(Name name, boolean ifExists) { PETemplate pet= pc.findTemplate(name); if (pet == null) { if (!ifExists) throw new SchemaException(Pass.SECOND, "Template " + name.getSQL() + " does not exist"); return new PEDropStatement<PETemplate,PersistentTemplate>(PETemplate.class,true,true,name,"TEMPLATE"); } return new PEDropStatement<PETemplate,PersistentTemplate>(PETemplate.class,ifExists,true,pet,"TEMPLATE"); } // 0=xml, 1=comment, 2=enabled private String[] unpackRawPlanParams(List<Pair<String,String>> params) { return unpackXmlParams(params,new String[] { "xml", "comment", "enabled" }); } public Statement buildCreateRawPlanStatement(Name name, boolean ine, Name dbName, List<Pair<String,String>> params) { PERawPlan perp = pc.findRawPlan(name); if (perp != null) { if (ine) return buildEmptyStatement("create existing plan"); throw new SchemaException(Pass.SECOND, "Raw plan " + name + " already exists"); } PEDatabase pdb = pc.findPEDatabase(dbName); if (pdb == null) throw new SchemaException(Pass.SECOND, "No such database: " + dbName.getSQL()); // unpack the params String[] fields = unpackRawPlanParams(params); perp = new PERawPlan(pc,name.getUnqualified(),pdb,fields[0],(fields[2] == null ? true : Boolean.valueOf(fields[2])),fields[1]); return new PECreateRawPlanStatement(perp,ine); } public Statement buildDropRawPlanStatement(Name name, boolean ie) { PERawPlan perp = pc.findRawPlan(name); if (perp == null) { if (ie) return buildEmptyStatement("drop nonexistent raw plan"); throw new SchemaException(Pass.SECOND, "Raw plan " + name + " does not exist"); } return new PEDropRawPlanStatement(perp,ie); } public Statement buildAlterRawPlanStatement(Name name, List<Pair<String,String>> params) { PERawPlan perp = pc.findRawPlan(name); if (perp == null) throw new SchemaException(Pass.SECOND,"No such raw plan: " + name); // 0=xml, 1=comment, 2=enabled String[] fields = unpackRawPlanParams(params); return new PEAlterRawPlanStatement(perp,fields[1],(fields[2] == null ? null : Boolean.valueOf(fields[2])),fields[0]); } public Statement buildCreateViewStatementKern(Name viewName, ProjectingStatement viewDef, List<UnqualifiedName> columnNames, String checkOption, List<TableComponent<?>> colDefs) { return PECreateViewStatement.build(pc,viewName,viewDef,columnNames,checkOption,colDefs); } public Statement buildDropViewStatement(Name viewName, boolean ifExists) { if (pc.getCapability() == Capability.PARSING_ONLY) return new PEDropViewStatement(viewName, ifExists); UnqualifiedName tableName = viewName.getUnqualified(); Database<?> ondb = findDatabase(viewName); if (ondb == null) throw new SchemaException(Pass.SECOND, "No such database: '" + viewName + "'"); if (!(ondb instanceof PEDatabase)) throw new SchemaException(Pass.SECOND, "Invalid database for drop view: '" + ondb.getName() + "'"); PEAbstractTable<?> exists = pc.findTable(PEAbstractTable.getTableKey((PEDatabase)ondb, tableName)); if (exists == null) { if (!ifExists) throw new SchemaException(Pass.SECOND, "No such view: " + viewName); return new PEDropViewStatement(viewName,ifExists); } return new PEDropViewStatement(exists.asView(),ifExists); } public Statement buildDropTriggerStatement(Name triggerName, boolean ifExists) { try { final Database<?> parentSchema = getDatabaseForObject(pc, triggerName); final PETrigger trigger = PETrigger.lookup(pc, triggerName, parentSchema); if (trigger != null) { return new PEDropTriggerStatement(trigger, ifExists); } else if (Boolean.TRUE.equals(ifExists)) { return new EmptyStatement("drop nonexistent trigger", StatementType.DROP_TRIGGER); } } catch (final SchemaException e) { final ErrorInfo error = e.getErrorInfo(); if (!AvailableErrors.UNKNOWN_DATABASE.equals(error.getCode())) { throw e; } } throw new SchemaException(new ErrorInfo(AvailableErrors.TRG_DOES_NOT_EXIST)); } public Statement buildPreparePreparedStatement(Name pstmtName, String stmt) { return new PreparePStmtStatement(pstmtName.getUnqualified(), PEStringUtils.dequote(stmt)); } public Statement buildExecutePreparedStatement(Name pstmtName, List<VariableInstance> vars) { return new ExecutePStmtStatement(pstmtName.getUnqualified(), vars); } public Statement buildDeallocatePreparedStatement(Name pstmtName) { return new DeallocatePStmtStatement(pstmtName.getUnqualified()); } public Statement buildShowPlanCache(boolean statsToo) { pc.getPolicyContext().checkRootPermission("show plan cache"); return new ShowPlanCacheStatement(statsToo); } public Statement buildXACommit(UserXid xid, Object onePhase) { return new XACommitTransactionStatement(xid, onePhase != null); } public Statement buildXAEnd(UserXid xid, Object suspend, Object forMigrate) { if (forMigrate != null) throw new SchemaException(Pass.SECOND, "No support for xa end suspend for migrate"); if (suspend != null) throw new SchemaException(Pass.SECOND, "No support for xa end suspend"); return new XAEndTransactionStatement(xid); } public Statement buildXAPrepare(UserXid xid) { return new XAPrepareTransactionStatement(xid); } public Statement buildXARecover() { return new XARecoverTransactionStatement(); } public Statement buildXARollback(UserXid xid) { return new XARollbackTransactionStatement(xid); } public Statement buildXAStart(UserXid xid, Object join, Object resume) { if (join != null) throw new SchemaException(Pass.SECOND, "No support for xa start join"); if (resume != null) throw new SchemaException(Pass.SECOND, "No support for xa start resume"); return new XABeginTransactionStatement(xid); } public UserXid buildXAXid(String gtrid, String bqual, String formatID) { // not quite right yet - we should convert based on the type of the literal return new UserXid(gtrid,bqual,formatID); } public void pushDML(String reason) { if (lockInfo == null && (opts != null && !opts.isIgnoreLocking())) { com.tesora.dve.lockmanager.LockType lt = StatementTraits.getLockType(reason); if (lt == null) throw new SchemaException(Pass.FIRST, "Missing lock type for dml reason " + reason); lockInfo = new LockInfo(lt,reason); } } public NativeCharSetCatalog getNativeCharSetCatalog() { if (supportedCharSets == null) { supportedCharSets = Singletons.require(NativeCharSetCatalog.class); } return supportedCharSets; } public NativeCollationCatalog getNativeCollationCatalog() { if (supportedCollations == null) { supportedCollations = Singletons.require(NativeCollationCatalog.class); } return supportedCollations; } public Statement buildCompoundStatement(List<Statement> stmts) { return new CompoundStatementList(null,stmts); } public Statement buildCreateTrigger(Name triggerName, boolean isBefore, TriggerEvent triggerType, PETable targetTable, Statement body,Token triggerToken) { PETrigger already = pc.findTrigger(PETrigger.buildCacheKey(triggerName.getUnquotedName().get(), (TableCacheKey) targetTable.getCacheKey())); if (already != null) { throw new SchemaException(new ErrorInfo(AvailableErrors.TRG_ALREADY_EXISTS)); } else if (((triggerType == TriggerEvent.UPDATE) || (triggerType == TriggerEvent.DELETE)) && !targetTable.hasUniqueKey(pc)) { final String targetTableName = targetTable.getName().getUnquotedName().get(); throw new SchemaException(new ErrorInfo(AvailableErrors.NO_UNIQUE_KEY_ON_TRG_TARGET, targetTableName)); } final EarlyTriggerTableCollector collector = TriggerTableInstance.collectTriggerTableReferences(body); if ((triggerType == TriggerEvent.INSERT) && collector.hasBeforeColumns()) { throw new SchemaException(new ErrorInfo(AvailableErrors.NO_SUCH_ROW_IN_TRG, TriggerTime.BEFORE.getAlias().get(), TriggerEvent.INSERT.toString())); } else if ((triggerType == TriggerEvent.DELETE) && collector.hasAfterColumns()) { throw new SchemaException(new ErrorInfo(AvailableErrors.NO_SUCH_ROW_IN_TRG, TriggerTime.AFTER.getAlias().get(), TriggerEvent.DELETE.toString())); } String origStmt = getInputSQL(); int l = triggerToken.getCharPositionInLine(); String rawSQL = origStmt.substring(l); String charset = KnownVariables.CHARACTER_SET_CLIENT.getSessionValue(pc.getConnection().getVariableSource()).getName(); String collation = KnownVariables.COLLATION_CONNECTION.getSessionValue(pc.getConnection().getVariableSource()); String collationDB = KnownVariables.COLLATION_DATABASE.getSessionValue(pc.getConnection().getVariableSource()); SQLMode sqlMode = KnownVariables.SQL_MODE.getSessionValue(pc.getConnection().getVariableSource()); SQLMode globalMode = KnownVariables.SQL_MODE.getGlobalValue(pc.getConnection().getVariableSource()); if (globalMode.equals(sqlMode)) sqlMode = null; PETrigger trig = new PETrigger(pc,triggerName,targetTable,body,triggerType, null /* PEUser user */, collation, charset, collationDB, isBefore ? TriggerTime.BEFORE : TriggerTime.AFTER, sqlMode,rawSQL); popScope(); opts = opts.setResolve(); return new PECreateTriggerStatement(trig); } public Statement addViewTriggerFields(Statement in, boolean createOrReplace, String algo, UserScope definer, String security) { PECreateStatement pect = (PECreateStatement) in; Persistable pt = pect.getCreated(); PEUser user = null; if (definer == null || pc.getOptions().isIgnoreMissingUser()) user = pc.getCurrentUser().get(pc); else { user = pc.findUser(definer.getUserName(), definer.getScope()); if (user == null) // apparently it is legal to specify a user that doesn't exist. in that case, just use the current user user = pc.getCurrentUser().get(pc); // throw new SchemaException(Pass.SECOND, "No such user: " + definer.getSQL()); } if (pt instanceof PEView) { PECreateViewStatement cvs = (PECreateViewStatement) pect; PEView pev = (PEView) pt; pev.setUser(pc, user, false); PEDatabase theDB = cvs.getDatabase(pc); PEAbstractTable<?> existing = pc.findTable(PEAbstractTable.getTableKey(theDB, pev.getName())); if (existing != null && !createOrReplace) { throw new SchemaException(Pass.SECOND, "View " + pev.getName() + " already exists"); } if (createOrReplace) { ((PECreateViewStatement)pect).setCreateOrReplace(); } String algorithm = (algo == null ? "UNDEFINED" : algo); String sec = (security == null ? "DEFINER" : security); pev.setAlgorithm(algorithm); pev.setSecurity(sec); } else { PETrigger trig = (PETrigger) pt; trig.setUser(pc,user); if (createOrReplace || algo != null || security != null) { // TODO: // come back and put in the right error message throw new SchemaException(Pass.FIRST, "Illegal syntax"); } } return pect; } public PETable pushTriggerTable(Name n) { TableInstance targTab = basicResolver.lookupTable(pc, n, lockInfo); PETable theTable = targTab.getAbstractTable().asTable(); long node = pc.getNextTable(); TriggerTableInstance before = new TriggerTableInstance(theTable,node,TriggerTime.BEFORE); TriggerTableInstance after = new TriggerTableInstance(theTable,node,TriggerTime.AFTER); pushScope(); scope.insertTable(before); scope.insertTable(after); opts = opts.clearSetting(Option.RESOLVE); return theTable; } public PETable pushTriggerTable(PETable tab) { // we always use the same node number for trigger tables so that // we can correctly plan when there are both before and after triggers long node = -1; TriggerTableInstance before = new TriggerTableInstance(tab,node,TriggerTime.BEFORE); TriggerTableInstance after = new TriggerTableInstance(tab,node,TriggerTime.AFTER); pushScope(); scope.insertTable(before); scope.insertTable(after); return tab; } public Statement buildCaseStatement(ExpressionNode testExpr, List<StatementWhenClause> whenClauses, Statement elseStatement) { return new CaseStatement(null,testExpr, whenClauses, elseStatement); } public StatementWhenClause buildStatementWhenClause(ExpressionNode testExpr, Statement result) { return new StatementWhenClause(testExpr,result,null); } }