KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > Table


1 /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the Hypersonic SQL Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * This software consists of voluntary contributions made by many individuals
31  * on behalf of the Hypersonic SQL Group.
32  *
33  *
34  * For work added by the HSQL Development Group:
35  *
36  * Copyright (c) 2001-2005, The HSQL Development Group
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  * Redistributions of source code must retain the above copyright notice, this
43  * list of conditions and the following disclaimer.
44  *
45  * Redistributions in binary form must reproduce the above copyright notice,
46  * this list of conditions and the following disclaimer in the documentation
47  * and/or other materials provided with the distribution.
48  *
49  * Neither the name of the HSQL Development Group nor the names of its
50  * contributors may be used to endorse or promote products derived from this
51  * software without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
57  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
60  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64  */

65
66
67 package org.hsqldb;
68
69 import java.io.IOException JavaDoc;
70
71 import org.hsqldb.HsqlNameManager.HsqlName;
72 import org.hsqldb.index.RowIterator;
73 import org.hsqldb.lib.ArrayUtil;
74 import org.hsqldb.lib.HashMappedList;
75 import org.hsqldb.lib.HashSet;
76 import org.hsqldb.lib.HsqlArrayList;
77 import org.hsqldb.lib.Iterator;
78 import org.hsqldb.lib.StringUtil;
79 import org.hsqldb.persist.CachedObject;
80 import org.hsqldb.persist.DataFileCache;
81 import org.hsqldb.persist.PersistentStore;
82 import org.hsqldb.rowio.RowInputInterface;
83 import org.hsqldb.store.ValuePool;
84
85 // fredt@users 20020130 - patch 491987 by jimbag@users - made optional
86
// fredt@users 20020405 - patch 1.7.0 by fredt - quoted identifiers
87
// for sql standard quoted identifiers for column and table names and aliases
88
// applied to different places
89
// fredt@users 20020225 - patch 1.7.0 - restructuring
90
// some methods moved from Database.java, some rewritten
91
// changes to several methods
92
// fredt@users 20020225 - patch 1.7.0 - ON DELETE CASCADE
93
// fredt@users 20020225 - patch 1.7.0 - named constraints
94
// boucherb@users 20020225 - patch 1.7.0 - multi-column primary keys
95
// fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
96
// tony_lai@users 20020820 - patch 595099 - user defined PK name
97
// tony_lai@users 20020820 - patch 595172 - drop constraint fix
98
// kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE | SET NULL | SET DEFAULT
99
// kloska@users 20021112 - patch 1.7.2 - ON DELETE SET NULL | SET DEFAULT
100
// fredt@users 20021210 - patch 1.7.2 - better ADD / DROP INDEX for non-CACHED tables
101
// fredt@users 20030901 - patch 1.7.2 - allow multiple nulls for UNIQUE columns
102
// fredt@users 20030901 - patch 1.7.2 - reworked IDENTITY support
103
// achnettest@users 20040130 - patch 878288 - bug fix for new indexes in memory tables by Arne Christensen
104
// boucherb@users 20040327 - doc 1.7.2 - javadoc updates
105
// boucherb@users 200404xx - patch 1.7.2 - proper uri for getCatalogName
106
// fredt@users 20050000 - 1.8.0 updates in several areas
107
// fredt@users 20050220 - patch 1.8.0 enforcement of DECIMAL precision/scale
108

109 /**
110  * Holds the data structures and methods for creation of a database table.
111  *
112  *
113  * Extensively rewritten and extended in successive versions of HSQLDB.
114  *
115  * @author Thomas Mueller (Hypersonic SQL Group)
116  * @version 1.8.0
117  * @since Hypersonic SQL
118  */

