1 21 22 package org.apache.derby.impl.sql.compile; 23 24 import org.apache.derby.iapi.services.context.ContextManager; 25 import org.apache.derby.iapi.services.monitor.Monitor; 26 import org.apache.derby.iapi.services.sanity.SanityManager; 27 28 import org.apache.derby.iapi.error.StandardException; 29 30 import org.apache.derby.iapi.sql.compile.CompilerContext; 31 import org.apache.derby.iapi.sql.compile.Parser; 32 33 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor; 34 import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator; 35 import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext; 36 import org.apache.derby.iapi.sql.dictionary.DataDictionary; 37 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor; 38 import org.apache.derby.iapi.sql.dictionary.SPSDescriptor; 39 import org.apache.derby.iapi.sql.dictionary.TableDescriptor; 40 import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor; 41 42 import org.apache.derby.iapi.sql.conn.Authorizer; 43 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; 44 45 import org.apache.derby.iapi.sql.depend.Dependent; 46 47 import org.apache.derby.iapi.reference.SQLState; 48 49 import org.apache.derby.iapi.sql.execute.ConstantAction; 50 51 import org.apache.derby.iapi.sql.ResultSet; 52 53 import org.apache.derby.iapi.types.DataTypeDescriptor; 54 import org.apache.derby.iapi.types.TypeId; 55 56 import org.apache.derby.catalog.UUID; 57 import java.sql.Timestamp ; 58 import java.sql.Types ; 59 import java.util.Enumeration ; 60 import java.util.Hashtable ; 61 import java.util.Vector ; 62 63 70 71 public class CreateTriggerNode extends DDLStatementNode 72 { 73 private TableName triggerName; 74 private TableName tableName; 75 private int triggerEventMask; 76 private ResultColumnList triggerCols; 77 private boolean isBefore; 78 private boolean isRow; 79 private boolean isEnabled; 80 private Vector refClause; 81 private QueryTreeNode whenClause; 82 private String whenText; 83 private int whenOffset; 84 private StatementNode actionNode; 85 private String actionText; 86 private String originalActionText; private int actionOffset; 88 89 private SchemaDescriptor triggerSchemaDescriptor; 90 private SchemaDescriptor compSchemaDescriptor; 91 private int[] referencedColInts; 92 private TableDescriptor triggerTableDescriptor; 93 private UUID actionCompSchemaId; 94 95 105 private String oldTableName; 106 private String newTableName; 107 108 private boolean oldTableInReferencingClause; 109 private boolean newTableInReferencingClause; 110 111 112 133 public void init 134 ( 135 Object triggerName, 136 Object tableName, 137 Object triggerEventMask, 138 Object triggerCols, 139 Object isBefore, 140 Object isRow, 141 Object isEnabled, 142 Object refClause, 143 Object whenClause, 144 Object whenText, 145 Object whenOffset, 146 Object actionNode, 147 Object actionText, 148 Object actionOffset 149 ) throws StandardException 150 { 151 initAndCheck(triggerName); 152 this.triggerName = (TableName) triggerName; 153 this.tableName = (TableName) tableName; 154 this.triggerEventMask = ((Integer ) triggerEventMask).intValue(); 155 this.triggerCols = (ResultColumnList) triggerCols; 156 this.isBefore = ((Boolean ) isBefore).booleanValue(); 157 this.isRow = ((Boolean ) isRow).booleanValue(); 158 this.isEnabled = ((Boolean ) isEnabled).booleanValue(); 159 this.refClause = (Vector ) refClause; 160 this.whenClause = (QueryTreeNode) whenClause; 161 this.whenText = (whenText == null) ? null : ((String ) whenText).trim(); 162 this.whenOffset = ((Integer ) whenOffset).intValue(); 163 this.actionNode = (StatementNode) actionNode; 164 this.originalActionText = (String ) actionText; 165 this.actionText = 166 (actionText == null) ? null : ((String ) actionText).trim(); 167 this.actionOffset = ((Integer ) actionOffset).intValue(); 168 169 implicitCreateSchema = true; 170 } 171 172 public String statementToString() 173 { 174 return "CREATE TRIGGER"; 175 } 176 177 183 184 public void printSubNodes(int depth) 185 { 186 if (SanityManager.DEBUG) 187 { 188 super.printSubNodes(depth); 189 190 if (triggerCols != null) 191 { 192 printLabel(depth, "triggerColumns: "); 193 triggerCols.treePrint(depth + 1); 194 } 195 if (whenClause != null) 196 { 197 printLabel(depth, "whenClause: "); 198 whenClause.treePrint(depth + 1); 199 } 200 if (actionNode != null) 201 { 202 printLabel(depth, "actionNode: "); 203 actionNode.treePrint(depth + 1); 204 } 205 } 206 } 207 208 209 211 212 214 222 public QueryTreeNode bind() throws StandardException 223 { 224 CompilerContext compilerContext = getCompilerContext(); 225 DataDictionary dd = getDataDictionary(); 226 230 LanguageConnectionContext lcc = getLanguageConnectionContext(); 231 compSchemaDescriptor = lcc.getDefaultSchema(); 232 233 239 triggerSchemaDescriptor = getSchemaDescriptor(); 240 241 244 triggerTableDescriptor = getTableDescriptor(tableName); 245 246 if (isSessionSchema(triggerTableDescriptor.getSchemaDescriptor())) 248 { 249 throw StandardException.newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES); 250 } 251 if (isPrivilegeCollectionRequired()) 252 { 253 compilerContext.pushCurrentPrivType(Authorizer.TRIGGER_PRIV); 254 compilerContext.addRequiredTablePriv(triggerTableDescriptor); 255 compilerContext.popCurrentPrivType(); 256 } 257 258 261 boolean needInternalSQL = bindReferencesClause(dd); 262 263 lcc.pushTriggerTable(triggerTableDescriptor); 264 try 265 { 266 273 if (needInternalSQL) 274 compilerContext.setReliability(CompilerContext.INTERNAL_SQL_LEGAL); 275 276 if(isBefore) 281 compilerContext.setReliability(CompilerContext.MODIFIES_SQL_DATA_PROCEDURE_ILLEGAL); 282 283 actionNode.bind(); 284 285 if (whenClause != null) 286 { 287 whenClause.bind(); 288 } 289 } 290 finally 291 { 292 lcc.popTriggerTable(triggerTableDescriptor); 293 } 294 295 298 compilerContext.createDependency(triggerTableDescriptor); 299 300 304 if (triggerCols != null && triggerCols.size() != 0) 305 { 306 referencedColInts = new int[triggerCols.size()]; 307 Hashtable columnNames = new Hashtable (); 308 int tcSize = triggerCols.size(); 309 for (int i = 0; i < tcSize; i++) 310 { 311 ResultColumn rc = (ResultColumn) triggerCols.elementAt(i); 312 if (columnNames.put(rc.getName(), rc) != null) 313 { 314 throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_IN_TRIGGER_UPDATE, 315 rc.getName(), 316 triggerName); 317 } 318 319 ColumnDescriptor cd = triggerTableDescriptor.getColumnDescriptor(rc.getName()); 320 if (cd == null) 321 { 322 throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, 323 rc.getName(), 324 tableName); 325 } 326 327 referencedColInts[i] = cd.getPosition(); 328 } 329 330 java.util.Arrays.sort(referencedColInts); 332 } 333 334 if (actionNode.referencesSessionSchema()) 336 throw StandardException.newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES); 337 338 return this; 339 } 340 341 348 public boolean referencesSessionSchema() 349 throws StandardException 350 { 351 return (isSessionSchema(triggerTableDescriptor.getSchemaName()) || actionNode.referencesSessionSchema()); 354 } 355 356 387 private boolean bindReferencesClause(DataDictionary dd) throws StandardException 388 { 389 validateReferencesClause(dd); 390 391 StringBuffer newText = new StringBuffer (); 392 boolean regenNode = false; 393 int start = 0; 394 if (isRow) 395 { 396 401 CollectNodesVisitor visitor = new CollectNodesVisitor(ColumnReference.class); 402 actionNode.accept(visitor); 403 Vector refs = visitor.getList(); 404 406 QueryTreeNode[] cols = sortRefs(refs, true); 407 408 for (int i = 0; i < cols.length; i++) 409 { 410 ColumnReference ref = (ColumnReference) cols[i]; 411 412 431 if (ref.getBeginOffset() == -1) 432 { 433 continue; 434 } 435 436 TableName tableName = ref.getTableNameNode(); 437 if ((tableName == null) || 438 ((oldTableName == null || !oldTableName.equals(tableName.getTableName())) && 439 (newTableName == null || !newTableName.equals(tableName.getTableName())))) 440 { 441 continue; 442 } 443 444 int tokBeginOffset = tableName.getBeginOffset(); 445 int tokEndOffset = tableName.getEndOffset(); 446 if (tokBeginOffset == -1) 447 { 448 continue; 449 } 450 451 regenNode = true; 452 checkInvalidTriggerReference(tableName.getTableName()); 453 String colName = ref.getColumnName(); 454 int columnLength = ref.getEndOffset() - ref.getBeginOffset() + 1; 455 456 newText.append(originalActionText.substring(start, tokBeginOffset-actionOffset)); 457 newText.append(genColumnReferenceSQL(dd, colName, tableName.getTableName(), tableName.getTableName().equals(oldTableName))); 458 start = tokEndOffset- actionOffset + columnLength + 2; 459 } 460 } 461 else 462 { 463 468 CollectNodesVisitor visitor = new CollectNodesVisitor(FromBaseTable.class); 469 actionNode.accept(visitor); 470 Vector refs = visitor.getList(); 471 QueryTreeNode[] tabs = sortRefs(refs, false); 472 for (int i = 0; i < tabs.length; i++) 473 { 474 FromBaseTable fromTable = (FromBaseTable) tabs[i]; 475 String refTableName = fromTable.getTableName().getTableName(); 476 String baseTableName = fromTable.getBaseTableName(); 477 if ((baseTableName == null) || 478 ((oldTableName == null || !oldTableName.equals(baseTableName)) && 479 (newTableName == null || !newTableName.equals(baseTableName)))) 480 { 481 continue; 482 } 483 int tokBeginOffset = fromTable.getTableNameField().getBeginOffset(); 484 int tokEndOffset = fromTable.getTableNameField().getEndOffset(); 485 if (tokBeginOffset == -1) 486 { 487 continue; 488 } 489 490 checkInvalidTriggerReference(baseTableName); 491 492 regenNode = true; 493 newText.append(originalActionText.substring(start, tokBeginOffset-actionOffset)); 494 newText.append(baseTableName.equals(oldTableName) ? 495 "new org.apache.derby.catalog.TriggerOldTransitionRows() " : 496 "new org.apache.derby.catalog.TriggerNewTransitionRows() "); 497 502 if (refTableName.equals(baseTableName)) 503 { 504 newText.append(baseTableName).append(" "); 505 } 506 start=tokEndOffset-actionOffset+1; 507 } 508 } 509 510 515 if (regenNode) 516 { 517 if (start < originalActionText.length()) 518 { 519 newText.append(originalActionText.substring(start)); 520 } 521 actionText = newText.toString(); 522 actionNode = (StatementNode)reparseTriggerText(); 523 } 524 525 return regenNode; 526 } 527 528 531 private QueryTreeNode[] sortRefs(Vector refs, boolean isRow) 532 { 533 int size = refs.size(); 534 QueryTreeNode[] sorted = new QueryTreeNode[size]; 535 int i = 0; 536 for (Enumeration e = refs.elements(); e.hasMoreElements(); ) 537 { 538 if (isRow) 539 sorted[i++] = (ColumnReference)e.nextElement(); 540 else 541 sorted[i++] = (FromBaseTable)e.nextElement(); 542 } 543 544 546 QueryTreeNode temp; 547 for (i = 0; i < size - 1; i++) 548 { 549 temp = null; 550 for (int j = 0; j < size - i - 1; j++) 551 { 552 if ((isRow && 553 sorted[j].getBeginOffset() > 554 sorted[j+1].getBeginOffset() 555 ) || 556 (!isRow && 557 ((FromBaseTable) sorted[j]).getTableNameField().getBeginOffset() > 558 ((FromBaseTable) sorted[j+1]).getTableNameField().getBeginOffset() 559 )) 560 { 561 temp = sorted[j]; 562 sorted[j] = sorted[j+1]; 563 sorted[j+1] = temp; 564 } 565 } 566 if (temp == null) break; 568 } 569 570 return sorted; 571 } 572 573 576 private QueryTreeNode reparseTriggerText() throws StandardException 577 { 578 582 LanguageConnectionContext lcc = getLanguageConnectionContext(); 583 CompilerContext newCC = lcc.pushCompilerContext(); 584 newCC.setReliability(CompilerContext.INTERNAL_SQL_LEGAL); 585 586 try 587 { 588 return QueryTreeNode.parseQueryText(newCC, actionText, (Object [])null, lcc); 589 } 590 591 finally 592 { 593 lcc.popCompilerContext(newCC); 594 } 595 } 596 606 private String genColumnReferenceSQL 607 ( 608 DataDictionary dd, 609 String colName, 610 String tabName, 611 boolean isOldTable 612 ) throws StandardException 613 { 614 ColumnDescriptor colDesc = null; 615 if ((colDesc = triggerTableDescriptor.getColumnDescriptor(colName)) == null) 616 { 617 throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, tabName+"."+colName); 618 } 619 620 640 StringBuffer methodCall = new StringBuffer (); 641 methodCall.append("CAST (org.apache.derby.iapi.db.Factory::getTriggerExecutionContext()."); 642 methodCall.append(isOldTable ? "getOldRow()" : "getNewRow()"); 643 methodCall.append(".getObject("); 644 methodCall.append(colDesc.getPosition()); 645 methodCall.append(") AS "); 646 DataTypeDescriptor dts = colDesc.getType(); 647 TypeId typeId = dts.getTypeId(); 648 649 654 methodCall.append( 655 (typeId.userType() ? typeId.getSQLTypeName() : dts.getSQLstring())); 656 657 methodCall.append(") "); 658 659 return methodCall.toString(); 660 } 661 662 666 private void checkInvalidTriggerReference(String tableName) throws StandardException 667 { 668 if (tableName.equals(oldTableName) && 669 (triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_INSERT) == TriggerDescriptor.TRIGGER_EVENT_INSERT) 670 { 671 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "INSERT", "new"); 672 } 673 else if (tableName.equals(newTableName) && 674 (triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_DELETE) == TriggerDescriptor.TRIGGER_EVENT_DELETE) 675 { 676 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "DELETE", "old"); 677 } 678 } 679 680 685 private void validateReferencesClause(DataDictionary dd) throws StandardException 686 { 687 if ((refClause == null) || (refClause.size() == 0)) 688 { 689 return; 690 } 691 692 for (Enumeration e = refClause.elements(); e.hasMoreElements(); ) 693 { 694 TriggerReferencingStruct trn = (TriggerReferencingStruct)e.nextElement(); 695 696 701 if (isRow && !trn.isRow) 702 { 703 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "ROW", "row"); 704 } 705 else if (!isRow && trn.isRow) 706 { 707 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "STATEMENT", "table"); 708 } 709 710 713 if (trn.isNew) 714 { 715 716 if (newTableInReferencingClause) 717 { 718 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_CLAUSE_DUPS); 719 } 720 721 724 if ((triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_DELETE) == TriggerDescriptor.TRIGGER_EVENT_DELETE) 725 { 726 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "DELETE", "old"); 727 } 728 newTableName = trn.identifier; 729 newTableInReferencingClause = true; 730 } 731 else 732 { 733 if (oldTableInReferencingClause) 734 { 735 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_CLAUSE_DUPS); 736 } 737 740 if ((triggerEventMask & TriggerDescriptor.TRIGGER_EVENT_INSERT) == TriggerDescriptor.TRIGGER_EVENT_INSERT) 741 { 742 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "INSERT", "new"); 743 } 744 oldTableName = trn.identifier; 745 oldTableInReferencingClause = true; 746 } 747 748 751 if (this.isBefore && !trn.isRow) { 752 throw StandardException.newException(SQLState.LANG_TRIGGER_BAD_REF_MISMATCH, "BEFORE", "row"); 754 } 755 756 } 757 758 } 759 760 761 766 public ConstantAction makeConstantAction() throws StandardException 767 { 768 String oldReferencingName = (oldTableInReferencingClause) ? oldTableName : null; 769 String newReferencingName = (newTableInReferencingClause) ? newTableName : null; 770 771 return getGenericConstantActionFactory().getCreateTriggerConstantAction( 772 triggerSchemaDescriptor.getSchemaName(), 773 getRelativeName(), 774 triggerEventMask, 775 isBefore, 776 isRow, 777 isEnabled, 778 triggerTableDescriptor, 779 (UUID)null, whenText, 781 (UUID)null, actionText, 783 (actionCompSchemaId == null) ? 784 compSchemaDescriptor.getUUID() : 785 actionCompSchemaId, 786 (Timestamp )null, referencedColInts, 788 originalActionText, 789 oldTableInReferencingClause, 790 newTableInReferencingClause, 791 oldReferencingName, 792 newReferencingName 793 ); 794 } 795 796 797 803 public String toString() 804 { 805 if (SanityManager.DEBUG) 806 { 807 String refString = "null"; 808 if (refClause != null) 809 { 810 StringBuffer buf = new StringBuffer (); 811 for (Enumeration e = refClause.elements(); e.hasMoreElements(); ) 812 { 813 buf.append("\t"); 814 TriggerReferencingStruct trn = 815 (TriggerReferencingStruct)e.nextElement(); 816 buf.append(trn.toString()); 817 buf.append("\n"); 818 } 819 refString = buf.toString(); 820 } 821 822 return super.toString() + 823 "tableName: "+tableName+ 824 "\ntriggerEventMask: "+triggerEventMask+ 825 "\nisBefore: "+isBefore+ 826 "\nisRow: "+isRow+ 827 "\nisEnabled: "+isEnabled+ 828 "\nwhenText: "+whenText+ 829 "\nrefClause: "+refString+ 830 "\nactionText: "+actionText+ 831 "\n"; 832 } 833 else 834 { 835 return ""; 836 } 837 } 838 839 } 840 | Popular Tags |