use of org.apache.derby.impl.sql.compile.StatementNode in project derby by apache.
the class AlterTableConstantAction method columnDroppedAndTriggerDependencies.
// For the trigger, get the trigger action sql provided by the user
// in the create trigger sql. This sql is saved in the system
// table. Since a column has been dropped from the trigger table,
// the trigger action sql may not be valid anymore. To establish
// that, we need to regenerate the internal representation of that
// sql and bind it again.
//
// This method is called both on the WHEN clause (if one exists) and the
// triggered SQL statement of the trigger action.
//
// Return true if the trigger was dropped by this method (if cascade is
// true and it turns out the trigger depends on the column being dropped),
// or false otherwise.
private boolean columnDroppedAndTriggerDependencies(TriggerDescriptor trd, UUID spsUUID, boolean isWhenClause, boolean cascade, String columnName) throws StandardException {
dd.dropTriggerDescriptor(trd, tc);
// Here we get the trigger action sql and use the parser to build
// the parse tree for it.
SchemaDescriptor compSchema = dd.getSchemaDescriptor(dd.getSPSDescriptor(spsUUID).getCompSchemaId(), null);
CompilerContext newCC = lcc.pushCompilerContext(compSchema);
Parser pa = newCC.getParser();
String originalSQL = isWhenClause ? trd.getWhenClauseText() : trd.getTriggerDefinition();
Visitable node = isWhenClause ? pa.parseSearchCondition(originalSQL) : pa.parseStatement(originalSQL);
lcc.popCompilerContext(newCC);
// Do not delete following. We use this in finally clause to
// determine if the CompilerContext needs to be popped.
newCC = null;
try {
// Regenerate the internal representation for the trigger action
// sql using the ColumnReference classes in the parse tree. It
// will catch dropped column getting used in trigger action sql
// through the REFERENCING clause(this can happen only for the
// the triggers created prior to 10.7. Trigger created with
// 10.7 and higher keep track of trigger action column used
// through the REFERENCING clause in system table and hence
// use of dropped column will be detected earlier in this
// method for such triggers).
//
// We might catch errors like following during this step.
// Say that following pre-10.7 trigger exists in the system and
// user is dropping column c11. During the regeneration of the
// internal trigger action sql format, we will catch that
// column oldt.c11 does not exist anymore
// CREATE TRIGGER DERBY4998_SOFT_UPGRADE_RESTRICT_tr1
// AFTER UPDATE OF c12
// ON DERBY4998_SOFT_UPGRADE_RESTRICT REFERENCING OLD AS oldt
// FOR EACH ROW
// SELECT oldt.c11 from DERBY4998_SOFT_UPGRADE_RESTRICT
SPSDescriptor sps = isWhenClause ? trd.getWhenClauseSPS(lcc) : trd.getActionSPS(lcc);
int[] referencedColsInTriggerAction = new int[td.getNumberOfColumns()];
java.util.Arrays.fill(referencedColsInTriggerAction, -1);
String newText = dd.getTriggerActionString(node, trd.getOldReferencingName(), trd.getNewReferencingName(), originalSQL, trd.getReferencedCols(), referencedColsInTriggerAction, 0, trd.getTableDescriptor(), trd.getTriggerEventMask(), true, null, null);
if (isWhenClause) {
// The WHEN clause is not a full SQL statement, just a search
// condition, so we need to turn it into a statement in order
// to create an SPS.
newText = "VALUES " + newText;
}
sps.setText(newText);
// Now that we have the internal format of the trigger action sql,
// bind that sql to make sure that we are not using colunm being
// dropped in the trigger action sql directly (ie not through
// REFERENCING clause.
// eg
// create table atdc_12 (a integer, b integer);
// create trigger atdc_12_trigger_1 after update of a
// on atdc_12 for each row select a,b from atdc_12
// Drop one of the columns used in the trigger action
// alter table atdc_12 drop column b
// Following rebinding of the trigger action sql will catch the use
// of column b in trigger atdc_12_trigger_1
newCC = lcc.pushCompilerContext(compSchema);
newCC.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
pa = newCC.getParser();
StatementNode stmtnode = (StatementNode) pa.parseStatement(newText);
// need a current dependent for bind
newCC.setCurrentDependent(sps.getPreparedStatement());
stmtnode.bindStatement();
} catch (StandardException se) {
// Ane drop column ATDC_13_TAB3.c12 is issued
if (se.getMessageId().equals(SQLState.LANG_COLUMN_NOT_FOUND) || (se.getMessageId().equals(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE) || (se.getMessageId().equals(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED) || (se.getMessageId().equals(SQLState.LANG_TABLE_NOT_FOUND))))) {
if (cascade) {
trd.drop(lcc);
activation.addWarning(StandardException.newWarning(SQLState.LANG_TRIGGER_DROPPED, trd.getName(), td.getName()));
return true;
} else {
// we'd better give an error if don't drop it,
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), columnName, "TRIGGER", trd.getName());
}
} else
throw se;
} finally {
if (newCC != null)
lcc.popCompilerContext(newCC);
}
// If we are here, then it means that the column being dropped
// is not getting used in the trigger action.
//
// We have recreated the trigger action SPS and recollected the
// column positions for trigger columns and trigger action columns
// getting accessed through REFERENCING clause because
// drop column can affect the column positioning of existing
// columns in the table. We will save that in the system table.
dd.addDescriptor(trd, sd, DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc);
return false;
}
use of org.apache.derby.impl.sql.compile.StatementNode in project derby by apache.
the class GenericStatement method prepMinion.
private PreparedStatement prepMinion(LanguageConnectionContext lcc, boolean cacheMe, Object[] paramDefaults, SchemaDescriptor spsSchema, boolean internalSQL) throws StandardException {
long beginTime = 0;
long parseTime = 0;
long bindTime = 0;
long optimizeTime = 0;
long generateTime = 0;
Timestamp beginTimestamp = null;
Timestamp endTimestamp = null;
StatementContext statementContext = null;
// if it is invalid, we will recompile now.
if (preparedStmt != null) {
if (preparedStmt.upToDate())
return preparedStmt;
}
// Start a new optimizer trace for this statement
if (lcc.optimizerTracingIsOn()) {
lcc.getOptimizerTracer().traceStartStatement(getSource());
}
beginTime = getCurrentTimeMillis(lcc);
/* beginTimestamp only meaningful if beginTime is meaningful.
* beginTime is meaningful if STATISTICS TIMING is ON.
*/
if (beginTime != 0) {
beginTimestamp = new Timestamp(beginTime);
}
/**
* set the prepare isolation from the LanguageConnectionContext now as
* we need to consider it in caching decisions
*/
prepareIsolationLevel = lcc.getPrepareIsolationLevel();
/* a note on statement caching:
*
* A GenericPreparedStatement (GPS) is only added it to the cache if the
* parameter cacheMe is set to TRUE when the GPS is created.
*
* Earlier only CacheStatement (CS) looked in the statement cache for a
* prepared statement when prepare was called. Now the functionality
* of CS has been folded into GenericStatement (GS). So we search the
* cache for an existing PreparedStatement only when cacheMe is TRUE.
* i.e if the user calls prepare with cacheMe set to TRUE:
* then we
* a) look for the prepared statement in the cache.
* b) add the prepared statement to the cache.
*
* In cases where the statement cache has been disabled (by setting the
* relevant Derby property) then the value of cacheMe is irrelevant.
*/
boolean foundInCache = false;
if (preparedStmt == null) {
if (cacheMe)
preparedStmt = (GenericPreparedStatement) ((GenericLanguageConnectionContext) lcc).lookupStatement(this);
if (preparedStmt == null) {
preparedStmt = new GenericPreparedStatement(this);
} else {
foundInCache = true;
}
}
// cache of prepared statement objects...
synchronized (preparedStmt) {
for (; ; ) {
if (foundInCache) {
if (preparedStmt.referencesSessionSchema()) {
// cannot use this state since it is private to a connection.
// switch to a new statement.
foundInCache = false;
preparedStmt = new GenericPreparedStatement(this);
break;
}
}
// did it get updated while we waited for the lock on it?
if (preparedStmt.upToDate()) {
return preparedStmt;
}
if (!preparedStmt.isCompiling()) {
break;
}
try {
preparedStmt.wait();
} catch (InterruptedException ie) {
InterruptStatus.setInterrupted();
}
}
preparedStmt.beginCompiling();
}
try {
HeaderPrintWriter istream = lcc.getLogStatementText() ? Monitor.getStream() : null;
/*
** For stored prepared statements, we want all
** errors, etc in the context of the underlying
** EXECUTE STATEMENT statement, so don't push/pop
** another statement context unless we don't have
** one. We won't have one if it is an internal
** SPS (e.g. jdbcmetadata).
*/
if (!preparedStmt.isStorable() || lcc.getStatementDepth() == 0) {
// since this is for compilation only, set atomic
// param to true and timeout param to 0
statementContext = lcc.pushStatementContext(true, isForReadOnly, getSource(), null, false, 0L);
}
/*
** RESOLVE: we may ultimately wish to pass in
** whether we are a jdbc metadata query or not to
** get the CompilerContext to make the createDependency()
** call a noop.
*/
CompilerContext cc = lcc.pushCompilerContext(compilationSchema);
if (prepareIsolationLevel != TransactionControl.UNSPECIFIED_ISOLATION_LEVEL) {
cc.setScanIsolationLevel(prepareIsolationLevel);
}
if (internalSQL || (spsSchema != null) && (spsSchema.isSystemSchema()) && (spsSchema.equals(compilationSchema))) {
cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
}
try {
// Statement logging if lcc.getLogStatementText() is true
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), " + LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), " + LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), " + LanguageConnectionContext.drdaStr + lcc.getDrdaID() + "), Begin compiling prepared statement: " + getSource() + " :End prepared statement");
}
Parser p = cc.getParser();
cc.setCurrentDependent(preparedStmt);
// Only top level statements go through here, nested statement
// will invoke this method from other places
StatementNode qt = (StatementNode) p.parseStatement(statementText, paramDefaults);
parseTime = getCurrentTimeMillis(lcc);
// Call user-written tree-printer if it exists
walkAST(lcc, qt, ASTVisitor.AFTER_PARSE);
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpParseTree")) {
SanityManager.GET_DEBUG_STREAM().print("\n\n============PARSE===========\n\n");
qt.treePrint();
lcc.getPrintedObjectsMap().clear();
}
if (SanityManager.DEBUG_ON("StopAfterParsing")) {
lcc.setLastQueryTree(qt);
throw StandardException.newException(SQLState.LANG_STOP_AFTER_PARSING);
}
}
/*
** Tell the data dictionary that we are about to do
** a bunch of "get" operations that must be consistent with
** each other.
*/
DataDictionary dataDictionary = lcc.getDataDictionary();
int ddMode = dataDictionary == null ? 0 : dataDictionary.startReading(lcc);
try {
// start a nested transaction -- all locks acquired by bind
// and optimize will be released when we end the nested
// transaction.
lcc.beginNestedTransaction(true);
qt.bindStatement();
bindTime = getCurrentTimeMillis(lcc);
// Call user-written tree-printer if it exists
walkAST(lcc, qt, ASTVisitor.AFTER_BIND);
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpBindTree")) {
SanityManager.GET_DEBUG_STREAM().print("\n\n============BIND===========\n\n");
qt.treePrint();
lcc.getPrintedObjectsMap().clear();
}
if (SanityManager.DEBUG_ON("StopAfterBinding")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_BINDING);
}
}
// we do the check here.
if (preparedStmt.referencesSessionSchema(qt)) {
if (foundInCache)
((GenericLanguageConnectionContext) lcc).removeStatement(this);
}
// stop adding privileges for user-defined types
cc.skipTypePrivileges(true);
qt.optimizeStatement();
optimizeTime = getCurrentTimeMillis(lcc);
// Call user-written tree-printer if it exists
walkAST(lcc, qt, ASTVisitor.AFTER_OPTIMIZE);
// Statement logging if lcc.getLogStatementText() is true
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), " + LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), " + LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), " + LanguageConnectionContext.drdaStr + lcc.getDrdaID() + "), End compiling prepared statement: " + getSource() + " :End prepared statement");
}
} catch (StandardException se) {
lcc.commitNestedTransaction();
// Statement logging if lcc.getLogStatementText() is true
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), " + LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), " + LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), " + LanguageConnectionContext.drdaStr + lcc.getDrdaID() + "), Error compiling prepared statement: " + getSource() + " :End prepared statement");
}
throw se;
} finally {
/* Tell the data dictionary that we are done reading */
if (dataDictionary != null)
dataDictionary.doneReading(ddMode, lcc);
}
/* we need to move the commit of nested sub-transaction
* after we mark PS valid, during compilation, we might need
* to get some lock to synchronize with another thread's DDL
* execution, in particular, the compilation of insert/update/
* delete vs. create index/constraint (see Beetle 3976). We
* can't release such lock until after we mark the PS valid.
* Otherwise we would just erase the DDL's invalidation when
* we mark it valid.
*/
try // put in try block, commit sub-transaction if bad
{
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpOptimizedTree")) {
SanityManager.GET_DEBUG_STREAM().print("\n\n============OPT===========\n\n");
qt.treePrint();
lcc.getPrintedObjectsMap().clear();
}
if (SanityManager.DEBUG_ON("StopAfterOptimizing")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_OPTIMIZING);
}
}
GeneratedClass ac = qt.generate(preparedStmt.getByteCodeSaver());
generateTime = getCurrentTimeMillis(lcc);
/* endTimestamp only meaningful if generateTime is meaningful.
* generateTime is meaningful if STATISTICS TIMING is ON.
*/
if (generateTime != 0) {
endTimestamp = new Timestamp(generateTime);
}
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("StopAfterGenerating")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_GENERATING);
}
}
/*
copy over the compile-time created objects
to the prepared statement. This always happens
at the end of a compile, so there is no need
to erase the previous entries on a re-compile --
this erases as it replaces. Set the activation
class in case it came from a StorablePreparedStatement
*/
preparedStmt.setConstantAction(qt.makeConstantAction());
preparedStmt.setSavedObjects(cc.getSavedObjects());
preparedStmt.setRequiredPermissionsList(cc.getRequiredPermissionsList());
preparedStmt.incrementVersionCounter();
preparedStmt.setActivationClass(ac);
preparedStmt.setNeedsSavepoint(qt.needsSavepoint());
preparedStmt.setCursorInfo((CursorInfo) cc.getCursorInfo());
preparedStmt.setIsAtomic(qt.isAtomic());
preparedStmt.setExecuteStatementNameAndSchema(qt.executeStatementName(), qt.executeSchemaName());
preparedStmt.setSPSName(qt.getSPSName());
preparedStmt.completeCompile(qt);
preparedStmt.setCompileTimeWarnings(cc.getWarnings());
// Schedule updates of any stale index statistics we may
// have detected when creating the plan.
TableDescriptor[] tds = qt.updateIndexStatisticsFor();
if (tds.length > 0) {
IndexStatisticsDaemon isd = lcc.getDataDictionary().getIndexStatsRefresher(true);
if (isd != null) {
for (int i = 0; i < tds.length; i++) {
isd.schedule(tds[i]);
}
}
}
} catch (// hold it, throw it
StandardException e) {
lcc.commitNestedTransaction();
throw e;
}
if (lcc.getRunTimeStatisticsMode()) {
preparedStmt.setCompileTimeMillis(// parse time
parseTime - beginTime, // bind time
bindTime - parseTime, // optimize time
optimizeTime - bindTime, // generate time
generateTime - optimizeTime, // total compile time
generateTime - beginTime, beginTimestamp, endTimestamp);
}
} finally // for block introduced by pushCompilerContext()
{
lcc.popCompilerContext(cc);
}
} catch (StandardException se) {
if (foundInCache)
((GenericLanguageConnectionContext) lcc).removeStatement(this);
throw se;
} finally {
preparedStmt.endCompiling();
}
lcc.commitNestedTransaction();
if (statementContext != null)
lcc.popStatementContext(statementContext, null);
return preparedStmt;
}
Aggregations