119 public class Table extends BaseTable {
120
121     // types of table
122
public static final int SYSTEM_TABLE = 0;
123     public static final int SYSTEM_SUBQUERY = 1;
124     public static final int TEMP_TABLE = 2;
125     public static final int MEMORY_TABLE = 3;
126     public static final int CACHED_TABLE = 4;
127     public static final int TEMP_TEXT_TABLE = 5;
128     public static final int TEXT_TABLE = 6;
129     public static final int VIEW = 7;
130
131 // boucherb@users - for future implementation of SQL standard INFORMATION_SCHEMA
132
static final int SYSTEM_VIEW = 8;
133
134     // main properties
135
// boucherb@users - access changed in support of metadata 1.7.2
136
public HashMappedList columnList; // columns in table
137
private int[] primaryKeyCols; // column numbers for primary key
138
private int[] primaryKeyTypes; // types for primary key
139
private int[] primaryKeyColsSequence; // {0,1,2,...}
140
int[] bestRowIdentifierCols; // column set for best index
141
boolean bestRowIdentifierStrict; // true if it has no nullable column
142
int[] bestIndexForColumn; // index of the 'best' index for each column
143
Index bestIndex; // the best index overall - null if there is no user-defined index
144
int identityColumn; // -1 means no such row
145
NumberSequence identitySequence; // next value of identity column
146
NumberSequence rowIdSequence; // next value of optional rowid
147

148 // -----------------------------------------------------------------------
149
Constraint[] constraintList; // constrainst for the table
150
HsqlArrayList[] triggerLists; // array of trigger lists
151
private int[] colTypes; // fredt - types of columns
152
private int[] colSizes; // fredt - copy of SIZE values for columns
153
private int[] colScales; // fredt - copy of SCALE values for columns
154
private boolean[] colNullable; // fredt - modified copy of isNullable() values
155
private Expression[] colDefaults; // fredt - expressions of DEFAULT values
156
private int[] defaultColumnMap; // fred - holding 0,1,2,3,...
157
private boolean hasDefaultValues; //fredt - shortcut for above
158
boolean sqlEnforceSize; // inherited from the database -
159

160     // properties for subclasses
161
protected int columnCount; // inclusive the hidden primary key
162
public Database database;
163     protected DataFileCache cache;
164     protected HsqlName tableName; // SQL name
165
private int tableType;
166     protected boolean isReadOnly;
167     protected boolean isTemp;
168     protected boolean isCached;
169     protected boolean isText;
170     protected boolean isMemory;
171     private boolean isView;
172     protected boolean isLogged;
173     protected int indexType; // fredt - type of index used
174
protected boolean onCommitPreserve; // for temp tables
175

176     //
177
PersistentStore rowStore;
178     Index[] indexList; // vIndex(0) is the primary key index
179

180     /**
181      * Constructor
182      *
183      * @param db
184      * @param name
185      * @param type
186      * @param sessionid
187      * @exception HsqlException
188      */

189     Table(Database db, HsqlName name, int type) throws HsqlException {
190
191         database = db;
192         sqlEnforceSize = db.sqlEnforceStrictSize;
193         identitySequence = new NumberSequence(null, 0, 1, Types.BIGINT);
194         rowIdSequence = new NumberSequence(null, 0, 1, Types.BIGINT);
195
196         switch (type) {
197
198             case SYSTEM_SUBQUERY :
199                 isTemp = true;
200                 isMemory = true;
201             case SYSTEM_TABLE :
202                 isMemory = true;
203                 break;
204
205             case CACHED_TABLE :
206                 if (DatabaseURL.isFileBasedDatabaseType(db.getType())) {
207                     cache = db.logger.getCache();
208                     isCached = true;
209                     isLogged = !database.isFilesReadOnly();
210                     indexType = Index.DISK_INDEX;
211                     rowStore = new RowStore();
212
213                     break;
214                 }
215
216                 type = MEMORY_TABLE;
217             case MEMORY_TABLE :
218                 isMemory = true;
219                 isLogged = !database.isFilesReadOnly();
220                 break;
221
222             case TEMP_TABLE :
223                 isMemory = true;
224                 isTemp = true;
225                 break;
226
227             case TEMP_TEXT_TABLE :
228                 if (!DatabaseURL.isFileBasedDatabaseType(db.getType())) {
229                     throw Trace.error(Trace.DATABASE_IS_MEMORY_ONLY);
230                 }
231
232                 isTemp = true;
233                 isText = true;
234                 isReadOnly = true;
235                 indexType = Index.POINTER_INDEX;
236                 rowStore = new RowStore();
237                 break;
238
239             case TEXT_TABLE :
240                 if (!DatabaseURL.isFileBasedDatabaseType(db.getType())) {
241                     throw Trace.error(Trace.DATABASE_IS_MEMORY_ONLY);
242                 }
243
244                 isText = true;
245                 indexType = Index.POINTER_INDEX;
246                 rowStore = new RowStore();
247                 break;
248
249             case VIEW :
250             case SYSTEM_VIEW :
251                 isView = true;
252                 break;
253         }
254
255         // type may have changed above for CACHED tables
256
tableType = type;
257         tableName = name;
258         primaryKeyCols = null;
259         primaryKeyTypes = null;
260         identityColumn = -1;
261         columnList = new HashMappedList();
262         indexList = new Index[0];
263         constraintList = new Constraint[0];
264         triggerLists = new HsqlArrayList[TriggerDef.NUM_TRIGS];
265
266 // ----------------------------------------------------------------------------
267
// akede@users - 1.7.2 patch Files readonly
268
// Changing the mode of the table if necessary
269
if (db.isFilesReadOnly() && isFileBased()) {
270             this.isReadOnly = true;
271         }
272
273 // ----------------------------------------------------------------------------
274
}
275
276     boolean equals(Session session, String JavaDoc name) {
277
278 /*
279         if (isTemp && (session != null
280                        && session.getId() != ownerSessionId)) {
281             return false;
282         }
283 */

284         return (tableName.name.equals(name));
285     }
286
287     boolean equals(String JavaDoc name) {
288         return (tableName.name.equals(name));
289     }
290
291     boolean equals(HsqlName name) {
292         return (tableName.equals(name));
293     }
294
295     public final boolean isText() {
296         return isText;
297     }
298
299     public final boolean isTemp() {
300         return isTemp;
301     }
302
303     public final boolean isReadOnly() {
304         return isReadOnly;
305     }
306
307     final boolean isView() {
308         return isView;
309     }
310
311     final int getIndexType() {
312         return indexType;
313     }
314
315     public final int getTableType() {
316         return tableType;
317     }
318
319     public final boolean isDataReadOnly() {
320         return isReadOnly;
321     }
322
323     /**
324      * Used by INSERT, DELETE, UPDATE operations
325      */

326     void checkDataReadOnly() throws HsqlException {
327
328         if (isReadOnly) {
329             throw Trace.error(Trace.DATA_IS_READONLY);
330         }
331     }
332
333 // ----------------------------------------------------------------------------
334
// akede@users - 1.7.2 patch Files readonly
335
void setDataReadOnly(boolean value) throws HsqlException {
336
337         // Changing the Read-Only mode for the table is only allowed if the
338
// the database can realize it.
339
if (!value && database.isFilesReadOnly() && isFileBased()) {
340             throw Trace.error(Trace.DATA_IS_READONLY);
341         }
342
343         isReadOnly = value;
344     }
345
346     /**
347      * Text or Cached Tables are normally file based
348      */

349     boolean isFileBased() {
350         return isCached || isText;
351     }
352
353     /**
354      * For text tables
355      */

356     protected void setDataSource(Session s, String JavaDoc source, boolean isDesc,
357                                  boolean newFile) throws HsqlException {
358         throw (Trace.error(Trace.TABLE_NOT_FOUND));
359     }
360
361     /**
362      * For text tables
363      */

364     protected String JavaDoc getDataSource() {
365         return null;
366     }
367
368     /**
369      * For text tables.
370      */

371     protected boolean isDescDataSource() {
372         return false;
373     }
374
375     /**
376      * For text tables.
377      */

378     public void setHeader(String JavaDoc header) throws HsqlException {
379         throw Trace.error(Trace.TEXT_TABLE_HEADER);
380     }
381
382     /**
383      * For text tables.
384      */

385     public String JavaDoc getHeader() {
386         return null;
387     }
388
389     /**
390      * Adds a constraint.
391      */

392     void addConstraint(Constraint c) {
393
394         constraintList =
395             (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, c,
396                 constraintList.length, 1);
397     }
398
399     /**
400      * Adds a constraint.
401      */

402     void addPKConstraint(Constraint c) {
403         constraintList =
404             (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, c, 0, 1);
405     }
406
407     /**
408      * Returns the list of constraints.
409      */

410     Constraint[] getConstraints() {
411         return constraintList;
412     }
413
414     /**
415      * Returns the primary constraint.
416      */

417     Constraint getPrimaryConstraint() {
418         return primaryKeyCols.length == 0 ? null
419                                           : constraintList[0];
420     }
421
422 /** @todo fredt - this can be improved to ignore order of columns in
423      * multi-column indexes */

424
425     /**
426      * Returns the index supporting a constraint with the given column signature.
427      * Only Unique constraints are considered.
428      */

429     Index getUniqueConstraintIndexForColumns(int[] col) {
430
431         if (ArrayUtil.areEqual(getPrimaryIndex().getColumns(), col,
432                                col.length, true)) {
433             return getPrimaryIndex();
434         }
435
436         for (int i = 0, size = constraintList.length; i < size; i++) {
437             Constraint c = constraintList[i];
438
439             if (c.getType() != Constraint.UNIQUE) {
440                 continue;
441             }
442
443             if (ArrayUtil.areEqual(c.getMainColumns(), col, col.length,
444                                    true)) {
445                 return c.getMainIndex();
446             }
447         }
448
449         return null;
450     }
451
452     /**
453      * Returns any foreign key constraint equivalent to the column sets
454      */

455     Constraint getConstraintForColumns(Table tablemain, int[] colmain,
456                                        int[] colref) {
457
458         for (int i = 0, size = constraintList.length; i < size; i++) {
459             Constraint c = constraintList[i];
460
461             if (c.isEquivalent(tablemain, colmain, this, colref)) {
462                 return c;
463             }
464         }
465
466         return null;
467     }
468
469     /**
470      * Returns any unique constraint equivalent to the column set
471      */

472     Constraint getUniqueConstraintForColumns(int[] cols) {
473
474         for (int i = 0, size = constraintList.length; i < size; i++) {
475             Constraint c = constraintList[i];
476
477             if (c.isEquivalent(cols, Constraint.UNIQUE)) {
478                 return c;
479             }
480         }
481
482         return null;
483     }
484
485     /**
486      * Returns any unique Constraint using this index
487      *
488      * @param index
489      * @return
490      */

491     Constraint getUniqueOrPKConstraintForIndex(Index index) {
492
493         for (int i = 0, size = constraintList.length; i < size; i++) {
494             Constraint c = constraintList[i];
495
496             if (c.getMainIndex() == index
497                     && (c.getType() == Constraint.UNIQUE
498                         || c.getType() == Constraint.PRIMARY_KEY)) {
499                 return c;
500             }
501         }
502
503         return null;
504     }
505
506     /**
507      * Returns the next constraint of a given type
508      *
509      * @param from
510      * @param type
511      * @return
512      */

513     int getNextConstraintIndex(int from, int type) {
514
515         for (int i = from, size = constraintList.length; i < size; i++) {
516             Constraint c = constraintList[i];
517
518             if (c.getType() == type) {
519                 return i;
520             }
521         }
522
523         return -1;
524     }
525
526 // fredt@users 20020220 - patch 475199 - duplicate column
527

528     /**
529      * Performs the table level checks and adds a column to the table at the
530      * DDL level. Only used at table creation, not at alter column.
531      */

532     void addColumn(Column column) throws HsqlException {
533
534         if (findColumn(column.columnName.name) >= 0) {
535             throw Trace.error(Trace.COLUMN_ALREADY_EXISTS);
536         }
537
538         if (column.isIdentity()) {
539             Trace.check(
540                 column.getType() == Types.INTEGER
541                 || column.getType() == Types.BIGINT, Trace.WRONG_DATA_TYPE,
542                     column.columnName.name);
543             Trace.check(identityColumn == -1, Trace.SECOND_PRIMARY_KEY,
544                         column.columnName.name);
545
546             identityColumn = columnCount;
547         }
548
549         if (primaryKeyCols != null) {
550             Trace.doAssert(false, "Table.addColumn");
551         }
552
553         columnList.add(column.columnName.name, column);
554
555         columnCount++;
556     }
557
558     /**
559      * Add a set of columns based on a ResultMetaData
560      */

561     void addColumns(Result.ResultMetaData metadata,
562                     int count) throws HsqlException {
563
564         for (int i = 0; i < count; i++) {
565             Column column = new Column(
566                 database.nameManager.newHsqlName(
567                     metadata.colLabels[i], metadata.isLabelQuoted[i]), true,
568                         metadata.colTypes[i], metadata.colSizes[i],
569                         metadata.colScales[i], false, null);
570
571             addColumn(column);
572         }
573     }
574
575     /**
576      * Adds a set of columns based on a compiled Select
577      */

578     void addColumns(Select select) throws HsqlException {
579
580         int colCount = select.iResultLen;
581
582         for (int i = 0; i < colCount; i++) {
583             Expression e = select.exprColumns[i];
584             Column column = new Column(
585                 database.nameManager.newHsqlName(
586                     e.getAlias(), e.isAliasQuoted()), true, e.getDataType(),
587                         e.getColumnSize(), e.getColumnScale(), false, null);
588
589             addColumn(column);
590         }
591     }
592
593     /**
594      * Returns the HsqlName object fo the table
595      */

596     public HsqlName getName() {
597         return tableName;
598     }
599
600     public int getId() {
601         return tableName.hashCode();
602     }
603
604     /**
605      * Changes table name. Used by 'alter table rename to'.
606      * Essential to use the existing HsqlName as this is is referenced by
607      * intances of Constraint etc.
608      */

609     void rename(Session session, String JavaDoc newname,
610                 boolean isquoted) throws HsqlException {
611
612         String JavaDoc oldname = tableName.name;
613
614         tableName.rename(newname, isquoted);
615
616         if (HsqlName.isReservedIndexName(getPrimaryIndex().getName().name)) {
617             getPrimaryIndex().getName().rename("SYS_PK", newname, isquoted);
618         }
619
620         renameTableInCheckConstraints(session, oldname, newname);
621     }
622
623     /**
624      * Returns total column counts, including hidden ones.
625      */

626     int getInternalColumnCount() {
627         return columnCount;
628     }
629
630     /**
631      * returns a basic duplicate of the table without the data structures.
632      */

633     protected Table duplicate() throws HsqlException {
634
635         Table t = (new Table(database, tableName, tableType));
636
637         return t;
638     }
639
640     /**
641      * Match two columns arrays for length and type of columns
642      *
643      * @param col column array from this Table
644      * @param other the other Table object
645      * @param othercol column array from the other Table
646      * @throws HsqlException if there is a mismatch
647      */

648     void checkColumnsMatch(int[] col, Table other,
649                            int[] othercol) throws HsqlException {
650
651         if (col.length != othercol.length) {
652             throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
653         }
654
655         for (int i = 0; i < col.length; i++) {
656
657             // integrity check - should not throw in normal operation
658
if (col[i] >= columnCount || othercol[i] >= other.columnCount) {
659                 throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
660             }
661
662             if (getColumn(col[i]).getType()
663                     != other.getColumn(othercol[i]).getType()) {
664                 throw Trace.error(Trace.COLUMN_TYPE_MISMATCH);
665             }
666         }
667     }
668
669 // fredt@users 20020405 - patch 1.7.0 by fredt - DROP and CREATE INDEX bug
670

671     /**
672      * Constraints that need removing are removed outside this method.<br>
673      * removeIndex is the index of an index to be removed, in which case
674      * no change is made to columns <br>
675      * When withoutindex is null, adjust {-1 | 0 | +1} indicates if a
676      * column is {removed | replaced | added}
677      *
678      */

679     Table moveDefinition(int[] removeIndex, Column newColumn, int colIndex,
680                          int adjust) throws HsqlException {
681
682         Table tn = duplicate();
683
684         // loop beyond the end in order to be able to add a column to the end
685
// of the list
686
for (int i = 0; i < columnCount + 1; i++) {
687             if (i == colIndex) {
688                 if (adjust == 0) {
689                     if (newColumn != null) {
690                         tn.addColumn(newColumn);
691
692                         continue;
693                     }
694                 } else if (adjust > 0) {
695                     tn.addColumn(newColumn);
696                 } else if (adjust < 0) {
697                     continue;
698                 }
699             }
700
701             if (i == columnCount) {
702                 break;
703             }
704
705             tn.addColumn(getColumn(i));
706         }
707
708         // treat it the same as new table creation and
709
int[] primarykey = primaryKeyCols.length == 0 ? null
710                                                       : primaryKeyCols;
711
712         if (primarykey != null) {
713             int[] newpk = ArrayUtil.toAdjustedColumnArray(primarykey,
714                 colIndex, adjust);
715
716             if (primarykey.length != newpk.length) {
717                 throw Trace.error(Trace.DROP_PRIMARY_KEY);
718             } else {
719                 primarykey = newpk;
720             }
721         }
722
723         tn.createPrimaryKey(getIndex(0).getName(), primarykey, false);
724
725         tn.constraintList = constraintList;
726
727         Index idx = null;
728
729         if (removeIndex != null) {
730             idx = getIndex(removeIndex, colIndex);
731         }
732
733         if (idx != null) {
734             if (idx.isConstraint()) {
735                 throw Trace.error(Trace.COLUMN_IS_IN_CONSTRAINT);
736             } else {
737                 throw Trace.error(Trace.COLUMN_IS_IN_INDEX);
738             }
739         }
740
741         for (int i = 1; i < indexList.length; i++) {
742             if (removeIndex != null && ArrayUtil.find(removeIndex, i) != -1) {
743                 continue;
744             }
745
746             tn.createAdjustedIndex(indexList[i], colIndex, adjust);
747         }
748
749         tn.triggerLists = triggerLists;
750
751         return tn;
752     }
753
754     Index getIndex(int[] exclude, int colIndex) {
755
756         for (int i = 1; i < indexList.length; i++) {
757             if (exclude != null && ArrayUtil.find(exclude, i) != -1) {
758                 continue;
759             }
760
761             Index idx = indexList[i];
762             int[] cols = idx.getColumns();
763
764             if (ArrayUtil.find(cols, colIndex) != -1) {
765                 return idx;
766             }
767         }
768
769         return null;
770     }
771
772     private void copyIndexes(Table tn, int removeIndex, int colIndex,
773                              int adjust) throws HsqlException {
774
775         for (int i = 1; i < getIndexCount(); i++) {
776             Index idx = indexList[i];
777
778             if (removeIndex == i) {
779                 continue;
780             }
781
782             Index newidx = tn.createAdjustedIndex(idx, colIndex, adjust);
783
784             if (newidx == null) {
785
786                 // column to remove is part of an index
787
throw Trace.error(Trace.COLUMN_IS_IN_INDEX);
788             }
789         }
790     }
791
792     /**
793      * cols == null means drop
794      */

795     Table moveDefinitionPK(int[] pkCols,
796                            boolean withIdentity) throws HsqlException {
797
798         // some checks
799
if ((hasPrimaryKey() && pkCols != null)
800                 || (!hasPrimaryKey() && pkCols == null)) {
801             throw Trace.error(Trace.DROP_PRIMARY_KEY);
802         }
803
804         Table tn = duplicate();
805
806         for (int i = 0; i < columnCount; i++) {
807             tn.addColumn(getColumn(i).duplicate(withIdentity));
808         }
809
810         tn.createPrimaryKey(getIndex(0).getName(), pkCols, true);
811
812         tn.constraintList = constraintList;
813
814         for (int i = 1; i < getIndexCount(); i++) {
815             Index idx = getIndex(i);
816
817             tn.createAdjustedIndex(idx, -1, 0);
818         }
819
820         tn.triggerLists = triggerLists;
821
822         return tn;
823     }
824
825     /**
826      * Updates the constraint and replaces references to the old table with
827      * the new one, adjusting column index arrays by the given amount.
828      */

829     void updateConstraintsTables(Session session, Table old, int colindex,
830                                  int adjust) throws HsqlException {
831
832         for (int i = 0, size = constraintList.length; i < size; i++) {
833             Constraint c = constraintList[i];
834
835             c.replaceTable(old, this, colindex, adjust);
836
837             if (c.constType == Constraint.CHECK) {
838                 recompileCheckConstraint(session, c);
839             }
840         }
841     }
842
843     private void recompileCheckConstraints(Session session)
844     throws HsqlException {
845
846         for (int i = 0, size = constraintList.length; i < size; i++) {
847             Constraint c = constraintList[i];
848
849             if (c.constType == Constraint.CHECK) {
850                 recompileCheckConstraint(session, c);
851             }
852         }
853     }
854
855     /**
856      * Used after adding columns or indexes to the table.
857      */

858     private void recompileCheckConstraint(Session session,
859                                           Constraint c) throws HsqlException {
860
861         String JavaDoc ddl = c.core.check.getDDL();
862         Tokenizer tokenizer = new Tokenizer(ddl);
863         Parser parser = new Parser(session, database, tokenizer);
864         Expression condition = parser.parseExpression();
865
866         c.core.check = condition;
867
868         // this workaround is here to stop LIKE optimisation (for proper scripting)
869
condition.setLikeOptimised();
870
871         Select s = Expression.getCheckSelect(session, this, condition);
872
873         c.core.checkFilter = s.tFilter[0];
874
875         c.core.checkFilter.setAsCheckFilter();
876
877         c.core.mainTable = this;
878     }
879
880     /**
881      * Used for drop column.
882      */

883     void checkColumnInCheckConstraint(String JavaDoc colname) throws HsqlException {
884
885         for (int i = 0, size = constraintList.length; i < size; i++) {
886             Constraint c = constraintList[i];
887
888             if (c.constType == Constraint.CHECK) {
889                 if (c.hasColumn(this, colname)) {
890                     throw Trace.error(Trace.COLUMN_IS_REFERENCED,
891                                       c.getName());
892                 }
893             }
894         }
895     }
896
897     /**
898      * Used for retype column. Checks whether column is in an FK or is
899      * referenced by a FK
900      * @param colIndex index
901      */

902     void checkColumnInFKConstraint(int colIndex) throws HsqlException {
903
904         for (int i = 0, size = constraintList.length; i < size; i++) {
905             Constraint c = constraintList[i];
906
907             if (c.hasColumn(colIndex)
908                     && (c.getType() == Constraint.MAIN
909                         || c.getType() == Constraint.FOREIGN_KEY)) {
910                 throw Trace.error(Trace.COLUMN_IS_REFERENCED,
911                                   c.getName().name);
912             }
913         }
914     }
915
916     /**
917      * Used for column defaults and nullability. Checks whether column is in an FK.
918      * @param colIndex index of column
919      * @param refOnly only check FK columns, not referenced columns
920      */

921     void checkColumnInFKConstraint(int colIndex,
922                                    int actionType) throws HsqlException {
923
924         for (int i = 0, size = constraintList.length; i < size; i++) {
925             Constraint c = constraintList[i];
926
927             if (c.hasColumn(colIndex)) {
928                 if (c.getType() == Constraint.FOREIGN_KEY
929                         && (actionType == c.getUpdateAction()
930                             || actionType == c.getDeleteAction())) {
931                     throw Trace.error(Trace.COLUMN_IS_REFERENCED,
932                                       c.getName().name);
933                 }
934             }
935         }
936     }
937
938     /**
939      * Used for rename column.
940      */

941     private void renameColumnInCheckConstraints(String JavaDoc oldname,
942             String JavaDoc newname, boolean isquoted) throws HsqlException {
943
944         for (int i = 0, size = constraintList.length; i < size; i++) {
945             Constraint c = constraintList[i];
946
947             if (c.constType == Constraint.CHECK) {
948                 Expression.Collector coll = new Expression.Collector();
949
950                 coll.addAll(c.core.check, Expression.COLUMN);
951
952                 Iterator it = coll.iterator();
953
954                 for (; it.hasNext(); ) {
955                     Expression e = (Expression) it.next();
956
957                     if (e.getColumnName() == oldname) {
958                         e.setColumnName(newname, isquoted);
959                     }
960                 }
961             }
962         }
963     }
964
965     /**
966      * Used for drop column.
967      */

968     private void renameTableInCheckConstraints(Session session,
969             String JavaDoc oldname, String JavaDoc newname) throws HsqlException {
970
971         for (int i = 0, size = constraintList.length; i < size; i++) {
972             Constraint c = constraintList[i];
973
974             if (c.constType == Constraint.CHECK) {
975                 Expression.Collector coll = new Expression.Collector();
976
977                 coll.addAll(c.core.check, Expression.COLUMN);
978
979                 Iterator it = coll.iterator();
980
981                 for (; it.hasNext(); ) {
982                     Expression e = (Expression) it.next();
983
984                     if (e.getTableName() == oldname) {
985                         e.setTableName(newname);
986                     }
987                 }
988             }
989         }
990
991         recompileCheckConstraints(session);
992     }
993
994     /**
995      * Returns the count of user defined columns.
996      */

997     public int getColumnCount() {
998         return columnCount;
999     }
1000
1001    /**
1002     * Returns the count of indexes on this table.
1003     */

1004    public int getIndexCount() {
1005        return indexList.length;
1006    }
1007
1008    /**
1009     * Returns the identity column or null.
1010     */

1011    int getIdentityColumn() {
1012        return identityColumn;
1013    }
1014
1015    /**
1016     * Returns the index of given column name or throws if not found
1017     */

1018    int getColumnNr(String JavaDoc c) throws HsqlException {
1019
1020        int i = findColumn(c);
1021
1022        if (i == -1) {
1023            throw Trace.error(Trace.COLUMN_NOT_FOUND, c);
1024        }
1025
1026        return i;
1027    }
1028
1029    /**
1030     * Returns the index of given column name or -1 if not found.
1031     */

1032    int findColumn(String JavaDoc c) {
1033
1034        int index = columnList.getIndex(c);
1035
1036        return index;
1037    }
1038
1039    /**
1040     * Returns the primary index (user defined or system defined)
1041     */

1042    public Index getPrimaryIndex() {
1043        return getIndex(0);
1044    }
1045
1046    /**
1047     * Return the user defined primary key column indexes, or empty array for system PK's.
1048     */

1049    public int[] getPrimaryKey() {
1050        return primaryKeyCols;
1051    }
1052
1053    public int[] getPrimaryKeyTypes() {
1054        return primaryKeyTypes;
1055    }
1056
1057    public boolean hasPrimaryKey() {
1058        return !(primaryKeyCols.length == 0);
1059    }
1060
1061    int[] getBestRowIdentifiers() {
1062        return bestRowIdentifierCols;
1063    }
1064
1065    boolean isBestRowIdentifiersStrict() {
1066        return bestRowIdentifierStrict;
1067    }
1068
1069    /**
1070     * This method is called whenever there is a change to table structure and
1071     * serves two porposes: (a) to reset the best set of columns that identify
1072     * the rows of the table (b) to reset the best index that can be used
1073     * to find rows of the table given a column value.
1074     *
1075     * (a) gives most weight to a primary key index, followed by a unique
1076     * address with the lowest count of nullable columns. Otherwise there is
1077     * no best row identifier.
1078     *
1079     * (b) finds for each column an index with a corresponding first column.
1080     * It uses any type of visible index and accepts the first one (it doesn't
1081     * make any difference to performance).
1082     *
1083     * bestIndex is the user defined, primary key, the first unique index, or
1084     * the first non-unique index. NULL if there is no user-defined index.
1085     *
1086     */

1087    void setBestRowIdentifiers() {
1088
1089        int[] briCols = null;
1090        int briColsCount = 0;
1091        boolean isStrict = false;
1092        int nNullCount = 0;
1093
1094        // ignore if called prior to completion of primary key construction
1095
if (colNullable == null) {
1096            return;
1097        }
1098
1099        bestIndex = null;
1100        bestIndexForColumn = new int[columnList.size()];
1101
1102        ArrayUtil.fillArray(bestIndexForColumn, -1);
1103
1104        for (int i = 0; i < indexList.length; i++) {
1105            Index index = indexList[i];
1106            int[] cols = index.getColumns();
1107            int colsCount = index.getVisibleColumns();
1108
1109            if (i == 0) {
1110
1111                // ignore system primary keys
1112
if (hasPrimaryKey()) {
1113                    isStrict = true;
1114                } else {
1115                    continue;
1116                }
1117            }
1118
1119            if (bestIndexForColumn[cols[0]] == -1) {
1120                bestIndexForColumn[cols[0]] = i;
1121            }
1122
1123            if (!index.isUnique()) {
1124                if (bestIndex == null) {
1125                    bestIndex = index;
1126                }
1127
1128                continue;
1129            }
1130
1131            int nnullc = 0;
1132
1133            for (int j = 0; j < colsCount; j++) {
1134                if (!colNullable[cols[j]]) {
1135                    nnullc++;
1136                }
1137            }
1138
1139            if (bestIndex != null) {
1140                bestIndex = index;
1141            }
1142
1143            if (nnullc == colsCount) {
1144                if (briCols == null || briColsCount != nNullCount
1145                        || colsCount < briColsCount) {
1146
1147                    // nothing found before ||
1148
// found but has null columns ||
1149
// found but has more columns than this index
1150
briCols = cols;
1151                    briColsCount = colsCount;
1152                    nNullCount = colsCount;
1153                    isStrict = true;
1154                }
1155
1156                continue;
1157            } else if (isStrict) {
1158                continue;
1159            } else if (briCols == null || colsCount < briColsCount
1160                       || nnullc > nNullCount) {
1161
1162                // nothing found before ||
1163
// found but has more columns than this index||
1164
// found but has fewer not null columns than this index
1165
briCols = cols;
1166                briColsCount = colsCount;
1167                nNullCount = nnullc;
1168            }
1169        }
1170
1171        // remove rowID column from bestRowIdentiferCols
1172
bestRowIdentifierCols = briCols == null
1173                                || briColsCount == briCols.length ? briCols
1174                                                                  : ArrayUtil
1175                                                                  .arraySlice(briCols,
1176                                                                      0, briColsCount);
1177        bestRowIdentifierStrict = isStrict;
1178
1179        if (hasPrimaryKey()) {
1180            bestIndex = getPrimaryIndex();
1181        }
1182    }
1183
1184    /**
1185     * Sets the SQL default value for a columm.
1186     */

1187    void setDefaultExpression(int columnIndex, Expression def) {
1188
1189        Column column = getColumn(columnIndex);
1190
1191        column.setDefaultExpression(def);
1192
1193        colDefaults[columnIndex] = column.getDefaultExpression();
1194
1195        resetDefaultsFlag();
1196    }
1197
1198    /**
1199     * sets the flag for the presence of any default expression
1200     */

1201    void resetDefaultsFlag() {
1202
1203        hasDefaultValues = false;
1204
1205        for (int i = 0; i < columnCount; i++) {
1206            hasDefaultValues = hasDefaultValues || colDefaults[i] != null;
1207        }
1208    }
1209
1210    DataFileCache getCache() {
1211        return cache;
1212    }
1213
1214    /**
1215     * Used in TableFilter to get an index for the column.
1216     * An index is created automatically for system tables or subqueries.
1217     */

1218    Index getIndexForColumn(Session session, int column) {
1219
1220        int i = bestIndexForColumn[column];
1221
1222        if (i == -1
1223                && (tableType == Table.SYSTEM_SUBQUERY
1224                    || tableType == Table.SYSTEM_TABLE)) {
1225            try {
1226                HsqlName indexName = database.nameManager.newAutoName("IDX");
1227
1228                createIndex(session, new int[]{ column }, indexName, false,
1229                            false, false);
1230
1231                i = bestIndexForColumn[column];
1232            } catch (Exception JavaDoc e) {}
1233        }
1234
1235        return i == -1 ? null
1236                       : getIndex(i);
1237    }
1238
1239    /**
1240     * Used for TableFilter to get an index for the columns
1241     */

1242    Index getIndexForColumns(boolean[] columnCheck) {
1243
1244        Index indexChoice = null;
1245        int colCount = 0;
1246
1247        for (int i = 0; i < indexList.length; i++) {
1248            Index index = indexList[i];
1249            boolean result = ArrayUtil.containsAllTrueElements(columnCheck,
1250                index.colCheck);
1251
1252            if (result && index.getVisibleColumns() > colCount) {
1253                colCount = index.getVisibleColumns();
1254                indexChoice = index;
1255            }
1256        }
1257
1258        return indexChoice;
1259    }
1260
1261    /**
1262     * Finds an existing index for a foreign key column group
1263     */

1264    Index getIndexForColumns(int[] col, boolean unique) throws HsqlException {
1265
1266        for (int i = 0, count = getIndexCount(); i < count; i++) {
1267            Index currentindex = getIndex(i);
1268            int[] indexcol = currentindex.getColumns();
1269
1270            if (ArrayUtil.haveEqualArrays(indexcol, col, col.length)) {
1271                if (!unique || currentindex.isUnique()) {
1272                    return currentindex;
1273                }
1274            }
1275        }
1276
1277        return null;
1278    }
1279
1280    /**
1281     * Return the list of file pointers to root nodes for this table's
1282     * indexes.
1283     */

1284    public int[] getIndexRootsArray() {
1285
1286        int[] roots = new int[getIndexCount()];
1287
1288        for (int i = 0; i < getIndexCount(); i++) {
1289            roots[i] = indexList[i].getRoot();
1290        }
1291
1292        return roots;
1293    }
1294
1295    /**
1296     * Returns the string consisting of file pointers to roots of indexes
1297     * plus the next identity value (hidden or user defined). This is used
1298     * with CACHED tables.
1299     */

1300    String JavaDoc getIndexRoots() {
1301
1302        String JavaDoc roots = StringUtil.getList(getIndexRootsArray(), " ", "");
1303        StringBuffer JavaDoc s = new StringBuffer JavaDoc(roots);
1304
1305        s.append(' ');
1306        s.append(identitySequence.peek());
1307
1308        return s.toString();
1309    }
1310
1311    /**
1312     * Sets the index roots of a cached/text table to specified file
1313     * pointers. If a
1314     * file pointer is -1 then the particular index root is null. A null index
1315     * root signifies an empty table. Accordingly, all index roots should be
1316     * null or all should be a valid file pointer/reference.
1317     */

1318    public void setIndexRoots(int[] roots) throws HsqlException {
1319
1320        Trace.check(isCached, Trace.TABLE_NOT_FOUND);
1321
1322        for (int i = 0; i < getIndexCount(); i++) {
1323            int p = roots[i];
1324            Row r = null;
1325
1326            if (p != -1) {
1327                r = (CachedRow) rowStore.get(p);
1328            }
1329
1330            Node f = null;
1331
1332            if (r != null) {
1333                f = r.getNode(i);
1334            }
1335
1336            indexList[i].setRoot(null, f);
1337        }
1338    }
1339
1340    /**
1341     * Sets the index roots and next identity.
1342     */

1343    void setIndexRoots(String JavaDoc s) throws HsqlException {
1344
1345        // the user may try to set this; this is not only internal problem
1346
Trace.check(isCached, Trace.TABLE_NOT_FOUND);
1347
1348        Tokenizer t = new Tokenizer(s);
1349        int[] roots = new int[getIndexCount()];
1350
1351        for (int i = 0; i < getIndexCount(); i++) {
1352            int v = t.getInt();
1353
1354            roots[i] = v;
1355        }
1356
1357        setIndexRoots(roots);
1358
1359        long v = t.getBigint();
1360
1361        identitySequence.reset(v);
1362    }
1363
1364    /**
1365     * Shortcut for creating system table PK's.
1366     */

1367    void createPrimaryKey(int[] cols) throws HsqlException {
1368        createPrimaryKey(null, cols, false);
1369    }
1370
1371    /**
1372     * Shortcut for creating default PK's.
1373     */

1374    void createPrimaryKey() throws HsqlException {
1375        createPrimaryKey(null, null, false);
1376    }
1377
1378    /**
1379     * Creates a single or multi-column primary key and index. sets the
1380     * colTypes array. Finalises the creation of the table. (fredt@users)
1381     */

1382
1383// tony_lai@users 20020820 - patch 595099
1384
void createPrimaryKey(HsqlName indexName, int[] columns,
1385                          boolean columnsNotNull) throws HsqlException {
1386
1387        if (primaryKeyCols != null) {
1388            Trace.doAssert(false, "Table.createPrimaryKey(column)");
1389        }
1390
1391        if (columns == null) {
1392            columns = new int[0];
1393        } else {
1394            for (int i = 0; i < columns.length; i++) {
1395                if (columnsNotNull) {
1396                    getColumn(columns[i]).setNullable(false);
1397                }
1398
1399                getColumn(columns[i]).setPrimaryKey(true);
1400            }
1401        }
1402
1403        primaryKeyCols = columns;
1404        colTypes = new int[columnCount];
1405        colDefaults = new Expression[columnCount];
1406        colSizes = new int[columnCount];
1407        colScales = new int[columnCount];
1408        colNullable = new boolean[columnCount];
1409        defaultColumnMap = new int[columnCount];
1410
1411        for (int i = 0; i < columnCount; i++) {
1412            setColumnTypeVars(i);
1413        }
1414
1415        primaryKeyTypes = new int[primaryKeyCols.length];
1416
1417        ArrayUtil.copyColumnValues(colTypes, primaryKeyCols, primaryKeyTypes);
1418
1419        primaryKeyColsSequence = new int[primaryKeyCols.length];
1420
1421        ArrayUtil.fillSequence(primaryKeyColsSequence);
1422        resetDefaultsFlag();
1423
1424        // tony_lai@users 20020820 - patch 595099
1425
HsqlName name = indexName != null ? indexName
1426                                          : database.nameManager.newAutoName(
1427                                              "IDX");
1428
1429        createPrimaryIndex(columns, name);
1430        setBestRowIdentifiers();
1431    }
1432
1433    void setColumnTypeVars(int i) {
1434
1435        Column column = getColumn(i);
1436
1437        colTypes[i] = column.getType();
1438        colSizes[i] = column.getSize();
1439        colScales[i] = column.getScale();
1440        colNullable[i] = column.isNullable();
1441        defaultColumnMap[i] = i;
1442
1443        if (column.isIdentity()) {
1444            identitySequence.reset(column.identityStart,
1445                                   column.identityIncrement);
1446        }
1447
1448        colDefaults[i] = column.getDefaultExpression();
1449    }
1450
1451    HsqlName makeSysPKName() throws HsqlException {
1452        return database.nameManager.newAutoName("PK");
1453    }
1454
1455    void createPrimaryIndex(int[] pkcols,
1456                            HsqlName name) throws HsqlException {
1457
1458        int[] pkcoltypes = new int[pkcols.length];
1459
1460        for (int j = 0; j < pkcols.length; j++) {
1461            pkcoltypes[j] = colTypes[pkcols[j]];
1462        }
1463
1464        Index newindex = new Index(database, name, this, pkcols, pkcoltypes,
1465                                   true, true, true, false, pkcols,
1466                                   pkcoltypes, isTemp);
1467
1468        addIndex(newindex);
1469    }
1470
1471    /**
1472     * Create new index taking into account removal or addition of a column
1473     * to the table.
1474     */

1475    private Index createAdjustedIndex(Index index, int colindex,
1476                                      int adjust) throws HsqlException {
1477
1478        int[] indexcolumns = (int[]) ArrayUtil.resizeArray(index.getColumns(),
1479            index.getVisibleColumns());
1480        int[] colarr = ArrayUtil.toAdjustedColumnArray(indexcolumns,
1481            colindex, adjust);
1482
1483        // if a column to remove is one of the Index columns
1484
if (colarr.length != index.getVisibleColumns()) {
1485            return null;
1486        }
1487
1488        return createIndexStructure(colarr, index.getName(),
1489                                    index.isUnique(), index.isConstraint,
1490                                    index.isForward);
1491    }
1492
1493    /**
1494     * Create new memory-resident index. For MEMORY and TEXT tables.
1495     */

1496    Index createIndex(Session session, int[] column, HsqlName name,
1497                      boolean unique, boolean constraint,
1498                      boolean forward) throws HsqlException {
1499
1500        int newindexNo = createIndexStructureGetNo(column, name, unique,
1501            constraint, forward);
1502        Index newindex = indexList[newindexNo];
1503        Index primaryindex = getPrimaryIndex();
1504        RowIterator it = primaryindex.firstRow(session);
1505        int rowCount = 0;
1506        HsqlException error = null;
1507
1508        try {
1509            while (it.hasNext()) {
1510                Row row = it.next();
1511                Node backnode = row.getNode(newindexNo - 1);
1512                Node newnode = Node.newNode(row, newindexNo, this);
1513
1514                newnode.nNext = backnode.nNext;
1515                backnode.nNext = newnode;
1516
1517                // count before inserting
1518
rowCount++;
1519
1520                newindex.insert(session, row, newindexNo);
1521            }
1522
1523            return newindex;
1524        } catch (java.lang.OutOfMemoryError JavaDoc e) {
1525            error = Trace.error(Trace.OUT_OF_MEMORY);
1526        } catch (HsqlException e) {
1527            error = e;
1528        }
1529
1530        // backtrack on error
1531
// rowCount rows have been modified
1532
it = primaryindex.firstRow(session);
1533
1534        for (int i = 0; i < rowCount; i++) {
1535            Row row = it.next();
1536            Node backnode = row.getNode(0);
1537            int j = newindexNo;
1538
1539            while (--j > 0) {
1540                backnode = backnode.nNext;
1541            }
1542
1543            backnode.nNext = backnode.nNext.nNext;
1544        }
1545
1546        indexList = (Index[]) ArrayUtil.toAdjustedArray(indexList, null,
1547                newindexNo, -1);
1548
1549        setBestRowIdentifiers();
1550
1551        throw error;
1552    }
1553
1554    /**
1555     * Creates the internal structures for an index.
1556     */

1557    Index createIndexStructure(int[] columns, HsqlName name, boolean unique,
1558                               boolean constraint,
1559                               boolean forward) throws HsqlException {
1560
1561        int i = createIndexStructureGetNo(columns, name, unique, constraint,
1562                                          forward);
1563
1564        return indexList[i];
1565    }
1566
1567    int createIndexStructureGetNo(int[] column, HsqlName name,
1568                                  boolean unique, boolean constraint,
1569                                  boolean forward) throws HsqlException {
1570
1571        if (primaryKeyCols == null) {
1572            Trace.doAssert(false, "createIndex");
1573        }
1574
1575        int s = column.length;
1576        int[] col = new int[s];
1577        int[] type = new int[s];
1578
1579        for (int j = 0; j < s; j++) {
1580            col[j] = column[j];
1581            type[j] = colTypes[col[j]];
1582        }
1583
1584        int[] pkcols = getPrimaryKey();
1585        int[] pktypes = getPrimaryKeyTypes();
1586        Index newindex = new Index(database, name, this, col, type, false,
1587                                   unique, constraint, forward, pkcols,
1588                                   pktypes, isTemp);
1589        int indexNo = addIndex(newindex);
1590
1591        setBestRowIdentifiers();
1592
1593        return indexNo;
1594    }
1595
1596    private int addIndex(Index index) {
1597
1598        int i = 0;
1599
1600        for (; i < indexList.length; i++) {
1601            Index current = indexList[i];
1602            int order = index.getIndexOrderValue()
1603                        - current.getIndexOrderValue();
1604
1605            if (order < 0) {
1606                break;
1607            }
1608        }
1609
1610        indexList = (Index[]) ArrayUtil.toAdjustedArray(indexList, index, i,
1611                1);
1612
1613        return i;
1614    }
1615
1616    /**
1617     * returns false if the table has to be recreated in order to add / drop
1618     * indexes. Only CACHED tables return false.
1619     */

1620    boolean isIndexingMutable() {
1621        return !isIndexCached();
1622    }
1623
1624    /**
1625     * Checks for use of a named index in table constraints,
1626     * while ignorring a given set of constraints.
1627     * @throws HsqlException if index is used in a constraint
1628     */

1629    void checkDropIndex(String JavaDoc indexname, HashSet ignore,
1630                        boolean dropPK) throws HsqlException {
1631
1632        Index index = this.getIndex(indexname);
1633
1634        if (index == null) {
1635            throw Trace.error(Trace.INDEX_NOT_FOUND, indexname);
1636        }
1637
1638        if (!dropPK && index.equals(getIndex(0))) {
1639            throw Trace.error(Trace.DROP_PRIMARY_KEY, indexname);
1640        }
1641
1642        for (int i = 0, size = constraintList.length; i < size; i++) {
1643            Constraint c = constraintList[i];
1644
1645            if (ignore != null && ignore.contains(c)) {
1646                continue;
1647            }
1648
1649            if (c.isIndexFK(index)) {
1650                throw Trace.error(Trace.DROP_FK_INDEX, indexname);
1651            }
1652
1653            if (c.isIndexUnique(index)) {
1654                throw Trace.error(Trace.SYSTEM_INDEX, indexname);
1655            }
1656        }
1657
1658        return;
1659    }
1660
1661    /**
1662     * Returns true if the table has any rows at all.
1663     */

1664    public boolean isEmpty(Session session) {
1665
1666        if (getIndexCount() == 0) {
1667            return true;
1668        }
1669
1670        return getIndex(0).isEmpty(session);
1671    }
1672
1673    /**
1674     * Returns direct mapping array.
1675     */

1676    int[] getColumnMap() {
1677        return defaultColumnMap;
1678    }
1679
1680    /**
1681     * Returns empty mapping array.
1682     */

1683    int[] getNewColumnMap() {
1684        return new int[columnCount];
1685    }
1686
1687    /**
1688     * Returns empty boolean array.
1689     */

1690    boolean[] getNewColumnCheckList() {
1691        return new boolean[columnCount];
1692    }
1693
1694    /**
1695     * Returns empty Object array for a new row.
1696     */

1697    public Object JavaDoc[] getEmptyRowData() {
1698        return new Object JavaDoc[columnCount];
1699    }
1700
1701    /**
1702     * Returns array for a new row with SQL DEFAULT value for each column n
1703     * where exists[n] is false. This provides default values only where
1704     * required and avoids evaluating these values where they will be
1705     * overwritten.
1706     */

1707    Object JavaDoc[] getNewRowData(Session session,
1708                           boolean[] exists) throws HsqlException {
1709
1710        Object JavaDoc[] data = new Object JavaDoc[columnCount];
1711        int i;
1712
1713        if (exists != null && hasDefaultValues) {
1714            for (i = 0; i < columnCount; i++) {
1715                Expression def = colDefaults[i];
1716
1717                if (exists[i] == false && def != null) {
1718                    data[i] = def.getValue(session, colTypes[i]);
1719                }
1720            }
1721        }
1722
1723        return data;
1724    }
1725
1726    /**
1727     * Performs Table structure modification and changes to the index nodes
1728     * to remove a given index from a MEMORY or TEXT table. Not for PK index.
1729     *
1730     */

1731    void dropIndex(Session session, String JavaDoc indexname) throws HsqlException {
1732
1733        // find the array index for indexname and remove
1734
int todrop = getIndexIndex(indexname);
1735
1736        indexList = (Index[]) ArrayUtil.toAdjustedArray(indexList, null,
1737                todrop, -1);
1738
1739        setBestRowIdentifiers();
1740        dropIndexFromRows(session, todrop);
1741    }
1742
1743    void dropIndexFromRows(Session session, int index) throws HsqlException {
1744
1745        RowIterator it = getPrimaryIndex().firstRow(session);
1746
1747        while (it.hasNext()) {
1748            Row row = it.next();
1749            int i = index - 1;
1750            Node backnode = row.getNode(0);
1751
1752            while (i-- > 0) {
1753                backnode = backnode.nNext;
1754            }
1755
1756            backnode.nNext = backnode.nNext.nNext;
1757        }
1758    }
1759
1760    /**
1761     * Moves the data from table to table.
1762     * The colindex argument is the index of the column that was
1763     * added or removed. The adjust argument is {-1 | 0 | +1}
1764     */

1765    void moveData(Session session, Table from, int colindex,
1766                  int adjust) throws HsqlException {
1767
1768        Object JavaDoc colvalue = null;
1769        Column column = null;
1770
1771        if (adjust >= 0 && colindex != -1) {
1772            column = getColumn(colindex);
1773            colvalue = column.getDefaultValue(session);
1774        }
1775
1776        RowIterator it = from.getPrimaryIndex().firstRow(session);
1777
1778        while (it.hasNext()) {
1779            Row row = it.next();
1780            Object JavaDoc[] o = row.getData();
1781            Object JavaDoc[] data = getEmptyRowData();
1782
1783            if (adjust == 0 && colindex != -1) {
1784                colvalue = Column.convertObject(session, o[colindex],
1785                                                column.getType(),
1786                                                column.getSize(),
1787                                                column.getScale());
1788            }
1789
1790            ArrayUtil.copyAdjustArray(o, data, colvalue, colindex, adjust);
1791            setIdentityColumn(session, data);
1792            enforceNullConstraints(data);
1793
1794            Row newrow = newRow(data);
1795
1796            indexRow(session, newrow);
1797        }
1798
1799        from.drop();
1800    }
1801
1802    /**
1803     * Highest level multiple row insert method. Corresponds to an SQL
1804     * INSERT INTO ... SELECT ... statement.
1805     */

1806    int insert(Session session, Result ins) throws HsqlException {
1807
1808        Record ni = ins.rRoot;
1809        int count = 0;
1810
1811        fireAll(session, Trigger.INSERT_BEFORE);
1812
1813        while (ni != null) {
1814            insertRow(session, ni.data);
1815
1816            ni = ni.next;
1817
1818            count++;
1819        }
1820
1821        fireAll(session, Trigger.INSERT_AFTER);
1822
1823        return count;
1824    }
1825
1826    /**
1827     * Highest level method for inserting a single row. Corresponds to an
1828     * SQL INSERT INTO .... VALUES(,,) statement.
1829     * fires triggers.
1830     */

1831    void insert(Session session, Object JavaDoc[] data) throws HsqlException {
1832
1833        fireAll(session, Trigger.INSERT_BEFORE);
1834        insertRow(session, data);
1835        fireAll(session, Trigger.INSERT_AFTER);
1836    }
1837
1838    /**
1839     * Mid level method for inserting rows. Performs constraint checks and
1840     * fires row level triggers.
1841     */

1842    private void insertRow(Session session,
1843                           Object JavaDoc[] data) throws HsqlException {
1844
1845        if (triggerLists[Trigger.INSERT_BEFORE_ROW] != null) {
1846            fireAll(session, Trigger.INSERT_BEFORE_ROW, null, data);
1847        }
1848
1849        setIdentityColumn(session, data);
1850        checkRowDataInsert(session, data);
1851        insertNoCheck(session, data);
1852
1853        if (triggerLists[Trigger.INSERT_AFTER_ROW] != null) {
1854            fireAll(session, Trigger.INSERT_AFTER_ROW, null, data);
1855            checkRowDataInsert(session, data);
1856        }
1857    }
1858
1859    /**
1860     * Multi-row insert method. Used for SELECT ... INTO tablename queries.
1861     * These tables are new, empty tables, with no constraints, triggers
1862     * column default values, column size enforcement whatsoever.
1863     *
1864     * Not used for INSERT INTO .... SELECT ... FROM queries
1865     */

1866    void insertIntoTable(Session session,
1867                         Result result) throws HsqlException {
1868
1869        insertResult(session, result);
1870
1871        if (!isLogged) {
1872            return;
1873        }
1874
1875        Record r = result.rRoot;
1876
1877        while (r != null) {
1878            database.logger.writeInsertStatement(session, this, r.data);
1879
1880            r = r.next;
1881        }
1882    }
1883
1884    /**
1885     * Low level method for row insert.
1886     * UNIQUE or PRIMARY constraints are enforced by attempting to
1887     * add the row to the indexes.
1888     */

1889    private void insertNoCheck(Session session,
1890                               Object JavaDoc[] data) throws HsqlException {
1891
1892        Row row = newRow(data);
1893
1894        // this handles the UNIQUE constraints
1895
indexRow(session, row);
1896
1897        if (session != null) {
1898            session.addInsertAction(this, row);
1899        }
1900
1901        if (isLogged) {
1902            database.logger.writeInsertStatement(session, this, data);
1903        }
1904    }
1905
1906    /**
1907     *
1908     */

1909    public void insertNoCheckFromLog(Session session,
1910                                     Object JavaDoc[] data) throws HsqlException {
1911
1912        Row r = newRow(data);
1913
1914        updateIdentityValue(data);
1915        indexRow(session, r);
1916
1917        if (session != null) {
1918            session.addInsertAction(this, r);
1919        }
1920    }
1921
1922    /**
1923     * Low level method for restoring deleted rows
1924     */

1925    void insertNoCheckRollback(Session session, Row row,
1926                               boolean log) throws HsqlException {
1927
1928        Row newrow = restoreRow(row);
1929
1930        // instead of new row, use new routine so that the row does not use
1931
// rowstore.add(), which will allocate new space and different pos
1932
indexRow(session, newrow);
1933
1934        if (log && isLogged) {
1935            database.logger.writeInsertStatement(session, this,
1936                                                 row.getData());
1937        }
1938    }
1939
1940    /**
1941     * Used for system table inserts. No checks. No identity
1942     * columns.
1943     */

1944    int insertSys(Result ins) throws HsqlException {
1945
1946        Record ni = ins.rRoot;
1947        int count = 0;
1948
1949        while (ni != null) {
1950            insertData(null, ni.data);
1951
1952            ni = ni.next;
1953
1954            count++;
1955        }
1956
1957        return count;
1958    }
1959
1960    /**
1961     * Used for subquery inserts. No checks. No identity
1962     * columns.
1963     */

1964    int insertResult(Session session, Result ins) throws HsqlException {
1965
1966        Record ni = ins.rRoot;
1967        int count = 0;
1968
1969        while (ni != null) {
1970            Object JavaDoc[] newData =
1971                (Object JavaDoc[]) ArrayUtil.resizeArrayIfDifferent(ni.data,
1972                    columnCount);
1973
1974            insertData(session, newData);
1975
1976            ni = ni.next;
1977
1978            count++;
1979        }
1980
1981        return count;
1982    }
1983
1984    /**
1985     * Not for general use.
1986     * Used by ScriptReader to unconditionally insert a row into
1987     * the table when the .script file is read.
1988     */

1989    public void insertFromScript(Object JavaDoc[] data) throws HsqlException {
1990        updateIdentityValue(data);
1991        insertData(null, data);
1992    }
1993
1994    /**
1995     * Used by the methods above.
1996     */

1997    public void insertData(Session session,
1998                           Object JavaDoc[] data) throws HsqlException {
1999
2000        Row row = newRow(data);
2001
2002        indexRow(session, row);
2003        commitRowToStore(row);
2004    }
2005
2006    /**
2007     * Used by the system tables
2008     */

2009    public void insertSys(Object JavaDoc[] data) throws HsqlException {
2010
2011        Row row = newRow(data);
2012
2013        indexRow(null, row);
2014    }
2015
2016    /**
2017     * Used by TextCache to insert a row into the indexes when the source
2018     * file is first read.
2019     */

2020    protected void insertFromTextSource(CachedRow row) throws HsqlException {
2021
2022        Object JavaDoc[] data = row.getData();
2023
2024        updateIdentityValue(data);
2025        enforceFieldValueLimits(data, defaultColumnMap);
2026        enforceNullConstraints(data);
2027
2028        int i = 0;
2029
2030        try {
2031            for (; i < indexList.length; i++) {
2032                indexList[i].insert(null, row, i);
2033            }
2034        } catch (HsqlException e) {
2035
2036            // unique index violation - rollback insert
2037
for (--i; i >= 0; i--) {
2038                Node n = row.getNode(i);
2039
2040                indexList[i].delete(null, n);
2041            }
2042
2043            row.delete();
2044            removeRowFromStore(row);
2045
2046            throw e;
2047        }
2048    }
2049
2050    /**
2051     * Checks a row against NOT NULL constraints on columns.
2052     */

2053    protected void enforceNullConstraints(Object JavaDoc[] data)
2054    throws HsqlException {
2055
2056        for (int i = 0; i < columnCount; i++) {
2057            if (data[i] == null &&!colNullable[i]) {
2058                Trace.throwerror(Trace.TRY_TO_INSERT_NULL,
2059                                 "column: " + getColumn(i).columnName.name
2060                                 + " table: " + tableName.name);
2061            }
2062        }
2063    }
2064
2065    /**
2066     * If there is an identity column (visible or hidden) on the table, sets
2067     * the value and/or adjusts the iIdentiy value for the table.
2068     */

2069    protected void setIdentityColumn(Session session,
2070                                     Object JavaDoc[] data) throws HsqlException {
2071
2072        if (identityColumn != -1) {
2073            Number JavaDoc id = (Number JavaDoc) data[identityColumn];
2074
2075            if (id == null) {
2076                if (colTypes[identityColumn] == Types.INTEGER) {
2077                    id = ValuePool.getInt((int) identitySequence.getValue());
2078                } else {
2079                    id = ValuePool.getLong(identitySequence.getValue());
2080                }
2081
2082                data[identityColumn] = id;
2083            } else {
2084                identitySequence.getValue(id.longValue());
2085            }
2086
2087            if (session != null) {
2088                session.setLastIdentity(id);
2089            }
2090        }
2091    }
2092
2093    /**
2094     * If there is an identity column (visible or hidden) on the table, sets
2095     * the max identity value.
2096     */

2097    protected void updateIdentityValue(Object JavaDoc[] data) throws HsqlException {
2098
2099        if (identityColumn != -1) {
2100            Number JavaDoc id = (Number JavaDoc) data[identityColumn];
2101
2102            if (id != null) {
2103                identitySequence.getValue(id.longValue());
2104            }
2105        }
2106    }
2107
2108    /**
2109     * Enforce max field sizes according to SQL column definition.
2110     * SQL92 13.8
2111     */

2112    void enforceFieldValueLimits(Object JavaDoc[] data,
2113                                 int[] cols) throws HsqlException {
2114
2115        int i;
2116        int colindex;
2117
2118        if (sqlEnforceSize) {
2119            if (cols == null) {
2120                cols = defaultColumnMap;
2121            }
2122
2123            for (i = 0; i < cols.length; i++) {
2124                colindex = cols[i];
2125
2126                if ((colTypes[colindex] == Types.TIMESTAMP || colSizes[colindex] != 0)
2127                        && data[colindex] != null) {
2128                    data[colindex] = Column.enforceSize(data[colindex],
2129                                                        colTypes[colindex],
2130                                                        colSizes[colindex],
2131                                                        colScales[colindex],
2132                                                        true);
2133                }
2134            }
2135        }
2136    }
2137
2138// fredt@users 20020130 - patch 491987 by jimbag@users - modified
2139

2140    /**
2141     * Fires all row-level triggers of the given set (trigger type)
2142     *
2143     */

2144    void fireAll(Session session, int trigVecIndx, Object JavaDoc[] oldrow,
2145                 Object JavaDoc[] newrow) {
2146
2147        if (!database.isReferentialIntegrity()) {
2148
2149            // isReferentialIntegrity is false when reloading db
2150
return;
2151        }
2152
2153        HsqlArrayList trigVec = triggerLists[trigVecIndx];
2154
2155        if (trigVec == null) {
2156            return;
2157        }
2158
2159        for (int i = 0, size = trigVec.size(); i < size; i++) {
2160            TriggerDef td = (TriggerDef) trigVec.get(i);
2161
2162            td.pushPair(session, oldrow, newrow); // tell the trigger thread to fire with this row
2163
}
2164    }
2165
2166    /**
2167     * Statement level triggers.
2168     */

2169    void fireAll(Session session, int trigVecIndex) {
2170
2171        if (triggerLists[trigVecIndex] != null) {
2172            fireAll(session, trigVecIndex, null, null);
2173        }
2174    }
2175
2176    /**
2177     * Adds a trigger.
2178     */

2179    void addTrigger(TriggerDef trigDef) {
2180
2181        if (triggerLists[trigDef.vectorIndex] == null) {
2182            triggerLists[trigDef.vectorIndex] = new HsqlArrayList();
2183        }
2184
2185        triggerLists[trigDef.vectorIndex].add(trigDef);
2186    }
2187
2188    /**
2189     * Drops a trigger.
2190     */

2191    void dropTrigger(String JavaDoc name) {
2192
2193        // look in each trigger list of each type of trigger
2194
int numTrigs = TriggerDef.NUM_TRIGS;
2195
2196        for (int tv = 0; tv < numTrigs; tv++) {
2197            HsqlArrayList v = triggerLists[tv];
2198
2199            if (v == null) {
2200                continue;
2201            }
2202
2203            for (int tr = v.size() - 1; tr >= 0; tr--) {
2204                TriggerDef td = (TriggerDef) v.get(tr);
2205
2206                if (td.name.name.equals(name)) {
2207                    v.remove(tr);
2208                    td.terminate();
2209                }
2210            }
2211
2212            if (v.isEmpty()) {
2213                triggerLists[tv] = null;
2214            }
2215        }
2216    }
2217
2218    /**
2219     * Drops all triggers.
2220     */

2221    void dropTriggers() {
2222
2223        // look in each trigger list of each type of trigger
2224
int numTrigs = TriggerDef.NUM_TRIGS;
2225
2226        for (int tv = 0; tv < numTrigs; tv++) {
2227            HsqlArrayList v = triggerLists[tv];
2228
2229            if (v == null) {
2230                continue;
2231            }
2232
2233            for (int tr = v.size() - 1; tr >= 0; tr--) {
2234                TriggerDef td = (TriggerDef) v.get(tr);
2235
2236                td.terminate();
2237            }
2238
2239            triggerLists[tv] = null;
2240        }
2241    }
2242
2243    /** @todo fredt - reused structures to be reviewed for multi-threading */
2244
2245    /**
2246     * Reusable set of all FK constraints that have so far been enforced while
2247     * a cascading insert or delete is in progress. This is emptied and passed
2248     * with the first call to checkCascadeDelete or checkCascadeUpdate. During
2249     * recursion, if an FK constraint is encountered and is already present
2250     * in the set, the recursion stops.
2251     */

2252    HashSet constraintPath;
2253
2254    /**
2255     * Current list of updates on this table. This is emptied once a cascading
2256     * operation is over.
2257     */

2258    HashMappedList tableUpdateList;
2259
2260// fredt@users 20020225 - patch 1.7.0 - CASCADING DELETES
2261

2262    /**
2263     * Method is called recursively on a tree of tables from the current one
2264     * until no referring foreign-key table is left. In the process, if a
2265     * non-cascading foreign-key referring table contains data, an exception
2266     * is thrown. Parameter delete indicates whether to delete refering rows.
2267     * The method is called first to check if the row can be deleted, then to
2268     * delete the row and all the refering rows.<p>
2269     *
2270     * Support added for SET NULL and SET DEFAULT by kloska@users involves
2271     * switching to checkCascadeUpdate(,,,,) when these rules are encountered
2272     * in the constraint.(fredt@users)
2273     *
2274     * @table table table to update
2275     * @param tableUpdateLists list of update lists
2276     * @param row row to delete
2277     * @param session
2278     * @param delete
2279     * @param path
2280     * @throws HsqlException
2281     */

2282    static void checkCascadeDelete(Session session, Table table,
2283                                   HashMappedList tableUpdateLists, Row row,
2284                                   boolean delete,
2285                                   HashSet path) throws HsqlException {
2286
2287        for (int i = 0, size = table.constraintList.length; i < size; i++) {
2288            Constraint c = table.constraintList[i];
2289
2290            if (c.getType() != Constraint.MAIN || c.getRef() == null) {
2291                continue;
2292            }
2293
2294            RowIterator refiterator = c.findFkRef(session, row.getData(),
2295                                                  delete);
2296
2297            if (!refiterator.hasNext()) {
2298                continue;
2299            }
2300
2301            try {
2302                if (c.core.deleteAction == Constraint.NO_ACTION) {
2303                    if (c.core.mainTable == c.core.refTable) {
2304                        Row refrow = refiterator.next();
2305
2306                        // fredt - it's the same row
2307
// this supports deleting a single row
2308
// in future we can iterate over and check against
2309
// the full delete row list to enable multi-row
2310
// with self-referencing FK's deletes
2311
if (row.equals(refrow)) {
2312                            continue;
2313                        }
2314                    }
2315
2316                    throw Trace.error(Trace.INTEGRITY_CONSTRAINT_VIOLATION,
2317                                      Trace.Constraint_violation,
2318                                      new Object JavaDoc[] {
2319                        c.core.fkName.name, c.core.refTable.getName().name
2320                    });
2321                }
2322
2323                Table reftable = c.getRef();
2324
2325                // shortcut when deltable has no imported constraint
2326
boolean hasref =
2327                    reftable.getNextConstraintIndex(0, Constraint.MAIN) != -1;
2328
2329                // if (reftable == this) we don't need to go further and can return ??
2330
if (delete == false && hasref == false) {
2331                    continue;
2332                }
2333
2334                Index refindex = c.getRefIndex();
2335                int[] m_columns = c.getMainColumns();
2336                int[] r_columns = c.getRefColumns();
2337                Object JavaDoc[] mdata = row.getData();
2338                boolean isUpdate = c.getDeleteAction() == Constraint.SET_NULL
2339                                   || c.getDeleteAction()
2340                                      == Constraint.SET_DEFAULT;
2341
2342                // -- list for records to be inserted if this is
2343
// -- a 'ON DELETE SET [NULL|DEFAULT]' constraint
2344
HashMappedList rowSet = null;
2345
2346                if (isUpdate) {
2347                    rowSet = (HashMappedList) tableUpdateLists.get(reftable);
2348
2349                    if (rowSet == null) {
2350                        rowSet = new HashMappedList();
2351
2352                        tableUpdateLists.add(reftable, rowSet);
2353                    }
2354                }
2355
2356                // walk the index for all the nodes that reference delnode
2357
for (;;) {
2358                    Row refrow = refiterator.next();
2359
2360                    if (refrow == null || refrow.isCascadeDeleted()
2361                            || refindex.compareRowNonUnique(
2362                                session, mdata, m_columns,
2363                                refrow.getData()) != 0) {
2364                        break;
2365                    }
2366
2367                    // -- if the constraint is a 'SET [DEFAULT|NULL]' constraint we have to keep
2368
// -- a new record to be inserted after deleting the current. We also have to
2369
// -- switch over to the 'checkCascadeUpdate' method below this level
2370
if (isUpdate) {
2371                        Object JavaDoc[] rnd = reftable.getEmptyRowData();
2372
2373                        System.arraycopy(refrow.getData(), 0, rnd, 0,
2374                                         rnd.length);
2375
2376                        if (c.getDeleteAction() == Constraint.SET_NULL) {
2377                            for (int j = 0; j < r_columns.length; j++) {
2378                                rnd[r_columns[j]] = null;
2379                            }
2380                        } else {
2381                            for (int j = 0; j < r_columns.length; j++) {
2382                                Column col = reftable.getColumn(r_columns[j]);
2383
2384                                rnd[r_columns[j]] =
2385                                    col.getDefaultValue(session);
2386                            }
2387                        }
2388
2389                        if (hasref && path.add(c)) {
2390
2391                            // fredt - avoid infinite recursion on circular references
2392
// these can be rings of two or more mutually dependent tables
2393
// so only one visit per constraint is allowed
2394
checkCascadeUpdate(session, reftable, null,
2395                                               refrow, rnd, r_columns, null,
2396                                               path);
2397                            path.remove(c);
2398                        }
2399
2400                        if (delete) {
2401
2402                            // foreign key referencing own table - do not update the row to be deleted
2403
if (reftable != table ||!refrow.equals(row)) {
2404                                mergeUpdate(rowSet, refrow, rnd, r_columns);
2405                            }
2406                        }
2407                    } else if (hasref) {
2408                        if (reftable != table) {
2409                            if (path.add(c)) {
2410                                checkCascadeDelete(session, reftable,
2411                                                   tableUpdateLists, refrow,
2412                                                   delete, path);
2413                                path.remove(c);
2414                            }
2415                        } else {
2416
2417                            // fredt - we avoid infinite recursion on the fk's referencing the same table
2418
// but chained rows can result in very deep recursion and StackOverflowError
2419
if (refrow != row) {
2420                                checkCascadeDelete(session, reftable,
2421                                                   tableUpdateLists, refrow,
2422                                                   delete, path);
2423                            }
2424                        }
2425                    }
2426
2427                    if (delete &&!isUpdate &&!refrow.isCascadeDeleted()) {
2428                        reftable.deleteNoRefCheck(session, refrow);
2429                    }
2430                }
2431            } finally {
2432                refiterator.release();
2433            }
2434        }
2435    }
2436
2437    /**
2438     * Check or perform an update cascade operation on a single row.
2439     * Check or cascade an update (delete/insert) operation.
2440     * The method takes a pair of rows (new data,old data) and checks
2441     * if Constraints permit the update operation.
2442     * A boolean arguement determines if the operation should
2443     * realy take place or if we just have to check for constraint violation.
2444     * fredt - cyclic conditions are now avoided by checking for second visit
2445     * to each constraint. The set of list of updates for all tables is passed
2446     * and filled in recursive calls.
2447     *
2448     * @param session current database session
2449     * @param table
2450     * @param tableUpdateLists lists of updates
2451     * @param orow old row data to be deleted.
2452     * @param nrow new row data to be inserted.
2453     * @param cols indices of the columns actually changed.
2454     * @param ref This should be initialized to null when the
2455     * method is called from the 'outside'. During recursion this will be the
2456     * current table (i.e. this) to indicate from where we came.
2457     * Foreign keys to this table do not have to be checked since they have
2458     * triggered the update and are valid by definition.
2459     *
2460     * @short Check or perform and update cascade operation on a single row.
2461     *
2462     *
2463     */

2464    static void checkCascadeUpdate(Session session, Table table,
2465                                   HashMappedList tableUpdateLists, Row orow,
2466                                   Object JavaDoc[] nrow, int[] cols, Table ref,
2467                                   HashSet path) throws HsqlException {
2468
2469        // -- We iterate through all constraints associated with this table
2470
// --
2471
for (int i = 0, size = table.constraintList.length; i < size; i++) {
2472            Constraint c = table.constraintList[i];
2473
2474            if (c.getType() == Constraint.FOREIGN_KEY && c.getRef() != null) {
2475
2476                // -- (1) If it is a foreign key constraint we have to check if the
2477
// -- main table still holds a record which allows the new values
2478
// -- to be set in the updated columns. This test however will be
2479
// -- skipped if the reference table is the main table since changes
2480
// -- in the reference table triggered the update and therefor
2481
// -- the referential integrity is guaranteed to be valid.
2482
// --
2483
if (ref == null || c.getMain() != ref) {
2484
2485                    // -- common indexes of the changed columns and the main/ref constraint
2486
if (ArrayUtil.countCommonElements(cols, c.getRefColumns())
2487                            == 0) {
2488
2489                        // -- Table::checkCascadeUpdate -- NO common cols; reiterating
2490
continue;
2491                    }
2492
2493                    c.hasMainRef(session, nrow);
2494                }
2495            } else if (c.getType() == Constraint.MAIN && c.getRef() != null) {
2496
2497                // -- (2) If it happens to be a main constraint we check if the slave
2498
// -- table holds any records refering to the old contents. If so,
2499
// -- the constraint has to support an 'on update' action or we
2500
// -- throw an exception (all via a call to Constraint.findFkRef).
2501
// --
2502
// -- If there are no common columns between the reference constraint
2503
// -- and the changed columns, we reiterate.
2504
int[] common = ArrayUtil.commonElements(cols,
2505                    c.getMainColumns());
2506
2507                if (common == null) {
2508
2509                    // -- NO common cols between; reiterating
2510
continue;
2511                }
2512
2513                int[] m_columns = c.getMainColumns();
2514                int[] r_columns = c.getRefColumns();
2515
2516                // fredt - find out if the FK columns have actually changed
2517
boolean nochange = true;
2518
2519                for (int j = 0; j < m_columns.length; j++) {
2520                    if (!orow.getData()[m_columns[j]].equals(
2521                            nrow[m_columns[j]])) {
2522                        nochange = false;
2523
2524                        break;
2525                    }
2526                }
2527
2528                if (nochange) {
2529                    continue;
2530                }
2531
2532                // there must be no record in the 'slave' table
2533
// sebastian@scienion -- dependent on forDelete | forUpdate
2534
RowIterator refiterator = c.findFkRef(session,
2535                                                      orow.getData(), false);
2536
2537                if (refiterator.hasNext()) {
2538                    if (c.core.updateAction == Constraint.NO_ACTION) {
2539                        throw Trace.error(
2540                            Trace.INTEGRITY_CONSTRAINT_VIOLATION,
2541                            Trace.Constraint_violation, new Object JavaDoc[] {
2542                            c.core.fkName.name, c.core.refTable.getName().name
2543                        });
2544                    }
2545                } else {
2546
2547                    // no referencing row found
2548
continue;
2549                }
2550
2551                Table reftable = c.getRef();
2552
2553                // -- unused shortcut when update table has no imported constraint
2554
boolean hasref =
2555                    reftable.getNextConstraintIndex(0, Constraint.MAIN) != -1;
2556                Index refindex = c.getRefIndex();
2557
2558                // -- walk the index for all the nodes that reference update node
2559
HashMappedList rowSet =
2560                    (HashMappedList) tableUpdateLists.get(reftable);
2561
2562                if (rowSet == null) {
2563                    rowSet = new HashMappedList();
2564
2565                    tableUpdateLists.add(reftable, rowSet);
2566                }
2567
2568                for (Row refrow = refiterator.next(); ;
2569                        refrow = refiterator.next()) {
2570                    if (refrow == null
2571                            || refindex.compareRowNonUnique(
2572                                session, orow.getData(), m_columns,
2573                                refrow.getData()) != 0) {
2574                        break;
2575                    }
2576
2577                    Object JavaDoc[] rnd = reftable.getEmptyRowData();
2578
2579                    System.arraycopy(refrow.getData(), 0, rnd, 0, rnd.length);
2580
2581                    // -- Depending on the type constraint we are dealing with we have to
2582
// -- fill up the forign key of the current record with different values
2583
// -- And handle the insertion procedure differently.
2584
if (c.getUpdateAction() == Constraint.SET_NULL) {
2585
2586                        // -- set null; we do not have to check referential integrity any further
2587
// -- since we are setting <code>null</code> values
2588
for (int j = 0; j < r_columns.length; j++) {
2589                            rnd[r_columns[j]] = null;
2590                        }
2591                    } else if (c.getUpdateAction()
2592                               == Constraint.SET_DEFAULT) {
2593
2594                        // -- set default; we check referential integrity with ref==null; since we manipulated
2595
// -- the values and referential integrity is no longer guaranteed to be valid
2596
for (int j = 0; j < r_columns.length; j++) {
2597                            Column col = reftable.getColumn(r_columns[j]);
2598
2599                            rnd[r_columns[j]] = col.getDefaultValue(session);
2600                        }
2601
2602                        if (path.add(c)) {
2603                            checkCascadeUpdate(session, reftable,
2604                                               tableUpdateLists, refrow, rnd,
2605                                               r_columns, null, path);
2606                            path.remove(c);
2607                        }
2608                    } else {
2609
2610                        // -- cascade; standard recursive call. We inherit values from the foreign key
2611
// -- table therefor we set ref==this.
2612
for (int j = 0; j < m_columns.length; j++) {
2613                            rnd[r_columns[j]] = nrow[m_columns[j]];
2614                        }
2615
2616                        if (path.add(c)) {
2617                            checkCascadeUpdate(session, reftable,
2618                                               tableUpdateLists, refrow, rnd,
2619                                               common, table, path);
2620                            path.remove(c);
2621                        }
2622                    }
2623
2624                    mergeUpdate(rowSet, refrow, rnd, r_columns);
2625                }
2626            }
2627        }
2628    }
2629
2630    /**
2631     * Merges a triggered change with a previous triggered change, or adds to
2632     * list.
2633     */

2634    static void mergeUpdate(HashMappedList rowSet, Row row, Object JavaDoc[] newData,
2635                            int[] cols) {
2636
2637        Object JavaDoc[] data = (Object JavaDoc[]) rowSet.get(row);
2638
2639        if (data != null) {
2640            for (int j = 0; j < cols.length; j++) {
2641                data[cols[j]] = newData[cols[j]];
2642            }
2643        } else {
2644            rowSet.add(row, newData);
2645        }
2646    }
2647
2648    /**
2649     * Merge the full triggered change with the updated row, or add to list.
2650     * Return false if changes conflict.
2651     */

2652    static boolean mergeKeepUpdate(Session session, HashMappedList rowSet,
2653                                   int[] cols, int[] colTypes, Row row,
2654                                   Object JavaDoc[] newData) throws HsqlException {
2655
2656        Object JavaDoc[] data = (Object JavaDoc[]) rowSet.get(row);
2657
2658        if (data != null) {
2659            if (Index.compareRows(
2660                    session, row
2661                        .getData(), newData, cols, colTypes) != 0 && Index
2662                            .compareRows(
2663                                session, newData, data, cols, colTypes) != 0) {
2664                return false;
2665            }
2666
2667            for (int j = 0; j < cols.length; j++) {
2668                newData[cols[j]] = data[cols[j]];
2669            }
2670
2671            rowSet.put(row, newData);
2672        } else {
2673            rowSet.add(row, newData);
2674        }
2675
2676        return true;
2677    }
2678
2679    static void clearUpdateLists(HashMappedList tableUpdateList) {
2680
2681        for (int i = 0; i < tableUpdateList.size(); i++) {
2682            HashMappedList updateList =
2683                (HashMappedList) tableUpdateList.get(i);
2684
2685            updateList.clear();
2686        }
2687    }
2688
2689    /**
2690     * Highest level multiple row delete method. Corresponds to an SQL
2691     * DELETE.
2692     */

2693    int delete(Session session,
2694               HsqlArrayList deleteList) throws HsqlException {
2695
2696        HashSet path = constraintPath == null ? new HashSet()
2697                                              : constraintPath;
2698
2699        constraintPath = null;
2700
2701        HashMappedList tUpdateList = tableUpdateList == null
2702                                     ? new HashMappedList()
2703                                     : tableUpdateList;
2704
2705        tableUpdateList = null;
2706
2707        if (database.isReferentialIntegrity()) {
2708            for (int i = 0; i < deleteList.size(); i++) {
2709                Row row = (Row) deleteList.get(i);
2710
2711                path.clear();
2712                checkCascadeDelete(session, this, tUpdateList, row, false,
2713                                   path);
2714            }
2715        }
2716
2717        // check transactions
2718
database.txManager.checkDelete(session, deleteList);
2719
2720        for (int i = 0; i < tUpdateList.size(); i++) {
2721            Table table = (Table) tUpdateList.getKey(i);
2722            HashMappedList updateList = (HashMappedList) tUpdateList.get(i);
2723
2724            database.txManager.checkDelete(session, updateList);
2725        }
2726
2727        // perform delete
2728
fireAll(session, Trigger.DELETE_BEFORE);
2729
2730        if (database.isReferentialIntegrity()) {
2731            for (int i = 0; i < deleteList.size(); i++) {
2732                Row row = (Row) deleteList.get(i);
2733
2734                path.clear();
2735                checkCascadeDelete(session, this, tUpdateList, row, true,
2736                                   path);
2737            }
2738        }
2739
2740        for (int i = 0; i < deleteList.size(); i++) {
2741            Row row = (Row) deleteList.get(i);
2742
2743            if (!row.isCascadeDeleted()) {
2744                deleteNoRefCheck(session, row);
2745            }
2746        }
2747
2748        for (int i = 0; i < tUpdateList.size(); i++) {
2749            Table table = (Table) tUpdateList.getKey(i);
2750            HashMappedList updateList = (HashMappedList) tUpdateList.get(i);
2751
2752            table.updateRowSet(session, updateList, null, false);
2753            updateList.clear();
2754        }
2755
2756        fireAll(session, Trigger.DELETE_AFTER);
2757        path.clear();
2758
2759        constraintPath = path;
2760        tableUpdateList = tUpdateList;
2761
2762        return deleteList.size();
2763    }
2764
2765    /**
2766     * Mid level row delete method. Fires triggers but no integrity
2767     * constraint checks.
2768     */

2769    private void deleteNoRefCheck(Session session,
2770                                  Row row) throws HsqlException {
2771
2772        Object JavaDoc[] data = row.getData();
2773
2774        fireAll(session, Trigger.DELETE_BEFORE_ROW, data, null);
2775        deleteNoCheck(session, row, true);
2776
2777        // fire the delete after statement trigger
2778
fireAll(session, Trigger.DELETE_AFTER_ROW, data, null);
2779    }
2780
2781    /**
2782     * Low level row delete method. Removes the row from the indexes and
2783     * from the Cache.
2784     */

2785    private void deleteNoCheck(Session session, Row row,
2786                               boolean log) throws HsqlException {
2787
2788        if (row.isCascadeDeleted()) {
2789            return;
2790        }
2791
2792        Object JavaDoc[] data = row.getData();
2793
2794        row = row.getUpdatedRow();
2795
2796        for (int i = indexList.length - 1; i >= 0; i--) {
2797            Node node = row.getNode(i);
2798
2799            indexList[i].delete(session, node);
2800        }
2801
2802        row.delete();
2803
2804        if (session != null) {
2805            session.addDeleteAction(this, row);
2806        }
2807
2808        if (log && isLogged) {
2809            database.logger.writeDeleteStatement(session, this, data);
2810        }
2811    }
2812
2813    /**
2814     * For log statements.
2815     */

2816    public void deleteNoCheckFromLog(Session session,
2817                                     Object JavaDoc[] data) throws HsqlException {
2818
2819        Row row = null;
2820
2821        if (hasPrimaryKey()) {
2822            RowIterator it = getPrimaryIndex().findFirstRow(session, data,
2823                primaryKeyColsSequence);
2824
2825            row = it.next();
2826        } else if (bestIndex == null) {
2827            RowIterator it = getPrimaryIndex().firstRow(session);
2828
2829            while (true) {
2830                row = it.next();
2831
2832                if (row == null) {
2833                    break;
2834                }
2835
2836                if (Index.compareRows(
2837                        session, row.getData(), data, defaultColumnMap,
2838                        colTypes) == 0) {
2839                    break;
2840                }
2841            }
2842        } else {
2843            RowIterator it = bestIndex.findFirstRow(session, data);
2844
2845            while (true) {
2846                row = it.next();
2847
2848                if (row == null) {
2849                    break;
2850                }
2851
2852                Object JavaDoc[] rowdata = row.getData();
2853
2854                // reached end of range
2855
if (bestIndex.compareRowNonUnique(
2856                        session, data, bestIndex.getColumns(),
2857                        rowdata) != 0) {
2858                    row = null;
2859
2860                    break;
2861                }
2862
2863                if (Index.compareRows(
2864                        session, rowdata, data, defaultColumnMap,
2865                        colTypes) == 0) {
2866                    break;
2867                }
2868            }
2869        }
2870
2871        if (row == null) {
2872            return;
2873        }
2874
2875        // not necessary for log deletes
2876
database.txManager.checkDelete(session, row);
2877
2878        for (int i = indexList.length - 1; i >= 0; i--) {
2879            Node node = row.getNode(i);
2880
2881            indexList[i].delete(session, node);
2882        }
2883
2884        row.delete();
2885
2886        if (session != null) {
2887            session.addDeleteAction(this, row);
2888        }
2889    }
2890
2891    /**
2892     * Low level row delete method. Removes the row from the indexes and
2893     * from the Cache. Used by rollback.
2894     */

2895    void deleteNoCheckRollback(Session session, Row row,
2896                               boolean log) throws HsqlException {
2897
2898        row = indexList[0].findRow(session, row);
2899
2900        for (int i = indexList.length - 1; i >= 0; i--) {
2901            Node node = row.getNode(i);
2902
2903            indexList[i].delete(session, node);
2904        }
2905
2906        row.delete();
2907        removeRowFromStore(row);
2908
2909        if (log && isLogged) {
2910            database.logger.writeDeleteStatement(session, this,
2911                                                 row.getData());
2912        }
2913    }
2914
2915    /**
2916     * Highest level multiple row update method. Corresponds to an SQL
2917     * UPDATE. To DEAL with unique constraints we need to perform all
2918     * deletes at once before the inserts. If there is a UNIQUE constraint
2919     * violation limited only to the duration of updating multiple rows,
2920     * we don't want to abort the operation. Example:
2921     * UPDATE MYTABLE SET UNIQUECOL = UNIQUECOL + 1
2922     * After performing each cascade update, delete the main row.
2923     * After all cascade ops and deletes have been performed, insert new
2924     * rows.
2925     *
2926     * The following clauses from SQL Standard section 11.8 are enforced
2927     * 9) Let ISS be the innermost SQL-statement being executed.
2928     * 10) If evaluation of these General Rules during the execution of ISS
2929     * would cause an update of some site to a value that is distinct from the
2930     * value to which that site was previously updated during the execution of
2931     * ISS, then an exception condition is raised: triggered data change
2932     * violation.
2933     * 11) If evaluation of these General Rules during the execution of ISS
2934     * would cause deletion of a row containing a site that is identified for
2935     * replacement in that row, then an exception condition is raised:
2936     * triggered data change violation.
2937     *
2938     * (fredt)
2939     */

2940    int update(Session session, HashMappedList updateList,
2941               int[] cols) throws HsqlException {
2942
2943        HashSet path = constraintPath == null ? new HashSet()
2944                                              : constraintPath;
2945
2946        constraintPath = null;
2947
2948        HashMappedList tUpdateList = tableUpdateList == null
2949                                     ? new HashMappedList()
2950                                     : tableUpdateList;
2951
2952        tableUpdateList = null;
2953
2954        // set identity column where null and check columns
2955
for (int i = 0; i < updateList.size(); i++) {
2956            Object JavaDoc[] data = (Object JavaDoc[]) updateList.get(i);
2957
2958            // this means the identity column can be set to null to force
2959
// creation of a new identity value
2960
setIdentityColumn(session, data);
2961            enforceFieldValueLimits(data, cols);
2962            enforceNullConstraints(data);
2963        }
2964
2965        // perform check/cascade operations
2966
if (database.isReferentialIntegrity()) {
2967            for (int i = 0; i < updateList.size(); i++) {
2968                Object JavaDoc[] data = (Object JavaDoc[]) updateList.get(i);
2969                Row row = (Row) updateList.getKey(i);
2970
2971                checkCascadeUpdate(session, this, tUpdateList, row, data,
2972                                   cols, null, path);
2973            }
2974        }
2975
2976        fireAll(session, Trigger.UPDATE_BEFORE);
2977
2978        // merge any triggered change to this table with the update list
2979
HashMappedList triggeredList = (HashMappedList) tUpdateList.get(this);
2980
2981        if (triggeredList != null) {
2982            for (int i = 0; i < triggeredList.size(); i++) {
2983                Row row = (Row) triggeredList.getKey(i);
2984                Object JavaDoc[] data = (Object JavaDoc[]) triggeredList.get(i);
2985
2986                mergeKeepUpdate(session, updateList, cols, colTypes, row,
2987                                data);
2988            }
2989
2990            triggeredList.clear();
2991        }
2992
2993        // check transactions
2994
for (int i = 0; i < tUpdateList.size(); i++) {
2995            Table table = (Table) tUpdateList.getKey(i);
2996            HashMappedList updateListT = (HashMappedList) tUpdateList.get(i);
2997
2998            database.txManager.checkDelete(session, updateListT);
2999        }
3000
3001        database.txManager.checkDelete(session, updateList);
3002
3003        // update lists - main list last
3004
for (int i = 0; i < tUpdateList.size(); i++) {
3005            Table table = (Table) tUpdateList.getKey(i);
3006            HashMappedList updateListT = (HashMappedList) tUpdateList.get(i);
3007
3008            table.updateRowSet(session, updateListT, null, false);
3009            updateListT.clear();
3010        }
3011
3012        updateRowSet(session, updateList, cols, true);
3013        fireAll(session, Trigger.UPDATE_AFTER);
3014        path.clear();
3015
3016        constraintPath = path;
3017        tableUpdateList = tUpdateList;
3018
3019        clearUpdateLists(tableUpdateList);
3020
3021        return updateList.size();
3022    }
3023
3024    void updateRowSet(Session session, HashMappedList rowSet, int[] cols,
3025                      boolean nodelete) throws HsqlException {
3026
3027        for (int i = rowSet.size() - 1; i >= 0; i--) {
3028            Row row = (Row) rowSet.getKey(i);
3029            Object JavaDoc[] data = (Object JavaDoc[]) rowSet.get(i);
3030
3031            if (row.isCascadeDeleted()) {
3032                if (nodelete) {
3033                    throw Trace.error(Trace.TRIGGERED_DATA_CHANGE);
3034                } else {
3035                    rowSet.remove(i);
3036
3037                    continue;
3038                }
3039            }
3040
3041            for (int j = 0; j < constraintList.length; j++) {
3042                Constraint c = constraintList[j];
3043
3044                if (c.getType() == Constraint.CHECK) {
3045                    c.checkCheckConstraint(session, data);
3046
3047                    continue;
3048                }
3049            }
3050
3051            deleteNoCheck(session, row, true);
3052        }
3053
3054        for (int i = 0; i < rowSet.size(); i++) {
3055            Row row = (Row) rowSet.getKey(i);
3056            Object JavaDoc[] data = (Object JavaDoc[]) rowSet.get(i);
3057
3058            if (triggerLists[Trigger.UPDATE_BEFORE_ROW] != null) {
3059                fireAll(session, Trigger.UPDATE_BEFORE_ROW, row.getData(),
3060                        data);
3061                checkRowDataUpdate(session, data, cols);
3062            }
3063
3064            insertNoCheck(session, data);
3065
3066            if (triggerLists[Trigger.UPDATE_AFTER_ROW] != null) {
3067                fireAll(session, Trigger.UPDATE_AFTER_ROW, row.getData(),
3068                        data);
3069                checkRowDataUpdate(session, data, cols);
3070            }
3071        }
3072    }
3073
3074    void checkRowDataInsert(Session session,
3075                            Object JavaDoc[] data) throws HsqlException {
3076
3077        enforceFieldValueLimits(data, null);
3078        enforceNullConstraints(data);
3079
3080        if (database.isReferentialIntegrity()) {
3081            for (int i = 0, size = constraintList.length; i < size; i++) {
3082                constraintList[i].checkInsert(session, data);
3083            }
3084        }
3085    }
3086
3087    void checkRowDataUpdate(Session session, Object JavaDoc[] data,
3088                            int[] cols) throws HsqlException {
3089
3090        enforceFieldValueLimits(data, cols);
3091        enforceNullConstraints(data);
3092
3093        for (int j = 0; j < constraintList.length; j++) {
3094            Constraint c = constraintList[j];
3095
3096            if (c.getType() == Constraint.CHECK) {
3097                c.checkCheckConstraint(session, data);
3098            }
3099        }
3100    }
3101
3102    /**
3103     * True if table is CACHED or TEXT
3104     *
3105     * @return
3106     */

3107    public boolean isCached() {
3108        return isCached;
3109    }
3110
3111    /**
3112     * Returns true if table is CACHED
3113     */

3114    boolean isIndexCached() {
3115        return isCached;
3116    }
3117
3118    /**
3119     * Returns the index of the Index object of the given name or -1 if not found.
3120     */

3121    int getIndexIndex(String JavaDoc indexName) {
3122
3123        Index[] indexes = indexList;
3124
3125        for (int i = 0; i < indexes.length; i++) {
3126            if (indexName.equals(indexes[i].getName().name)) {
3127                return i;
3128            }
3129        }
3130
3131        // no such index
3132
return -1;
3133    }
3134
3135    /**
3136     * Returns the Index object of the given name or null if not found.
3137     */

3138    Index getIndex(String JavaDoc indexName) {
3139
3140        Index[] indexes = indexList;
3141        int i = getIndexIndex(indexName);
3142
3143        return i == -1 ? null
3144                       : indexes[i];
3145    }
3146
3147    /**
3148     * Return the position of the constraint within the list
3149     */

3150    int getConstraintIndex(String JavaDoc constraintName) {
3151
3152        for (int i = 0, size = constraintList.length; i < size; i++) {
3153            if (constraintList[i].getName().name.equals(constraintName)) {
3154                return i;
3155            }
3156        }
3157
3158        return -1;
3159    }
3160
3161    /**
3162     * return the named constriant
3163     */

3164    Constraint getConstraint(String JavaDoc constraintName) {
3165
3166        int i = getConstraintIndex(constraintName);
3167
3168        return (i < 0) ? null
3169                       : (Constraint) constraintList[i];
3170    }
3171
3172    /**
3173     * remove a named constraint
3174     */

3175    void removeConstraint(String JavaDoc name) {
3176
3177        int index = getConstraintIndex(name);
3178
3179        constraintList =
3180            (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, null,
3181                index, -1);
3182    }
3183
3184    /**
3185     * Returns the Column object at the given index
3186     */

3187    Column getColumn(int i) {
3188        return (Column) columnList.get(i);
3189    }
3190
3191    void renameColumn(Column column, String JavaDoc newName,
3192                      boolean isquoted) throws HsqlException {
3193
3194        String JavaDoc oldname = column.columnName.name;
3195        int i = getColumnNr(oldname);
3196
3197        columnList.setKey(i, newName);
3198        column.columnName.rename(newName, isquoted);
3199        renameColumnInCheckConstraints(oldname, newName, isquoted);
3200    }
3201
3202    /**
3203     * Returns an array of int valuse indicating the SQL type of the columns
3204     */

3205    public int[] getColumnTypes() {
3206        return colTypes;
3207    }
3208
3209    /**
3210     * Returns the Index object at the given index
3211     */

3212    public Index getIndex(int i) {
3213        return indexList[i];
3214    }
3215
3216    public Index[] getIndexes() {
3217        return indexList;
3218    }
3219
3220    /**
3221     * Used by CACHED tables to fetch a Row from the Cache, resulting in the
3222     * Row being read from disk if it is not in the Cache.
3223     *
3224     * TEXT tables pass the memory resident Node parameter so that the Row
3225     * and its index Nodes can be relinked.
3226     */

3227    CachedRow getRow(int pos, Node primarynode) throws HsqlException {
3228
3229        if (isText) {
3230            CachedDataRow row = (CachedDataRow) rowStore.get(pos);
3231
3232            row.nPrimaryNode = primarynode;
3233
3234            return row;
3235        } else if (isCached) {
3236            return (CachedRow) rowStore.get(pos);
3237        }
3238
3239        return null;
3240    }
3241
3242    /**
3243     * As above, only for CACHED tables
3244     */

3245    CachedRow getRow(int pos) {
3246        return (CachedRow) rowStore.get(pos);
3247    }
3248
3249    /**
3250     * As above, only for CACHED tables
3251     */

3252    CachedRow getRow(long id) {
3253        return (CachedRow) rowStore.get((int) id);
3254    }
3255
3256    /**
3257     * called in autocommit mode or by transaction manager when a a delete is committed
3258     */

3259    void removeRowFromStore(Row row) throws HsqlException {
3260
3261        if (isCached || isText && cache != null) {
3262            rowStore.remove(row.getPos());
3263        }
3264    }
3265
3266    void releaseRowFromStore(Row row) throws HsqlException {
3267
3268        if (isCached || isText && cache != null) {
3269            rowStore.release(row.getPos());
3270        }
3271    }
3272
3273    void commitRowToStore(Row row) {
3274
3275        if (isText && cache != null) {
3276            rowStore.commit(row);
3277        }
3278    }
3279
3280    void indexRow(Session session, Row row) throws HsqlException {
3281
3282        int i = 0;
3283
3284        try {
3285            for (; i < indexList.length; i++) {
3286                indexList[i].insert(session, row, i);
3287            }
3288        } catch (HsqlException e) {
3289            Index index = indexList[i];
3290            boolean isconstraint = index.isConstraint;
3291
3292            // unique index violation - rollback insert
3293
for (--i; i >= 0; i--) {
3294                Node n = row.getNode(i);
3295
3296                indexList[i].delete(session, n);
3297            }
3298
3299            row.delete();
3300            removeRowFromStore(row);
3301
3302            if (isconstraint) {
3303                Constraint c = getUniqueOrPKConstraintForIndex(index);
3304                String JavaDoc name = c == null ? index.getName().name
3305                                            : c.getName().name;
3306
3307                throw Trace.error(Trace.VIOLATION_OF_UNIQUE_CONSTRAINT, name);
3308            }
3309
3310            throw e;
3311        }
3312    }
3313
3314    /**
3315     *
3316     */

3317    void clearAllRows(Session session) {
3318
3319        for (int i = 0; i < indexList.length; i++) {
3320            indexList[i].clearAll(session);
3321        }
3322
3323        if (!isTemp) {
3324            identitySequence.reset();
3325            rowIdSequence.reset();
3326        }
3327    }
3328
3329/** @todo -- release the rows */
3330    void drop() throws HsqlException {}
3331
3332    boolean isWritable() {
3333        return !isReadOnly &&!database.databaseReadOnly
3334               &&!(database.isFilesReadOnly() && (isCached || isText));
3335    }
3336
3337    /**
3338     * Returns the catalog name or null, depending on a database property.
3339     */

3340    String JavaDoc getCatalogName() {
3341
3342        // PRE: database is never null
3343
return database.getProperties().isPropertyTrue("hsqldb.catalogs")
3344               ? database.getURI()
3345               : null;
3346    }
3347
3348    /**
3349     * Returns the schema name.
3350     */

3351    public String JavaDoc getSchemaName() {
3352        return tableName.schema.name;
3353    }
3354
3355    public int getRowCount(Session session) throws HsqlException {
3356        return getPrimaryIndex().size(session);
3357    }
3358
3359    /**
3360     * Necessary when over Integer.MAX_VALUE Row objects have been generated
3361     * for a memory table.
3362     */

3363    public void resetRowId(Session session) throws HsqlException {
3364
3365        if (isCached) {
3366            return;
3367        }
3368
3369        rowIdSequence = new NumberSequence(null, 0, 1, Types.BIGINT);
3370
3371        RowIterator it = getPrimaryIndex().firstRow(session);;
3372
3373        while (it.hasNext()) {
3374            Row row = it.next();
3375            int pos = (int) rowIdSequence.getValue();
3376
3377            row.setPos(pos);
3378        }
3379    }
3380
3381    /**
3382     * Factory method instantiates a Row based on table type.
3383     */

3384    Row newRow(Object JavaDoc[] o) throws HsqlException {
3385
3386        Row row;
3387
3388        try {
3389            if (isMemory) {
3390                row = new Row(this, o);
3391
3392                int pos = (int) rowIdSequence.getValue();
3393
3394                row.setPos(pos);
3395            } else {
3396                row = CachedRow.newCachedRow(this, o);
3397
3398                rowStore.add(row);
3399            }
3400        } catch (IOException JavaDoc e) {
3401            throw new HsqlException(
3402                e, Trace.getMessage(Trace.GENERAL_IO_ERROR),
3403                Trace.GENERAL_IO_ERROR);
3404        }
3405
3406        return row;
3407    }
3408
3409    Row restoreRow(Row oldrow) throws HsqlException {
3410
3411        Row row;
3412
3413        try {
3414            if (isMemory) {
3415                row = new Row(this, oldrow.oData);
3416
3417                row.setPos(oldrow.getPos());
3418            } else {
3419                row = CachedRow.newCachedRow(this, oldrow.oData);
3420
3421                row.setStorageSize(oldrow.getStorageSize());
3422                row.setPos(oldrow.getPos());
3423                rowStore.restore(row);
3424            }
3425        } catch (IOException JavaDoc e) {
3426            throw new HsqlException(
3427                e, Trace.getMessage(Trace.GENERAL_IO_ERROR),
3428                Trace.GENERAL_IO_ERROR);
3429        }
3430
3431        return row;
3432    }
3433
3434    public class RowStore implements PersistentStore {
3435
3436        public CachedObject get(int i) {
3437
3438            try {
3439                return cache.get(i, this, false);
3440            } catch (HsqlException e) {
3441                return null;
3442            }
3443        }
3444
3445        public CachedObject getKeep(int i) {
3446
3447            try {
3448                return cache.get(i, this, true);
3449            } catch (HsqlException e) {
3450                return null;
3451            }
3452        }
3453
3454        public int getStorageSize(int i) {
3455
3456            try {
3457                return cache.get(i, this, false).getStorageSize();
3458            } catch (HsqlException e) {
3459                return 0;
3460            }
3461        }
3462
3463        public void add(CachedObject row) throws IOException JavaDoc {
3464            cache.add(row);
3465        }
3466
3467        public void restore(CachedObject row) throws IOException JavaDoc {
3468            cache.restore(row);
3469        }
3470
3471        public CachedObject get(RowInputInterface in) {
3472
3473            try {
3474                if (Table.this.isText) {
3475                    return new CachedDataRow(Table.this, in);
3476                }
3477
3478                CachedObject row = new CachedRow(Table.this, in);
3479
3480                return row;
3481            } catch (HsqlException e) {
3482                return null;
3483            } catch (IOException JavaDoc e) {
3484                return null;
3485            }
3486        }
3487
3488        public CachedObject getNewInstance(int size) {
3489            return null;
3490        }
3491
3492        public void remove(int i) {
3493
3494            try {
3495                cache.remove(i, this);
3496            } catch (IOException JavaDoc e) {}
3497        }
3498
3499        public void removePersistence(int i) {
3500
3501            try {
3502                cache.removePersistence(i, this);
3503            } catch (IOException JavaDoc e) {
3504
3505                //
3506
}
3507        }
3508
3509        public void release(int i) {
3510            cache.release(i);
3511        }
3512
3513        public void commit(CachedObject row) {
3514
3515            try {
3516                if (Table.this.isText) {
3517                    cache.saveRow(row);
3518                }
3519            } catch (IOException JavaDoc e) {
3520
3521                //
3522
}
3523        }
3524    }
3525}
3526
Popular Tags