/** * Bind this LockTableNode. This means looking up the table, verifying it exists and getting the * heap conglomerate number. * * @exception StandardException Thrown on error */ public void bindStatement() throws StandardException { CompilerContext cc = getCompilerContext(); ConglomerateDescriptor cd; DataDictionary dd = getDataDictionary(); SchemaDescriptor sd; String schemaName = tableName.getSchemaName(); sd = getSchemaDescriptor(schemaName); // Users are not allowed to lock system tables if (sd.isSystemSchema()) { throw StandardException.newException( SQLState.LANG_NO_USER_DDL_IN_SYSTEM_SCHEMA, statementToString(), schemaName); } lockTableDescriptor = getTableDescriptor(tableName.getTableName(), sd); if (lockTableDescriptor == null) { // Check if the reference is for a synonym. TableName synonymTab = resolveTableToSynonym(tableName); if (synonymTab == null) throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, tableName); tableName = synonymTab; sd = getSchemaDescriptor(tableName.getSchemaName()); lockTableDescriptor = getTableDescriptor(synonymTab.getTableName(), sd); if (lockTableDescriptor == null) throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, tableName); } // throw an exception if user is attempting to lock a temporary table if (lockTableDescriptor.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) { throw StandardException.newException( SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE); } conglomerateNumber = lockTableDescriptor.getHeapConglomerateId(); /* Get the base conglomerate descriptor */ cd = lockTableDescriptor.getConglomerateDescriptor(conglomerateNumber); /* Statement is dependent on the TableDescriptor and ConglomerateDescriptor */ cc.createDependency(lockTableDescriptor); cc.createDependency(cd); if (isPrivilegeCollectionRequired()) { // need SELECT privilege to perform lock table statement. cc.pushCurrentPrivType(Authorizer.SELECT_PRIV); cc.addRequiredTablePriv(lockTableDescriptor); cc.popCurrentPrivType(); } }
/* ** Create an sps that is used by the trigger. */ private SPSDescriptor createSPS( LanguageConnectionContext lcc, DataDescriptorGenerator ddg, DataDictionary dd, TransactionController tc, UUID triggerId, SchemaDescriptor sd, UUID spsId, UUID compSchemaId, String text, boolean isWhen, TableDescriptor triggerTable) throws StandardException { if (text == null) { return null; } /* ** Note: the format of this string is very important. ** Dont change it arbitrarily -- see sps code. */ String spsName = "TRIGGER" + (isWhen ? "WHEN_" : "ACTN_") + triggerId + "_" + triggerTable.getUUID().toString(); SPSDescriptor spsd = new SPSDescriptor( dd, spsName, (spsId == null) ? dd.getUUIDFactory().createUUID() : spsId, sd.getUUID(), compSchemaId == null ? lcc.getDefaultSchema().getUUID() : compSchemaId, SPSDescriptor.SPS_TYPE_TRIGGER, true, // it is valid text, // the text true); // no defaults /* ** Prepared the stored prepared statement ** and release the activation class -- we ** know we aren't going to execute statement ** after create it, so for now we are finished. */ spsd.prepareAndRelease(lcc, triggerTable); dd.addSPSDescriptor(spsd, tc); return spsd; }
/** * Create the Constant information that will drive the guts of Execution. * * @exception StandardException Thrown on failure */ public ConstantAction makeConstantAction() throws StandardException { TableElementList coldefs = tableElementList; // for each column, stuff system.column ColumnInfo[] colInfos = new ColumnInfo[coldefs.countNumberOfColumns()]; int numConstraints = coldefs.genColumnInfos(colInfos); /* If we've seen a constraint, then build a constraint list */ CreateConstraintConstantAction[] conActions = null; SchemaDescriptor sd = getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true); if (numConstraints > 0) { conActions = new CreateConstraintConstantAction[numConstraints]; coldefs.genConstraintActions(true, conActions, getRelativeName(), sd, getDataDictionary()); } // if the any of columns are "long" and user has not specified a // page size, set the pagesize to 32k. // Also in case where the approximate sum of the column sizes is // greater than the bump threshold , bump the pagesize to 32k boolean table_has_long_column = false; int approxLength = 0; for (int i = 0; i < colInfos.length; i++) { DataTypeDescriptor dts = colInfos[i].dataType; if (dts.getTypeId().isLongConcatableTypeId()) { table_has_long_column = true; break; } approxLength += dts.getTypeId().getApproximateLengthInBytes(dts); } if (table_has_long_column || (approxLength > Property.TBL_PAGE_SIZE_BUMP_THRESHOLD)) { if (((properties == null) || (properties.get(Property.PAGE_SIZE_PARAMETER) == null)) && (PropertyUtil.getServiceProperty( getLanguageConnectionContext().getTransactionCompile(), Property.PAGE_SIZE_PARAMETER) == null)) { // do not override the user's choice of page size, whether it // is set for the whole database or just set on this statement. if (properties == null) properties = new Properties(); properties.put(Property.PAGE_SIZE_PARAMETER, Property.PAGE_SIZE_DEFAULT_LONG); } } return (getGenericConstantActionFactory() .getCreateTableConstantAction( sd.getSchemaName(), getRelativeName(), tableType, colInfos, conActions, properties, lockGranularity, onCommitDeleteRows, onRollbackDeleteRows)); }
/** * Bind this CreateTableNode. This means doing any static error checking that can be done before * actually creating the base table or declaring the global temporary table. For eg, verifying * that the TableElementList does not contain any duplicate column names. * * @exception StandardException Thrown on error */ public void bindStatement() throws StandardException { DataDictionary dataDictionary = getDataDictionary(); int numPrimaryKeys = 0; int numCheckConstraints = 0; int numReferenceConstraints = 0; int numUniqueConstraints = 0; int numGenerationClauses = 0; SchemaDescriptor sd = getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true); if (queryExpression != null) { FromList fromList = (FromList) getNodeFactory() .getNode( C_NodeTypes.FROM_LIST, getNodeFactory().doJoinOrderOptimization(), getContextManager()); CompilerContext cc = getCompilerContext(); ProviderList prevAPL = cc.getCurrentAuxiliaryProviderList(); ProviderList apl = new ProviderList(); try { cc.setCurrentAuxiliaryProviderList(apl); cc.pushCurrentPrivType(Authorizer.SELECT_PRIV); /* Bind the tables in the queryExpression */ queryExpression = queryExpression.bindNonVTITables(dataDictionary, fromList); queryExpression = queryExpression.bindVTITables(fromList); /* Bind the expressions under the resultSet */ queryExpression.bindExpressions(fromList); /* Bind the query expression */ queryExpression.bindResultColumns(fromList); /* Reject any untyped nulls in the RCL */ /* e.g. CREATE TABLE t1 (x) AS VALUES NULL WITH NO DATA */ queryExpression.bindUntypedNullsToResultColumns(null); } finally { cc.popCurrentPrivType(); cc.setCurrentAuxiliaryProviderList(prevAPL); } /* If there is an RCL for the table definition then copy the * names to the queryExpression's RCL after verifying that * they both have the same size. */ ResultColumnList qeRCL = queryExpression.getResultColumns(); if (resultColumns != null) { if (resultColumns.size() != qeRCL.visibleSize()) { throw StandardException.newException( SQLState.LANG_TABLE_DEFINITION_R_C_L_MISMATCH, getFullName()); } qeRCL.copyResultColumnNames(resultColumns); } int schemaCollationType = sd.getCollationType(); /* Create table element list from columns in query expression */ tableElementList = new TableElementList(); for (int index = 0; index < qeRCL.size(); index++) { ResultColumn rc = (ResultColumn) qeRCL.elementAt(index); if (rc.isGenerated()) { continue; } /* Raise error if column name is system generated. */ if (rc.isNameGenerated()) { throw StandardException.newException(SQLState.LANG_TABLE_REQUIRES_COLUMN_NAMES); } DataTypeDescriptor dtd = rc.getExpression().getTypeServices(); if ((dtd != null) && !dtd.isUserCreatableType()) { throw StandardException.newException( SQLState.LANG_INVALID_COLUMN_TYPE_CREATE_TABLE, dtd.getFullSQLTypeName(), rc.getName()); } // DERBY-2879 CREATE TABLE AS <subquery> does not maintain the // collation for character types. // eg for a territory based collation database // create table t as select tablename from sys.systables with no data; // Derby at this point does not support for a table's character // columns to have a collation different from it's schema's // collation. Which means that in a territory based database, // the query above will cause table t's character columns to // have collation of UCS_BASIC but the containing schema of t // has collation of territory based. This is not supported and // hence we will throw an exception below for the query above in // a territory based database. if (dtd.getTypeId().isStringTypeId() && dtd.getCollationType() != schemaCollationType) { throw StandardException.newException( SQLState.LANG_CAN_NOT_CREATE_TABLE, dtd.getCollationName(), DataTypeDescriptor.getCollationName(schemaCollationType)); } ColumnDefinitionNode column = (ColumnDefinitionNode) getNodeFactory() .getNode( C_NodeTypes.COLUMN_DEFINITION_NODE, rc.getName(), null, rc.getType(), null, getContextManager()); tableElementList.addTableElement(column); } } else { // Set the collation type and collation derivation of all the // character type columns. Their collation type will be same as the // collation of the schema they belong to. Their collation // derivation will be "implicit". // Earlier we did this in makeConstantAction but that is little too // late (DERBY-2955) // eg // CREATE TABLE STAFF9 (EMPNAME CHAR(20), // CONSTRAINT STAFF9_EMPNAME CHECK (EMPNAME NOT LIKE 'T%')) // For the query above, when run in a territory based db, we need // to have the correct collation set in bind phase of create table // so that when LIKE is handled in LikeEscapeOperatorNode, we have // the correct collation set for EMPNAME otherwise it will throw an // exception for 'T%' having collation of territory based and // EMPNAME having the default collation of UCS_BASIC tableElementList.setCollationTypesOnCharacterStringColumns( getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true)); } tableElementList.validate(this, dataDictionary, (TableDescriptor) null); /* Only 1012 columns allowed per table */ if (tableElementList.countNumberOfColumns() > Limits.DB2_MAX_COLUMNS_IN_TABLE) { throw StandardException.newException( SQLState.LANG_TOO_MANY_COLUMNS_IN_TABLE_OR_VIEW, String.valueOf(tableElementList.countNumberOfColumns()), getRelativeName(), String.valueOf(Limits.DB2_MAX_COLUMNS_IN_TABLE)); } numPrimaryKeys = tableElementList.countConstraints(DataDictionary.PRIMARYKEY_CONSTRAINT); /* Only 1 primary key allowed per table */ if (numPrimaryKeys > 1) { throw StandardException.newException( SQLState.LANG_TOO_MANY_PRIMARY_KEY_CONSTRAINTS, getRelativeName()); } /* Check the validity of all check constraints */ numCheckConstraints = tableElementList.countConstraints(DataDictionary.CHECK_CONSTRAINT); numReferenceConstraints = tableElementList.countConstraints(DataDictionary.FOREIGNKEY_CONSTRAINT); numUniqueConstraints = tableElementList.countConstraints(DataDictionary.UNIQUE_CONSTRAINT); numGenerationClauses = tableElementList.countGenerationClauses(); // temp tables can't have primary key or check or foreign key or unique constraints defined on // them if ((tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) && (numPrimaryKeys > 0 || numCheckConstraints > 0 || numReferenceConstraints > 0 || numUniqueConstraints > 0)) throw StandardException.newException( SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE); // each of these constraints have a backing index in the back. We need to make sure that a table // never has more // more than 32767 indexes on it and that is why this check. if ((numPrimaryKeys + numReferenceConstraints + numUniqueConstraints) > Limits.DB2_MAX_INDEXES_ON_TABLE) { throw StandardException.newException( SQLState.LANG_TOO_MANY_INDEXES_ON_TABLE, String.valueOf(numPrimaryKeys + numReferenceConstraints + numUniqueConstraints), getRelativeName(), String.valueOf(Limits.DB2_MAX_INDEXES_ON_TABLE)); } if ((numCheckConstraints > 0) || (numGenerationClauses > 0) || (numReferenceConstraints > 0)) { /* In order to check the validity of the check constraints and * generation clauses * we must goober up a FromList containing a single table, * the table being created, with an RCL containing the * new columns and their types. This will allow us to * bind the constraint definition trees against that * FromList. When doing this, we verify that there are * no nodes which can return non-deterministic results. */ FromList fromList = makeFromList(null, tableElementList, true); FormatableBitSet generatedColumns = new FormatableBitSet(); /* Now that we've finally goobered stuff up, bind and validate * the check constraints and generation clauses. */ if (numGenerationClauses > 0) { tableElementList.bindAndValidateGenerationClauses(sd, fromList, generatedColumns, null); } if (numCheckConstraints > 0) { tableElementList.bindAndValidateCheckConstraints(fromList); } if (numReferenceConstraints > 0) { tableElementList.validateForeignKeysOnGenerationClauses(fromList, generatedColumns); } } if (numPrimaryKeys > 0) { tableElementList.validatePrimaryKeyNullability(); } }
/** * This is the guts of the Execution-time logic for CREATE TRIGGER. * * @see ConstantAction#executeConstantAction * @exception StandardException Thrown on failure */ public void executeConstantAction(Activation activation) throws StandardException { SPSDescriptor whenspsd = null; SPSDescriptor actionspsd; LanguageConnectionContext lcc = activation.getLanguageConnectionContext(); DataDictionary dd = lcc.getDataDictionary(); DependencyManager dm = dd.getDependencyManager(); TransactionController tc = lcc.getTransactionExecute(); /* ** Indicate that we are about to modify the data dictionary. ** ** We tell the data dictionary we're done writing at the end of ** the transaction. */ dd.startWriting(lcc); SchemaDescriptor triggerSd = getSchemaDescriptorForCreate(dd, activation, triggerSchemaName); if (spsCompSchemaId == null) { SchemaDescriptor def = lcc.getDefaultSchema(); if (def.getUUID() == null) { // Descriptor for default schema is stale, // look it up in the dictionary def = dd.getSchemaDescriptor(def.getDescriptorName(), tc, false); } /* ** It is possible for spsCompSchemaId to be null. For instance, ** the current schema may not have been physically created yet but ** it exists "virtually". In this case, its UUID will have the ** value of null meaning that it is not persistent. e.g.: ** ** CONNECT 'db;create=true' user 'ernie'; ** CREATE TABLE bert.t1 (i INT); ** CREATE TRIGGER bert.tr1 AFTER INSERT ON bert.t1 ** FOR EACH STATEMENT MODE DB2SQL ** SELECT * FROM SYS.SYSTABLES; ** ** Note that in the above case, the trigger action statement have a ** null compilation schema. A compilation schema with null value ** indicates that the trigger action statement text does not have ** any dependencies with the CURRENT SCHEMA. This means: ** ** o It is safe to compile this statement in any schema since ** there is no dependency with the CURRENT SCHEMA. i.e.: All ** relevent identifiers are qualified with a specific schema. ** ** o The statement cache mechanism can utilize this piece of ** information to enable better statement plan sharing across ** connections in different schemas; thus, avoiding unnecessary ** statement compilation. */ if (def != null) spsCompSchemaId = def.getUUID(); } String tabName; if (triggerTable != null) { triggerTableId = triggerTable.getUUID(); tabName = triggerTable.getName(); } else tabName = "with UUID " + triggerTableId; /* We need to get table descriptor again. We simply can't trust the * one we got at compile time, the lock on system table was released * when compile was done, and the table might well have been dropped. */ triggerTable = dd.getTableDescriptor(triggerTableId); if (triggerTable == null) { throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tabName); } /* Lock the table for DDL. Otherwise during our execution, the table * might be changed, even dropped. Beetle 4269 */ lockTableForDDL(tc, triggerTable.getHeapConglomerateId(), true); /* get triggerTable again for correctness, in case it's changed before * the lock is aquired */ triggerTable = dd.getTableDescriptor(triggerTableId); if (triggerTable == null) { throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tabName); } /* ** Send an invalidate on the table from which ** the triggering event emanates. This it ** to make sure that DML statements on this table ** will be recompiled. Do this before we create ** our trigger spses lest we invalidate them just ** after creating them. */ dm.invalidateFor(triggerTable, DependencyManager.CREATE_TRIGGER, lcc); /* ** Lets get our trigger id up front, we'll use it when ** we create our spses. */ UUID tmpTriggerId = dd.getUUIDFactory().createUUID(); actionSPSId = (actionSPSId == null) ? dd.getUUIDFactory().createUUID() : actionSPSId; DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator(); /* ** Create the trigger descriptor first so the trigger action ** compilation can pick up the relevant trigger especially in ** the case of self triggering. */ TriggerDescriptor triggerd = ddg.newTriggerDescriptor( triggerSd, tmpTriggerId, triggerName, eventMask, isBefore, isRow, isEnabled, triggerTable, whenspsd == null ? null : whenspsd.getUUID(), actionSPSId, creationTimestamp == null ? new Timestamp(System.currentTimeMillis()) : creationTimestamp, referencedCols, originalActionText, referencingOld, referencingNew, oldReferencingName, newReferencingName); dd.addDescriptor(triggerd, triggerSd, DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc); /* ** If we have a WHEN action we create it now. */ if (whenText != null) { whenspsd = createSPS( lcc, ddg, dd, tc, tmpTriggerId, triggerSd, whenSPSId, spsCompSchemaId, whenText, true, triggerTable); } /* ** Create the trigger action */ actionspsd = createSPS( lcc, ddg, dd, tc, tmpTriggerId, triggerSd, actionSPSId, spsCompSchemaId, actionText, false, triggerTable); /* ** Make underlying spses dependent on the trigger. */ if (whenspsd != null) { dm.addDependency(triggerd, whenspsd, lcc.getContextManager()); } dm.addDependency(triggerd, actionspsd, lcc.getContextManager()); dm.addDependency(triggerd, triggerTable, lcc.getContextManager()); dm.addDependency(actionspsd, triggerTable, lcc.getContextManager()); // store trigger's dependency on various privileges in the dependeny system storeViewTriggerDependenciesOnPrivileges(activation, triggerd); }