File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\table\Table.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.table;
8
9 import java.util.ArrayList;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Set;
13 import org.h2.command.Prepared;
14 import org.h2.constant.ErrorCode;
15 import org.h2.constraint.Constraint;
16 import org.h2.engine.Constants;
17 import org.h2.engine.DbObject;
18 import org.h2.engine.Right;
19 import org.h2.engine.Session;
20 import org.h2.engine.UndoLogRecord;
21 import org.h2.expression.Expression;
22 import org.h2.expression.ExpressionVisitor;
23 import org.h2.index.Index;
24 import org.h2.index.IndexType;
25 import org.h2.message.DbException;
26 import org.h2.message.Trace;
27 import org.h2.result.Row;
28 import org.h2.result.RowList;
29 import org.h2.result.SearchRow;
30 import org.h2.result.SimpleRow;
31 import org.h2.result.SimpleRowValue;
32 import org.h2.schema.Schema;
33 import org.h2.schema.SchemaObjectBase;
34 import org.h2.schema.Sequence;
35 import org.h2.schema.TriggerObject;
36 import org.h2.util.New;
37 import org.h2.value.CompareMode;
38 import org.h2.value.Value;
39 import org.h2.value.ValueNull;
40
41 /**
42 * This is the base class for most tables.
43 * A table contains a list of columns and a list of rows.
44 */
45 public abstract class Table extends SchemaObjectBase {
46
47 /**
48 * The table type that means this table is a regular persistent table.
49 */
50 public static final int TYPE_CACHED = 0;
51
52 /**
53 * The table type that means this table is a regular persistent table.
54 */
55 public static final int TYPE_MEMORY = 1;
56
57 /**
58 * The table type name for linked tables.
59 */
60 public static final String TABLE_LINK = "TABLE LINK";
61
62 /**
63 * The table type name for system tables.
64 */
65 public static final String SYSTEM_TABLE = "SYSTEM TABLE";
66
67 /**
68 * The table type name for regular data tables.
69 */
70 public static final String TABLE = "TABLE";
71
72 /**
73 * The table type name for views.
74 */
75 public static final String VIEW = "VIEW";
76
77 /**
78 * The table type name for external table engines.
79 */
80 public static final String EXTERNAL_TABLE_ENGINE = "EXTERNAL";
81
82 /**
83 * The columns of this table.
84 */
85 protected Column[] columns;
86
87 /**
88 * The compare mode used for this table.
89 */
90 protected CompareMode compareMode;
91
92 /**
93 * Protected tables are not listed in the meta data and are excluded when
94 * using the SCRIPT command.
95 */
96 protected boolean isHidden;
97
98 private final HashMap<String, Column> columnMap;
99 private final boolean persistIndexes;
100 private final boolean persistData;
101 private ArrayList<TriggerObject> triggers;
102 private ArrayList<Constraint> constraints;
103 private ArrayList<Sequence> sequences;
104 private ArrayList<TableView> views;
105 private boolean checkForeignKeyConstraints = true;
106 private boolean onCommitDrop, onCommitTruncate;
107 private Row nullRow;
108
109 public Table(Schema schema, int id, String name, boolean persistIndexes, boolean persistData) {
110 columnMap = schema.getDatabase().newStringMap();
111 initSchemaObjectBase(schema, id, name, Trace.TABLE);
112 this.persistIndexes = persistIndexes;
113 this.persistData = persistData;
114 compareMode = schema.getDatabase().getCompareMode();
115 }
116
117 public void rename(String newName) {
118 super.rename(newName);
119 if (constraints != null) {
120 for (int i = 0, size = constraints.size(); i < size; i++) {
121 Constraint constraint = constraints.get(i);
122 constraint.rebuild();
123 }
124 }
125 }
126
127 /**
128 * Lock the table for the given session.
129 * This method waits until the lock is granted.
130 *
131 * @param session the session
132 * @param exclusive true for write locks, false for read locks
133 * @param force lock even in the MVCC mode
134 * @throws DbException if a lock timeout occurred
135 */
136 public abstract void lock(Session session, boolean exclusive, boolean force);
137
138 /**
139 * Close the table object and flush changes.
140 *
141 * @param session the session
142 */
143 public abstract void close(Session session);
144
145 /**
146 * Release the lock for this session.
147 *
148 * @param s the session
149 */
150 public abstract void unlock(Session s);
151
152 /**
153 * Create an index for this table
154 *
155 * @param session the session
156 * @param indexName the name of the index
157 * @param indexId the id
158 * @param cols the index columns
159 * @param indexType the index type
160 * @param create whether this is a new index
161 * @param indexComment the comment
162 * @return the index
163 */
164 public abstract Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType,
165 boolean create, String indexComment);
166
167 /**
168 * Remove a row from the table and all indexes.
169 *
170 * @param session the session
171 * @param row the row
172 */
173 public abstract void removeRow(Session session, Row row);
174
175 /**
176 * Remove all rows from the table and indexes.
177 *
178 * @param session the session
179 */
180 public abstract void truncate(Session session);
181
182 /**
183 * Add a row to the table and all indexes.
184 *
185 * @param session the session
186 * @param row the row
187 * @throws DbException if a constraint was violated
188 */
189 public abstract void addRow(Session session, Row row);
190
191 /**
192 * Commit an operation (when using multi-version concurrency).
193 *
194 * @param operation the operation
195 * @param row the row
196 */
197 public void commit(short operation, Row row) {
198 // nothing to do
199 }
200
201 /**
202 * Check if this table supports ALTER TABLE.
203 *
204 * @throws DbException if it is not supported
205 */
206 public abstract void checkSupportAlter();
207
208 /**
209 * Get the table type name
210 *
211 * @return the table type name
212 */
213 public abstract String getTableType();
214
215 /**
216 * Get the scan index to iterate through all rows.
217 *
218 * @param session the session
219 * @return the index
220 */
221 public abstract Index getScanIndex(Session session);
222
223 /**
224 * Get any unique index for this table if one exists.
225 *
226 * @return a unique index
227 */
228 public abstract Index getUniqueIndex();
229
230 /**
231 * Get all indexes for this table.
232 *
233 * @return the list of indexes
234 */
235 public abstract ArrayList<Index> getIndexes();
236
237 /**
238 * Check if this table is locked exclusively.
239 *
240 * @return true if it is.
241 */
242 public abstract boolean isLockedExclusively();
243
244 /**
245 * Get the last data modification id.
246 *
247 * @return the modification id
248 */
249 public abstract long getMaxDataModificationId();
250
251 /**
252 * Check if the table is deterministic.
253 *
254 * @return true if it is
255 */
256 public abstract boolean isDeterministic();
257
258 /**
259 * Check if the row count can be retrieved quickly.
260 *
261 * @return true if it can
262 */
263 public abstract boolean canGetRowCount();
264
265 /**
266 * Check if this table can be referenced.
267 *
268 * @return true if it can
269 */
270 public boolean canReference() {
271 return true;
272 }
273
274 /**
275 * Check if this table can be dropped.
276 *
277 * @return true if it can
278 */
279 public abstract boolean canDrop();
280
281 /**
282 * Get the row count for this table.
283 *
284 * @param session the session
285 * @return the row count
286 */
287 public abstract long getRowCount(Session session);
288
289 /**
290 * Get the approximated row count for this table.
291 *
292 * @return the approximated row count
293 */
294 public abstract long getRowCountApproximation();
295
296 /**
297 * Get the row id column if this table has one.
298 *
299 * @return the row id column, or null
300 */
301 public Column getRowIdColumn() {
302 return null;
303 }
304
305 public String getCreateSQLForCopy(Table table, String quotedName) {
306 throw DbException.throwInternalError();
307 }
308
309 /**
310 * Add all objects that this table depends on to the hash set.
311 *
312 * @param dependencies the current set of dependencies
313 */
314 public void addDependencies(HashSet<DbObject> dependencies) {
315 if (dependencies.contains(this)) {
316 // avoid endless recursion
317 return;
318 }
319 if (sequences != null) {
320 for (Sequence s : sequences) {
321 dependencies.add(s);
322 }
323 }
324 ExpressionVisitor visitor = ExpressionVisitor.getDependenciesVisitor(dependencies);
325 for (Column col : columns) {
326 col.isEverything(visitor);
327 }
328 if (constraints != null) {
329 for (Constraint c : constraints) {
330 c.isEverything(visitor);
331 }
332 }
333 dependencies.add(this);
334 }
335
336 public ArrayList<DbObject> getChildren() {
337 ArrayList<DbObject> children = New.arrayList();
338 ArrayList<Index> indexes = getIndexes();
339 if (indexes != null) {
340 children.addAll(indexes);
341 }
342 if (constraints != null) {
343 children.addAll(constraints);
344 }
345 if (triggers != null) {
346 children.addAll(triggers);
347 }
348 if (sequences != null) {
349 children.addAll(sequences);
350 }
351 if (views != null) {
352 children.addAll(views);
353 }
354 ArrayList<Right> rights = database.getAllRights();
355 for (Right right : rights) {
356 if (right.getGrantedTable() == this) {
357 children.add(right);
358 }
359 }
360 return children;
361 }
362
363 protected void setColumns(Column[] columns) {
364 this.columns = columns;
365 if (columnMap.size() > 0) {
366 columnMap.clear();
367 }
368 for (int i = 0; i < columns.length; i++) {
369 Column col = columns[i];
370 int dataType = col.getType();
371 if (dataType == Value.UNKNOWN) {
372 throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, col.getSQL());
373 }
374 col.setTable(this, i);
375 String columnName = col.getName();
376 if (columnMap.get(columnName) != null) {
377 throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1, columnName);
378 }
379 columnMap.put(columnName, col);
380 }
381 }
382
383 /**
384 * Rename a column of this table.
385 *
386 * @param column the column to rename
387 * @param newName the new column name
388 */
389 public void renameColumn(Column column, String newName) {
390 for (Column c : columns) {
391 if (c == column) {
392 continue;
393 }
394 if (c.getName().equals(newName)) {
395 throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1, newName);
396 }
397 }
398 columnMap.remove(column.getName());
399 column.rename(newName);
400 columnMap.put(newName, column);
401 }
402
403 /**
404 * Check if the table is exclusively locked by this session.
405 *
406 * @param session the session
407 * @return true if it is
408 */
409 public boolean isLockedExclusivelyBy(Session session) {
410 return false;
411 }
412
413 /**
414 * Update a list of rows in this table.
415 *
416 * @param prepared the prepared statement
417 * @param session the session
418 * @param rows a list of row pairs of the form old row, new row, old row,
419 * new row,...
420 */
421 public void updateRows(Prepared prepared, Session session, RowList rows) {
422 // in case we need to undo the update
423 int rollback = session.getUndoLogPos();
424 // remove the old rows
425 int rowScanCount = 0;
426 for (rows.reset(); rows.hasNext();) {
427 if ((++rowScanCount & 127) == 0) {
428 prepared.checkCanceled();
429 }
430 Row o = rows.next();
431 rows.next();
432 removeRow(session, o);
433 session.log(this, UndoLogRecord.DELETE, o);
434 }
435 // add the new rows
436 for (rows.reset(); rows.hasNext();) {
437 if ((++rowScanCount & 127) == 0) {
438 prepared.checkCanceled();
439 }
440 rows.next();
441 Row n = rows.next();
442 try {
443 addRow(session, n);
444 } catch (DbException e) {
445 if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1) {
446 session.rollbackTo(rollback, false);
447 }
448 throw e;
449 }
450 session.log(this, UndoLogRecord.INSERT, n);
451 }
452 }
453
454 public ArrayList<TableView> getViews() {
455 return views;
456 }
457
458 public void removeChildrenAndResources(Session session) {
459 while (views != null && views.size() > 0) {
460 TableView view = views.get(0);
461 views.remove(0);
462 database.removeSchemaObject(session, view);
463 }
464 while (triggers != null && triggers.size() > 0) {
465 TriggerObject trigger = triggers.get(0);
466 triggers.remove(0);
467 database.removeSchemaObject(session, trigger);
468 }
469 while (constraints != null && constraints.size() > 0) {
470 Constraint constraint = constraints.get(0);
471 constraints.remove(0);
472 database.removeSchemaObject(session, constraint);
473 }
474 for (Right right : database.getAllRights()) {
475 if (right.getGrantedTable() == this) {
476 database.removeDatabaseObject(session, right);
477 }
478 }
479 database.removeMeta(session, getId());
480 // must delete sequences later (in case there is a power failure
481 // before removing the table object)
482 while (sequences != null && sequences.size() > 0) {
483 Sequence sequence = sequences.get(0);
484 sequences.remove(0);
485 if (!isTemporary()) {
486 // only remove if no other table depends on this sequence
487 // this is possible when calling ALTER TABLE ALTER COLUMN
488 if (database.getDependentTable(sequence, this) == null) {
489 database.removeSchemaObject(session, sequence);
490 }
491 }
492 }
493 }
494
495 /**
496 * Check that this column is not referenced by a multi-column constraint or
497 * multi-column index. If it is, an exception is thrown. Single-column
498 * references and indexes are dropped.
499 *
500 * @param session the session
501 * @param col the column
502 * @throws DbException if the column is referenced by multi-column
503 * constraints or indexes
504 */
505 public void dropSingleColumnConstraintsAndIndexes(Session session, Column col) {
506 ArrayList<Constraint> constraintsToDrop = New.arrayList();
507 if (constraints != null) {
508 for (int i = 0, size = constraints.size(); i < size; i++) {
509 Constraint constraint = constraints.get(i);
510 HashSet<Column> columns = constraint.getReferencedColumns(this);
511 if (!columns.contains(col)) {
512 continue;
513 }
514 if (columns.size() == 1) {
515 constraintsToDrop.add(constraint);
516 } else {
517 throw DbException.get(ErrorCode.COLUMN_IS_REFERENCED_1, constraint.getSQL());
518 }
519 }
520 }
521 ArrayList<Index> indexesToDrop = New.arrayList();
522 ArrayList<Index> indexes = getIndexes();
523 if (indexes != null) {
524 for (int i = 0, size = indexes.size(); i < size; i++) {
525 Index index = indexes.get(i);
526 if (index.getCreateSQL() == null) {
527 continue;
528 }
529 if (index.getColumnIndex(col) < 0) {
530 continue;
531 }
532 if (index.getColumns().length == 1) {
533 indexesToDrop.add(index);
534 } else {
535 throw DbException.get(ErrorCode.COLUMN_IS_REFERENCED_1, index.getSQL());
536 }
537 }
538 }
539 for (Constraint c : constraintsToDrop) {
540 session.getDatabase().removeSchemaObject(session, c);
541 }
542 for (Index i : indexesToDrop) {
543 // the index may already have been dropped when dropping the constraint
544 if (getIndexes().contains(i)) {
545 session.getDatabase().removeSchemaObject(session, i);
546 }
547 }
548 }
549
550 public Row getTemplateRow() {
551 return new Row(new Value[columns.length], Row.MEMORY_CALCULATE);
552 }
553
554 /**
555 * Get a new simple row object.
556 *
557 * @param singleColumn if only one value need to be stored
558 * @return the simple row object
559 */
560 public SearchRow getTemplateSimpleRow(boolean singleColumn) {
561 if (singleColumn) {
562 return new SimpleRowValue(columns.length);
563 }
564 return new SimpleRow(new Value[columns.length]);
565 }
566
567 synchronized Row getNullRow() {
568 if (nullRow == null) {
569 nullRow = new Row(new Value[columns.length], 1);
570 for (int i = 0; i < columns.length; i++) {
571 nullRow.setValue(i, ValueNull.INSTANCE);
572 }
573 }
574 return nullRow;
575 }
576
577 public Column[] getColumns() {
578 return columns;
579 }
580
581 public int getType() {
582 return DbObject.TABLE_OR_VIEW;
583 }
584
585 /**
586 * Get the column at the given index.
587 *
588 * @param index the column index (0, 1,...)
589 * @return the column
590 */
591 public Column getColumn(int index) {
592 return columns[index];
593 }
594
595 /**
596 * Get the column with the given name.
597 *
598 * @param columnName the column name
599 * @return the column
600 * @throws DbException if the column was not found
601 */
602 public Column getColumn(String columnName) {
603 Column column = columnMap.get(columnName);
604 if (column == null) {
605 throw DbException.get(ErrorCode.COLUMN_NOT_FOUND_1, columnName);
606 }
607 return column;
608 }
609
610 /**
611 * Does the column with the given name exist?
612 *
613 * @param columnName the column name
614 * @return true if the column exists
615 */
616 public boolean doesColumnExist(String columnName) {
617 return columnMap.containsKey(columnName);
618 }
619
620 /**
621 * Get the best plan for the given search mask.
622 *
623 * @param session the session
624 * @param masks null means 'always false'
625 * @return the plan item
626 */
627 public PlanItem getBestPlanItem(Session session, int[] masks) {
628 PlanItem item = new PlanItem();
629 item.setIndex(getScanIndex(session));
630 item.cost = item.getIndex().getCost(session, null);
631 ArrayList<Index> indexes = getIndexes();
632 if (indexes != null && masks != null) {
633 for (int i = 1, size = indexes.size(); i < size; i++) {
634 Index index = indexes.get(i);
635 double cost = index.getCost(session, masks);
636 if (cost < item.cost) {
637 item.cost = cost;
638 item.setIndex(index);
639 }
640 }
641 }
642 return item;
643 }
644
645 /**
646 * Get the primary key index if there is one, or null if there is none.
647 *
648 * @return the primary key index or null
649 */
650 public Index findPrimaryKey() {
651 ArrayList<Index> indexes = getIndexes();
652 if (indexes != null) {
653 for (int i = 0, size = indexes.size(); i < size; i++) {
654 Index idx = indexes.get(i);
655 if (idx.getIndexType().isPrimaryKey()) {
656 return idx;
657 }
658 }
659 }
660 return null;
661 }
662
663 public Index getPrimaryKey() {
664 Index index = findPrimaryKey();
665 if (index != null) {
666 return index;
667 }
668 throw DbException.get(ErrorCode.INDEX_NOT_FOUND_1, Constants.PREFIX_PRIMARY_KEY);
669 }
670
671 /**
672 * Validate all values in this row, convert the values if required, and
673 * update the sequence values if required. This call will also set the
674 * default values if required and set the computed column if there are any.
675 *
676 * @param session the session
677 * @param row the row
678 */
679 public void validateConvertUpdateSequence(Session session, Row row) {
680 for (int i = 0; i < columns.length; i++) {
681 Value value = row.getValue(i);
682 Column column = columns[i];
683 Value v2;
684 if (column.getComputed()) {
685 // force updating the value
686 value = null;
687 v2 = column.computeValue(session, row);
688 }
689 v2 = column.validateConvertUpdateSequence(session, value);
690 if (v2 != value) {
691 row.setValue(i, v2);
692 }
693 }
694 }
695
696 private static void remove(ArrayList<? extends DbObject> list, DbObject obj) {
697 if (list != null) {
698 int i = list.indexOf(obj);
699 if (i >= 0) {
700 list.remove(i);
701 }
702 }
703 }
704
705 /**
706 * Remove the given index from the list.
707 *
708 * @param index the index to remove
709 */
710 public void removeIndex(Index index) {
711 ArrayList<Index> indexes = getIndexes();
712 if (indexes != null) {
713 remove(indexes, index);
714 if (index.getIndexType().isPrimaryKey()) {
715 for (Column col : index.getColumns()) {
716 col.setPrimaryKey(false);
717 }
718 }
719 }
720 }
721
722 /**
723 * Remove the given view from the list.
724 *
725 * @param view the view to remove
726 */
727 public void removeView(TableView view) {
728 remove(views, view);
729 }
730
731 /**
732 * Remove the given constraint from the list.
733 *
734 * @param constraint the constraint to remove
735 */
736 public void removeConstraint(Constraint constraint) {
737 remove(constraints, constraint);
738 }
739
740 /**
741 * Remove a sequence from the table. Sequences are used as identity columns.
742 *
743 * @param sequence the sequence to remove
744 */
745 public final void removeSequence(Sequence sequence) {
746 remove(sequences, sequence);
747 }
748
749 /**
750 * Remove the given trigger from the list.
751 *
752 * @param trigger the trigger to remove
753 */
754 public void removeTrigger(TriggerObject trigger) {
755 remove(triggers, trigger);
756 }
757
758 /**
759 * Add a view to this table.
760 *
761 * @param view the view to add
762 */
763 public void addView(TableView view) {
764 views = add(views, view);
765 }
766
767 /**
768 * Add a constraint to the table.
769 *
770 * @param constraint the constraint to add
771 */
772 public void addConstraint(Constraint constraint) {
773 if (constraints == null || constraints.indexOf(constraint) < 0) {
774 constraints = add(constraints, constraint);
775 }
776 }
777
778 public ArrayList<Constraint> getConstraints() {
779 return constraints;
780 }
781
782 /**
783 * Add a sequence to this table.
784 *
785 * @param sequence the sequence to add
786 */
787 public void addSequence(Sequence sequence) {
788 sequences = add(sequences, sequence);
789 }
790
791 /**
792 * Add a trigger to this table.
793 *
794 * @param trigger the trigger to add
795 */
796 public void addTrigger(TriggerObject trigger) {
797 triggers = add(triggers, trigger);
798 }
799
800 private static <T> ArrayList<T> add(ArrayList<T> list, T obj) {
801 if (list == null) {
802 list = New.arrayList();
803 }
804 // self constraints are two entries in the list
805 list.add(obj);
806 return list;
807 }
808
809 /**
810 * Fire the triggers for this table.
811 *
812 * @param session the session
813 * @param type the trigger type
814 * @param beforeAction whether 'before' triggers should be called
815 */
816 public void fire(Session session, int type, boolean beforeAction) {
817 if (triggers != null) {
818 for (TriggerObject trigger : triggers) {
819 trigger.fire(session, type, beforeAction);
820 }
821 }
822 }
823
824 /**
825 * Check whether this table has a select trigger.
826 *
827 * @return true if it has
828 */
829 public boolean hasSelectTrigger() {
830 if (triggers != null) {
831 for (TriggerObject trigger : triggers) {
832 if (trigger.isSelectTrigger()) {
833 return true;
834 }
835 }
836 }
837 return false;
838 }
839
840 /**
841 * Check if row based triggers or constraints are defined.
842 * In this case the fire after and before row methods need to be called.
843 *
844 * @return if there are any triggers or rows defined
845 */
846 public boolean fireRow() {
847 return (constraints != null && constraints.size() > 0) || (triggers != null && triggers.size() > 0);
848 }
849
850 /**
851 * Fire all triggers that need to be called before a row is updated.
852 *
853 * @param session the session
854 * @param oldRow the old data or null for an insert
855 * @param newRow the new data or null for a delete
856 * @return true if no further action is required (for 'instead of' triggers)
857 */
858 public boolean fireBeforeRow(Session session, Row oldRow, Row newRow) {
859 boolean done = fireRow(session, oldRow, newRow, true, false);
860 fireConstraints(session, oldRow, newRow, true);
861 return done;
862 }
863
864 private void fireConstraints(Session session, Row oldRow, Row newRow, boolean before) {
865 if (constraints != null) {
866 // don't use enhanced for loop to avoid creating objects
867 for (int i = 0, size = constraints.size(); i < size; i++) {
868 Constraint constraint = constraints.get(i);
869 if (constraint.isBefore() == before) {
870 constraint.checkRow(session, this, oldRow, newRow);
871 }
872 }
873 }
874 }
875
876 /**
877 * Fire all triggers that need to be called after a row is updated.
878 *
879 * @param session the session
880 * @param oldRow the old data or null for an insert
881 * @param newRow the new data or null for a delete
882 * @param rollback when the operation occurred within a rollback
883 */
884 public void fireAfterRow(Session session, Row oldRow, Row newRow, boolean rollback) {
885 fireRow(session, oldRow, newRow, false, rollback);
886 if (!rollback) {
887 fireConstraints(session, oldRow, newRow, false);
888 }
889 }
890
891 private boolean fireRow(Session session, Row oldRow, Row newRow, boolean beforeAction, boolean rollback) {
892 if (triggers != null) {
893 for (TriggerObject trigger : triggers) {
894 boolean done = trigger.fireRow(session, oldRow, newRow, beforeAction, rollback);
895 if (done) {
896 return true;
897 }
898 }
899 }
900 return false;
901 }
902
903 public boolean isGlobalTemporary() {
904 return false;
905 }
906
907 /**
908 * Check if this table can be truncated.
909 *
910 * @return true if it can
911 */
912 public boolean canTruncate() {
913 return false;
914 }
915
916 /**
917 * Enable or disable foreign key constraint checking for this table.
918 *
919 * @param session the session
920 * @param enabled true if checking should be enabled
921 * @param checkExisting true if existing rows must be checked during this call
922 */
923 public void setCheckForeignKeyConstraints(Session session, boolean enabled, boolean checkExisting)
924 {
925 if (enabled && checkExisting) {
926 if (constraints != null) {
927 for (Constraint c : constraints) {
928 c.checkExistingData(session);
929 }
930 }
931 }
932 checkForeignKeyConstraints = enabled;
933 }
934
935 public boolean getCheckForeignKeyConstraints() {
936 return checkForeignKeyConstraints;
937 }
938
939 /**
940 * Get the index that has the given column as the first element.
941 * This method returns null if no matching index is found.
942 *
943 * @param column the column
944 * @return the index or null
945 */
946 public Index getIndexForColumn(Column column) {
947 ArrayList<Index> indexes = getIndexes();
948 if (indexes != null) {
949 for (int i = 1, size = indexes.size(); i < size; i++) {
950 Index index = indexes.get(i);
951 if (index.canGetFirstOrLast()) {
952 int idx = index.getColumnIndex(column);
953 if (idx == 0) {
954 return index;
955 }
956 }
957 }
958 }
959 return null;
960 }
961
962 public boolean getOnCommitDrop() {
963 return onCommitDrop;
964 }
965
966 public void setOnCommitDrop(boolean onCommitDrop) {
967 this.onCommitDrop = onCommitDrop;
968 }
969
970 public boolean getOnCommitTruncate() {
971 return onCommitTruncate;
972 }
973
974 public void setOnCommitTruncate(boolean onCommitTruncate) {
975 this.onCommitTruncate = onCommitTruncate;
976 }
977
978 /**
979 * If the index is still required by a constraint, transfer the ownership to
980 * it. Otherwise, the index is removed.
981 *
982 * @param session the session
983 * @param index the index that is no longer required
984 */
985 public void removeIndexOrTransferOwnership(Session session, Index index) {
986 boolean stillNeeded = false;
987 if (constraints != null) {
988 for (Constraint cons : constraints) {
989 if (cons.usesIndex(index)) {
990 cons.setIndexOwner(index);
991 database.update(session, cons);
992 stillNeeded = true;
993 }
994 }
995 }
996 if (!stillNeeded) {
997 database.removeSchemaObject(session, index);
998 }
999 }
1000
1001 /**
1002 * Check if a deadlock occurred. This method is called recursively. There is
1003 * a circle if the session to be tested has already being visited. If this
1004 * session is part of the circle (if it is the clash session), the method
1005 * must return an empty object array. Once a deadlock has been detected, the
1006 * methods must add the session to the list. If this session is not part of
1007 * the circle, or if no deadlock is detected, this method returns null.
1008 *
1009 * @param session the session to be tested for
1010 * @param clash set with sessions already visited, and null when starting
1011 * verification
1012 * @param visited set with sessions already visited, and null when starting
1013 * verification
1014 * @return an object array with the sessions involved in the deadlock, or
1015 * null
1016 */
1017 public ArrayList<Session> checkDeadlock(Session session, Session clash, Set<Session> visited) {
1018 return null;
1019 }
1020
1021 public boolean isPersistIndexes() {
1022 return persistIndexes;
1023 }
1024
1025 public boolean isPersistData() {
1026 return persistData;
1027 }
1028
1029 /**
1030 * Compare two values with the current comparison mode. The values may be of
1031 * different type.
1032 *
1033 * @param a the first value
1034 * @param b the second value
1035 * @return 0 if both values are equal, -1 if the first value is smaller, and
1036 * 1 otherwise
1037 */
1038 public int compareTypeSave(Value a, Value b) {
1039 if (a == b) {
1040 return 0;
1041 }
1042 int dataType = Value.getHigherOrder(a.getType(), b.getType());
1043 a = a.convertTo(dataType);
1044 b = b.convertTo(dataType);
1045 return a.compareTypeSave(b, compareMode);
1046 }
1047
1048 public CompareMode getCompareMode() {
1049 return compareMode;
1050 }
1051
1052 /**
1053 * Tests if the table can be written. Usually, this depends on the
1054 * database.checkWritingAllowed method, but some tables (eg. TableLink)
1055 * overwrite this default behaviour.
1056 */
1057 public void checkWritingAllowed() {
1058 database.checkWritingAllowed();
1059 }
1060
1061 /**
1062 * Get or generate a default value for the given column.
1063 *
1064 * @param session the session
1065 * @param column the column
1066 * @return the value
1067 */
1068 public Value getDefaultValue(Session session, Column column) {
1069 Expression defaultExpr = column.getDefaultExpression();
1070 Value v;
1071 if (defaultExpr == null) {
1072 v = column.validateConvertUpdateSequence(session, null);
1073 } else {
1074 v = defaultExpr.getValue(session);
1075 }
1076 return column.convert(v);
1077 }
1078
1079 public boolean isHidden() {
1080 return isHidden;
1081 }
1082
1083 public void setHidden(boolean hidden) {
1084 this.isHidden = hidden;
1085 }
1086
1087 }
1088