1 24 25 package com.mckoi.database; 26 27 import java.io.*; 28 import java.util.ArrayList ; 29 import com.mckoi.debug.Lvl; 30 import com.mckoi.util.IntegerVector; 31 import com.mckoi.util.BigNumber; 32 33 43 44 public final class ConnectionTriggerManager { 45 46 49 private DatabaseConnection connection; 50 51 55 private ArrayList triggers_active; 56 57 61 private boolean list_validated; 62 63 66 private boolean trigger_modified; 67 68 71 ConnectionTriggerManager(DatabaseConnection connection) { 72 this.connection = connection; 73 this.triggers_active = new ArrayList (); 74 this.list_validated = false; 75 this.trigger_modified = false; 76 connection.attachTableBackedCache(new CTMBackedCache()); 78 } 79 80 84 private Table findTrigger(QueryContext context, DataTable table, 85 String schema, String name) { 86 Operator EQUALS = Operator.get("="); 88 89 Variable schemav = table.getResolvedVariable(0); 90 Variable namev = table.getResolvedVariable(1); 91 92 Table t = table.simpleSelect(context, namev, EQUALS, 93 new Expression(TObject.stringVal(name))); 94 return t.exhaustiveSelect(context, Expression.simple( 95 schemav, EQUALS, TObject.stringVal(schema))); 96 } 97 98 109 public void createTableTrigger(String schema, String name, 110 int type, TableName on_table, 111 String procedure_name, TObject[] params) 112 throws DatabaseException { 113 114 TableName trigger_table_name = new TableName(schema, name); 115 116 DatabaseConnection.checkAllowCreate(trigger_table_name); 118 119 if (!connection.tableExists(trigger_table_name)) { 122 123 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 125 try { 126 ObjectOutputStream ob_out = new ObjectOutputStream(bout); 127 ob_out.writeInt(1); ob_out.writeObject(params); 129 ob_out.flush(); 130 } 131 catch (IOException e) { 132 throw new RuntimeException ("IO Error: " + e.getMessage()); 133 } 134 byte[] encoded_params = bout.toByteArray(); 135 136 DataTable table = connection.getTable(Database.SYS_DATA_TRIGGER); 138 RowData row = new RowData(table); 139 row.setColumnDataFromTObject(0, TObject.stringVal(schema)); 140 row.setColumnDataFromTObject(1, TObject.stringVal(name)); 141 row.setColumnDataFromTObject(2, TObject.intVal(type)); 142 row.setColumnDataFromTObject(3, 143 TObject.stringVal("T:" + on_table.toString())); 144 row.setColumnDataFromTObject(4, TObject.stringVal(procedure_name)); 145 row.setColumnDataFromTObject(5, TObject.objectVal(encoded_params)); 146 row.setColumnDataFromTObject(6, 147 TObject.stringVal(connection.getUser().getUserName())); 148 table.add(row); 149 150 invalidateTriggerList(); 152 153 connection.databaseObjectCreated(trigger_table_name); 155 156 trigger_modified = true; 158 } 159 else { 160 throw new RuntimeException ("Trigger name '" + schema + "." + name + 161 "' already in use."); 162 } 163 } 164 165 168 public void dropTrigger(String schema, String name) throws DatabaseException { 169 QueryContext context = new DatabaseQueryContext(connection); 170 DataTable table = connection.getTable(Database.SYS_DATA_TRIGGER); 171 172 Table t = findTrigger(context, table, schema, name); 174 175 if (t.getRowCount() == 0) { 176 throw new StatementException("Trigger '" + schema + "." + name + 177 "' not found."); 178 } 179 else if (t.getRowCount() > 1) { 180 throw new RuntimeException ( 181 "Assertion failed: multiple entries for the same trigger name."); 182 } 183 else { 184 table.delete(t); 186 187 connection.databaseObjectDropped(new TableName(schema, name)); 189 190 trigger_modified = true; 192 } 193 194 } 195 196 199 public boolean triggerExists(String schema, String name) { 200 QueryContext context = new DatabaseQueryContext(connection); 201 DataTable table = connection.getTable(Database.SYS_DATA_TRIGGER); 202 203 Table t = findTrigger(context, table, schema, name); 205 206 if (t.getRowCount() == 0) { 207 return false; 209 } 210 else if (t.getRowCount() > 1) { 211 throw new RuntimeException ( 212 "Assertion failed: multiple entries for the same trigger name."); 213 } 214 else { 215 return true; 217 } 218 } 219 220 227 private void invalidateTriggerList() { 228 list_validated = false; 229 triggers_active.clear(); 230 } 231 232 235 private void buildTriggerList() { 236 if (!list_validated) { 237 DataTable table = connection.getTable(Database.SYS_DATA_TRIGGER); 239 RowEnumeration e = table.rowEnumeration(); 240 241 while (e.hasMoreRows()) { 243 int row_index = e.nextRowIndex(); 244 245 TObject trig_schem = table.getCellContents(0, row_index); 246 TObject trig_name = table.getCellContents(1, row_index); 247 TObject type = table.getCellContents(2, row_index); 248 TObject on_object = table.getCellContents(3, row_index); 249 TObject action = table.getCellContents(4, row_index); 250 TObject misc = table.getCellContents(5, row_index); 251 252 TriggerInfo trigger_info = new TriggerInfo(); 253 trigger_info.schema = trig_schem.getObject().toString(); 254 trigger_info.name = trig_name.getObject().toString(); 255 trigger_info.type = type.toBigNumber().intValue(); 256 trigger_info.on_object = on_object.getObject().toString(); 257 trigger_info.action = action.getObject().toString(); 258 trigger_info.misc = misc; 259 260 triggers_active.add(trigger_info); 262 } 263 264 list_validated = true; 265 } 266 } 267 268 273 void performTriggerAction(TableModificationEvent evt) { 274 if (connection.tableExists(Database.SYS_DATA_TRIGGER)) { 279 buildTriggerList(); 281 282 TableName table_name = evt.getTableName(); 284 String on_ob_test = "T:" + table_name.toString(); 285 286 int sz = triggers_active.size(); 288 for (int i = 0; i < sz; ++i) { 289 TriggerInfo t_info = (TriggerInfo) triggers_active.get(i); 290 if (t_info.on_object.equals(on_ob_test)) { 291 if (evt.listenedBy(t_info.type)) { 295 String action = t_info.action; 298 ProcedureName procedure_name = 301 ProcedureName.qualify(table_name.getSchema(), action); 302 304 DatabaseConnection.OldNewTableState current_state = 306 connection.getOldNewTableState(); 307 308 connection.setOldNewTableState( 314 new DatabaseConnection.OldNewTableState(table_name, 315 evt.getRowIndex(), evt.getRowData(), evt.isBefore())); 316 317 try { 318 connection.getProcedureManager().invokeProcedure( 320 procedure_name, new TObject[0]); 321 } 322 finally { 323 connection.setOldNewTableState(current_state); 325 } 326 327 } 328 329 } 330 331 } 333 } 334 335 } 336 337 342 static InternalTableInfo createInternalTableInfo(Transaction transaction) { 343 return new TriggerInternalTableInfo(transaction); 344 } 345 346 348 352 private class CTMBackedCache extends TableBackedCache { 353 354 357 public CTMBackedCache() { 358 super(Database.SYS_DATA_TRIGGER); 359 } 360 361 public void purgeCacheOfInvalidatedEntries( 362 IntegerVector added_rows, IntegerVector removed_rows) { 363 365 if (trigger_modified) { 368 invalidateTriggerList(); 369 trigger_modified = false; 370 } 371 else if ((removed_rows != null && removed_rows.size() > 0) || 374 (added_rows != null && added_rows.size() > 0)) { 375 invalidateTriggerList(); 376 } 377 } 378 379 } 380 381 384 private class TriggerInfo { 385 String schema; 386 String name; 387 int type; 388 String on_object; 389 String action; 390 TObject misc; 391 } 392 393 397 private static class TriggerInternalTableInfo 398 extends AbstractInternalTableInfo2 { 399 400 TriggerInternalTableInfo(Transaction transaction) { 401 super(transaction, Database.SYS_DATA_TRIGGER); 402 } 403 404 private static DataTableDef createDataTableDef(String schema, String name) { 405 DataTableDef def = new DataTableDef(); 407 def.setTableName(new TableName(schema, name)); 408 409 def.addColumn(DataTableColumnDef.createNumericColumn("type")); 411 def.addColumn(DataTableColumnDef.createStringColumn("on_object")); 412 def.addColumn(DataTableColumnDef.createStringColumn("procedure_name")); 413 def.addColumn(DataTableColumnDef.createStringColumn("param_args")); 414 def.addColumn(DataTableColumnDef.createStringColumn("owner")); 415 416 def.setImmutable(); 418 419 return def; 421 } 422 423 424 public String getTableType(int i) { 425 return "TRIGGER"; 426 } 427 428 public DataTableDef getDataTableDef(int i) { 429 TableName table_name = getTableName(i); 430 return createDataTableDef(table_name.getSchema(), table_name.getName()); 431 } 432 433 public MutableTableDataSource createInternalTable(int index) { 434 MutableTableDataSource table = 435 transaction.getTable(Database.SYS_DATA_TRIGGER); 436 RowEnumeration row_e = table.rowEnumeration(); 437 int p = 0; 438 int i; 439 int row_i = -1; 440 while (row_e.hasMoreRows()) { 441 i = row_e.nextRowIndex(); 442 if (p == index) { 443 row_i = i; 444 } 445 else { 446 ++p; 447 } 448 } 449 if (p == index) { 450 String schema = table.getCellContents(0, row_i).getObject().toString(); 451 String name = table.getCellContents(1, row_i).getObject().toString(); 452 453 final DataTableDef table_def = createDataTableDef(schema, name); 454 final TObject type = table.getCellContents(2, row_i); 455 final TObject on_object = table.getCellContents(3, row_i); 456 final TObject procedure_name = table.getCellContents(4, row_i); 457 final TObject param_args = table.getCellContents(5, row_i); 458 final TObject owner = table.getCellContents(6, row_i); 459 460 return new GTDataSource(transaction.getSystem()) { 463 public DataTableDef getDataTableDef() { 464 return table_def; 465 } 466 public int getRowCount() { 467 return 1; 468 } 469 public TObject getCellContents(int col, int row) { 470 switch (col) { 471 case 0: 472 return type; 473 case 1: 474 return on_object; 475 case 2: 476 return procedure_name; 477 case 3: 478 return param_args; 479 case 4: 480 return owner; 481 default: 482 throw new RuntimeException ("Column out of bounds."); 483 } 484 } 485 }; 486 487 } 488 else { 489 throw new RuntimeException ("Index out of bounds."); 490 } 491 492 } 493 494 } 495 496 } 497 498 | Popular Tags |