1 /* Copyright (c) 2001-2016, The HSQL Development 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 HSQL Development 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 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 31 32 package org.hsqldb; 33 34 import org.hsqldb.HsqlNameManager.HsqlName; 35 import org.hsqldb.RangeVariable.RangeIteratorBase; 36 import org.hsqldb.error.Error; 37 import org.hsqldb.error.ErrorCode; 38 import org.hsqldb.index.Index; 39 import org.hsqldb.index.Index.IndexUse; 40 import org.hsqldb.lib.ArrayUtil; 41 import org.hsqldb.lib.HashMappedList; 42 import org.hsqldb.lib.HsqlArrayList; 43 import org.hsqldb.lib.OrderedHashSet; 44 import org.hsqldb.lib.OrderedIntHashSet; 45 import org.hsqldb.lib.Set; 46 import org.hsqldb.lib.StringUtil; 47 import org.hsqldb.map.ValuePool; 48 import org.hsqldb.navigator.RowIterator; 49 import org.hsqldb.navigator.RowSetNavigator; 50 import org.hsqldb.navigator.RowSetNavigatorDataChange; 51 import org.hsqldb.persist.CachedObject; 52 import org.hsqldb.persist.DataSpaceManager; 53 import org.hsqldb.persist.PersistentStore; 54 import org.hsqldb.result.Result; 55 import org.hsqldb.rights.Grantee; 56 import org.hsqldb.types.BinaryData; 57 import org.hsqldb.types.CharacterType; 58 import org.hsqldb.types.Collation; 59 import org.hsqldb.types.Type; 60 61 /** 62 * Holds the data structures and methods for creation of a named database table. 63 * 64 * @author Fred Toussi (fredt@users dot sourceforge.net) 65 * @version 2.3.4 66 * @since 1.6.1 67 */ 68 public class Table extends TableBase implements SchemaObject { 69 70 public static final Table[] emptyArray = new Table[]{}; 71 72 // main properties 73 protected HsqlName tableName; 74 protected long changeTimestamp; 75 76 // 77 public HashMappedList columnList; // columns in table 78 int identityColumn; // -1 means no such column 79 NumberSequence identitySequence; // next value of identity column 80 81 // ----------------------------------------------------------------------- 82 Constraint[] constraintList; // constraint for the table 83 Constraint[] fkConstraints; // 84 Constraint[] fkMainConstraints; 85 Constraint[] checkConstraints; 86 TriggerDef[] triggerList; 87 TriggerDef[][] triggerLists; // array of trigger lists 88 Expression[] colDefaults; // expressions of DEFAULT values 89 private boolean hasDefaultValues; // shortcut for above 90 boolean[] colGenerated; // generated columns 91 private boolean hasGeneratedValues; // shortcut for above 92 boolean[] colUpdated; // auto update columns 93 private boolean hasUpdatedValues; // shortcut for above 94 boolean[] colRefFK; // foreign key columns 95 boolean[] colMainFK; // columns referenced by foreign key 96 int referentialActions; // has set null, set default or cascade 97 int cascadingDeletes; // has on delete cascade 98 boolean isDropped; // has been dropped 99 private boolean hasDomainColumns; // shortcut 100 private boolean hasNotNullColumns; // shortcut 101 protected int[] defaultColumnMap; // holding 0,1,2,3,... 102 RangeVariable[] defaultRanges; 103 104 // Table(Database database, HsqlName name, int type)105 public Table(Database database, HsqlName name, int type) { 106 107 this.database = database; 108 this.tableName = name; 109 this.persistenceId = database.persistentStoreCollection.getNextId(); 110 111 switch (type) { 112 113 case CHANGE_SET_TABLE : 114 persistenceScope = SCOPE_STATEMENT; 115 isSessionBased = true; 116 break; 117 118 case SYSTEM_SUBQUERY : 119 persistenceScope = SCOPE_STATEMENT; 120 isSessionBased = true; 121 break; 122 123 case INFO_SCHEMA_TABLE : 124 persistenceScope = SCOPE_TRANSACTION; 125 isSessionBased = true; 126 break; 127 128 case SYSTEM_TABLE : 129 persistenceScope = SCOPE_FULL; 130 isSchemaBased = true; 131 break; 132 133 case CACHED_TABLE : 134 if (database.logger.isFileDatabase()) { 135 persistenceScope = SCOPE_FULL; 136 isSchemaBased = true; 137 isCached = true; 138 isLogged = !database.isFilesReadOnly(); 139 140 break; 141 } 142 143 type = MEMORY_TABLE; 144 145 // fall through 146 case MEMORY_TABLE : 147 persistenceScope = SCOPE_FULL; 148 isSchemaBased = true; 149 isLogged = !database.isFilesReadOnly(); 150 break; 151 152 case TEMP_TABLE : 153 persistenceScope = SCOPE_TRANSACTION; 154 isTemp = true; 155 isSchemaBased = true; 156 isSessionBased = true; 157 break; 158 159 case TEMP_TEXT_TABLE : 160 persistenceScope = SCOPE_SESSION; 161 162 if (!database.logger.isFileDatabase()) { 163 throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY); 164 } 165 166 isSchemaBased = true; 167 isSessionBased = true; 168 isTemp = true; 169 isText = true; 170 isReadOnly = true; 171 break; 172 173 case TEXT_TABLE : 174 persistenceScope = SCOPE_FULL; 175 176 if (!database.logger.isFileDatabase()) { 177 if (!database.logger.isAllowedFullPath()) { 178 throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY); 179 } 180 181 isReadOnly = true; 182 } 183 184 isSchemaBased = true; 185 isText = true; 186 break; 187 188 case VIEW_TABLE : 189 persistenceScope = SCOPE_STATEMENT; 190 isSchemaBased = true; 191 isSessionBased = true; 192 isView = true; 193 break; 194 195 case RESULT_TABLE : 196 persistenceScope = SCOPE_SESSION; 197 isSessionBased = true; 198 break; 199 200 case TableBase.FUNCTION_TABLE : 201 persistenceScope = SCOPE_STATEMENT; 202 isSessionBased = true; 203 break; 204 205 default : 206 throw Error.runtimeError(ErrorCode.U_S0500, "Table"); 207 } 208 209 // type may have changed above for CACHED tables 210 tableType = type; 211 identityColumn = -1; 212 columnList = new HashMappedList(); 213 indexList = Index.emptyArray; 214 constraintList = Constraint.emptyArray; 215 fkConstraints = Constraint.emptyArray; 216 fkMainConstraints = Constraint.emptyArray; 217 checkConstraints = Constraint.emptyArray; 218 triggerList = TriggerDef.emptyArray; 219 triggerLists = new TriggerDef[TriggerDef.NUM_TRIGS][]; 220 221 for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) { 222 triggerLists[i] = TriggerDef.emptyArray; 223 } 224 225 if (database.isFilesReadOnly() && isFileBased()) { 226 this.isReadOnly = true; 227 } 228 } 229 230 /** trigger transition table */ Table(Table table, HsqlName name)231 public Table(Table table, HsqlName name) { 232 233 persistenceScope = SCOPE_STATEMENT; 234 name.schema = SqlInvariants.SYSTEM_SCHEMA_HSQLNAME; 235 this.tableName = name; 236 this.database = table.database; 237 this.tableType = RESULT_TABLE; 238 this.columnList = table.columnList; 239 this.columnCount = table.columnCount; 240 this.indexList = Index.emptyArray; 241 this.constraintList = Constraint.emptyArray; 242 243 createPrimaryKey(); 244 } 245 getType()246 public int getType() { 247 return SchemaObject.TABLE; 248 } 249 250 /** 251 * Returns the HsqlName object fo the table 252 */ getName()253 public final HsqlName getName() { 254 return tableName; 255 } 256 257 /** 258 * Returns the catalog name or null, depending on a database property. 259 */ getCatalogName()260 public HsqlName getCatalogName() { 261 return database.getCatalogName(); 262 } 263 264 /** 265 * Returns the schema name. 266 */ getSchemaName()267 public HsqlName getSchemaName() { 268 return tableName.schema; 269 } 270 getOwner()271 public Grantee getOwner() { 272 return tableName.schema.owner; 273 } 274 getReferences()275 public OrderedHashSet getReferences() { 276 277 OrderedHashSet set = new OrderedHashSet(); 278 279 if (identitySequence != null && identitySequence.getName() != null) { 280 set.add(identitySequence.getName()); 281 } 282 283 return set; 284 } 285 getDefaultRanges()286 public RangeVariable[] getDefaultRanges() { 287 288 if (defaultRanges == null) { 289 defaultRanges = new RangeVariable[]{ new RangeVariable(this, 0) }; 290 } 291 292 return defaultRanges; 293 } 294 getReferencesForDependents()295 public OrderedHashSet getReferencesForDependents() { 296 297 OrderedHashSet set = new OrderedHashSet(); 298 299 for (int i = 0; i < colTypes.length; i++) { 300 ColumnSchema column = getColumn(i); 301 OrderedHashSet refs = column.getReferences(); 302 303 if (refs != null && !refs.isEmpty()) { 304 set.add(column.getName()); 305 } 306 } 307 308 for (int i = 0; i < fkConstraints.length; i++) { 309 if (fkConstraints[i].getMainTableName() != getName()) { 310 set.add(fkConstraints[i].getName()); 311 } 312 } 313 314 for (int i = 0; i < triggerList.length; i++) { 315 set.add(triggerList[i].getName()); 316 } 317 318 return set; 319 } 320 getComponents()321 public OrderedHashSet getComponents() { 322 323 OrderedHashSet set = new OrderedHashSet(); 324 325 set.addAll(constraintList); 326 set.addAll(triggerList); 327 328 for (int i = 0; i < indexList.length; i++) { 329 if (!indexList[i].isConstraint()) { 330 set.add(indexList[i]); 331 } 332 } 333 334 return set; 335 } 336 compile(Session session, SchemaObject parentObject)337 public void compile(Session session, SchemaObject parentObject) { 338 339 for (int i = 0; i < columnCount; i++) { 340 ColumnSchema column = getColumn(i); 341 342 column.compile(session, this); 343 } 344 } 345 getSQL()346 public String getSQL() { 347 348 StringBuffer sb = new StringBuffer(); 349 350 sb.append(Tokens.T_CREATE).append(' '); 351 352 if (isTemp()) { 353 sb.append(Tokens.T_GLOBAL).append(' '); 354 sb.append(Tokens.T_TEMPORARY).append(' '); 355 } else if (isText()) { 356 sb.append(Tokens.T_TEXT).append(' '); 357 } else if (isCached()) { 358 sb.append(Tokens.T_CACHED).append(' '); 359 } else { 360 sb.append(Tokens.T_MEMORY).append(' '); 361 } 362 363 sb.append(Tokens.T_TABLE).append(' '); 364 sb.append(getName().getSchemaQualifiedStatementName()); 365 sb.append('('); 366 367 int[] pk = getPrimaryKey(); 368 Constraint pkConst = getPrimaryConstraint(); 369 370 for (int j = 0; j < columnCount; j++) { 371 ColumnSchema column = getColumn(j); 372 String colname = column.getName().statementName; 373 Type type = column.getDataType(); 374 375 if (j > 0) { 376 sb.append(','); 377 } 378 379 sb.append(colname); 380 sb.append(' '); 381 sb.append(type.getTypeDefinition()); 382 383 if (!type.isDistinctType() && !type.isDomainType()) { 384 if (type.isCharacterType()) { 385 Collation collation = 386 ((CharacterType) type).getCollation(); 387 388 if (collation.isObjectCollation()) { 389 sb.append(' ').append(collation.getCollateSQL()); 390 } 391 } 392 } 393 394 String defaultString = column.getDefaultSQL(); 395 396 if (defaultString != null) { 397 sb.append(' ').append(Tokens.T_DEFAULT).append(' '); 398 sb.append(defaultString); 399 } 400 401 if (column.isAutoUpdate()) { 402 sb.append(' ').append(Tokens.T_ON).append(' '); 403 sb.append(Tokens.T_UPDATE).append(' '); 404 sb.append(column.getUpdateExpression().getSQL()); 405 } 406 407 if (column.isIdentity()) { 408 sb.append(' ').append( 409 column.getIdentitySequence().getSQLColumnDefinition()); 410 } 411 412 if (column.isGenerated()) { 413 sb.append(' ').append(Tokens.T_GENERATED).append(' '); 414 sb.append(Tokens.T_ALWAYS).append(' ').append( 415 Tokens.T_AS).append(Tokens.T_OPENBRACKET); 416 sb.append(column.getGeneratingExpression().getSQL()); 417 sb.append(Tokens.T_CLOSEBRACKET); 418 } 419 420 if (!column.isNullable()) { 421 Constraint c = getNotNullConstraintForColumn(j); 422 423 if (c != null && !c.getName().isReservedName()) { 424 sb.append(' ').append(Tokens.T_CONSTRAINT).append( 425 ' ').append(c.getName().statementName); 426 } 427 428 sb.append(' ').append(Tokens.T_NOT).append(' ').append( 429 Tokens.T_NULL); 430 } 431 432 if (pk.length == 1 && j == pk[0] 433 && pkConst.getName().isReservedName()) { 434 sb.append(' ').append(Tokens.T_PRIMARY).append(' ').append( 435 Tokens.T_KEY); 436 } 437 } 438 439 Constraint[] constraintList = getConstraints(); 440 441 for (int j = 0, vSize = constraintList.length; j < vSize; j++) { 442 Constraint c = constraintList[j]; 443 444 if (!c.isForward) { 445 String d = c.getSQL(); 446 447 if (d.length() > 0) { 448 sb.append(','); 449 sb.append(d); 450 } 451 } 452 } 453 454 sb.append(')'); 455 456 if (onCommitPreserve()) { 457 sb.append(' ').append(Tokens.T_ON).append(' '); 458 sb.append(Tokens.T_COMMIT).append(' ').append(Tokens.T_PRESERVE); 459 sb.append(' ').append(Tokens.T_ROWS); 460 } 461 462 return sb.toString(); 463 } 464 getChangeTimestamp()465 public long getChangeTimestamp() { 466 return changeTimestamp; 467 } 468 setName(HsqlName name)469 public final void setName(HsqlName name) { 470 tableName = name; 471 } 472 getSQL(OrderedHashSet resolved, OrderedHashSet unresolved)473 String[] getSQL(OrderedHashSet resolved, OrderedHashSet unresolved) { 474 475 for (int i = 0; i < constraintList.length; i++) { 476 Constraint c = constraintList[i]; 477 478 if (c.isForward) { 479 unresolved.add(c); 480 } else if (c.getConstraintType() == SchemaObject.ConstraintTypes 481 .UNIQUE || c.getConstraintType() == SchemaObject 482 .ConstraintTypes.PRIMARY_KEY) { 483 resolved.add(c.getName()); 484 } 485 } 486 487 HsqlArrayList list = new HsqlArrayList(); 488 489 list.add(getSQL()); 490 491 if (!isTemp && !isText && identitySequence != null 492 && identitySequence.getName() == null) { 493 list.add(NumberSequence.getRestartSQL(this)); 494 } 495 496 for (int i = 0; i < indexList.length; i++) { 497 if (!indexList[i].isConstraint() 498 && indexList[i].getColumnCount() > 0) { 499 list.add(indexList[i].getSQL()); 500 } 501 } 502 503 String[] array = new String[list.size()]; 504 505 list.toArray(array); 506 507 return array; 508 } 509 getSQLForReadOnly()510 public String getSQLForReadOnly() { 511 512 if (isReadOnly) { 513 StringBuffer sb = new StringBuffer(64); 514 515 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append( 516 ' '); 517 sb.append(getName().getSchemaQualifiedStatementName()); 518 sb.append(' ').append(Tokens.T_READ).append(' '); 519 sb.append(Tokens.T_ONLY); 520 521 return sb.toString(); 522 } else { 523 return null; 524 } 525 } 526 getSQLForTextSource(boolean withHeader)527 public String[] getSQLForTextSource(boolean withHeader) { 528 529 // readonly for TEXT tables only 530 if (isText()) { 531 HsqlArrayList list = new HsqlArrayList(); 532 533 if (isReadOnly) { 534 list.add(getSQLForReadOnly()); 535 } 536 537 // data source 538 String dataSource = ((TextTable) this).getDataSourceDDL(); 539 540 if (dataSource != null) { 541 list.add(dataSource); 542 } 543 544 // header 545 String header = ((TextTable) this).getDataSourceHeader(); 546 547 if (withHeader && header != null && !isReadOnly) { 548 list.add(header); 549 } 550 551 String[] array = new String[list.size()]; 552 553 list.toArray(array); 554 555 return array; 556 } else { 557 return null; 558 } 559 } 560 getSQLForClustered()561 public String getSQLForClustered() { 562 563 if (!isCached() && !isText()) { 564 return null; 565 } 566 567 Index index = getClusteredIndex(); 568 569 if (index == null) { 570 return null; 571 } 572 573 String colList = getColumnListSQL(index.getColumns(), 574 index.getColumnCount()); 575 StringBuffer sb = new StringBuffer(64); 576 577 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(' '); 578 sb.append(getName().getSchemaQualifiedStatementName()); 579 sb.append(' ').append(Tokens.T_CLUSTERED).append(' '); 580 sb.append(Tokens.T_ON).append(' ').append(colList); 581 582 return sb.toString(); 583 } 584 getSQLForTableSpace()585 public String getSQLForTableSpace() { 586 587 if (!isCached() || tableSpace == DataSpaceManager.tableIdDefault) { 588 return null; 589 } 590 591 StringBuffer sb = new StringBuffer(64); 592 593 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(' '); 594 sb.append(getName().getSchemaQualifiedStatementName()); 595 sb.append(' ').append(Tokens.T_SPACE).append(' ').append(tableSpace); 596 597 return sb.toString(); 598 } 599 getTriggerSQL()600 public String[] getTriggerSQL() { 601 602 HsqlArrayList list = new HsqlArrayList(); 603 604 for (int i = 0; i < triggerList.length; i++) { 605 if (!triggerList[i].isSystem()) { 606 list.add(triggerList[i].getSQL()); 607 } 608 } 609 610 String[] array = new String[list.size()]; 611 612 list.toArray(array); 613 614 return array; 615 } 616 getIndexRootsSQL(long[] roots)617 public String getIndexRootsSQL(long[] roots) { 618 619 StringBuffer sb = new StringBuffer(128); 620 621 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(' '); 622 sb.append(getName().getSchemaQualifiedStatementName()); 623 sb.append(' ').append(Tokens.T_INDEX).append(' ').append('\''); 624 sb.append(StringUtil.getList(roots, " ", "")); 625 sb.append(' '); 626 sb.append(StringUtil.getList(new long[indexList.length], " ", "")); 627 sb.append(' ').append(store.elementCount()); 628 sb.append('\''); 629 630 return sb.toString(); 631 } 632 getColumnListSQL(int[] col, int len)633 public String getColumnListSQL(int[] col, int len) { 634 635 StringBuffer sb = new StringBuffer(); 636 637 sb.append('('); 638 639 for (int i = 0; i < len; i++) { 640 sb.append(getColumn(col[i]).getName().statementName); 641 642 if (i < len - 1) { 643 sb.append(','); 644 } 645 } 646 647 sb.append(')'); 648 649 return sb.toString(); 650 } 651 getColumnListWithTypeSQL()652 public String getColumnListWithTypeSQL() { 653 654 StringBuffer sb = new StringBuffer(); 655 656 sb.append('('); 657 658 for (int j = 0; j < columnCount; j++) { 659 ColumnSchema column = getColumn(j); 660 String colname = column.getName().statementName; 661 Type type = column.getDataType(); 662 663 if (j > 0) { 664 sb.append(','); 665 } 666 667 sb.append(colname); 668 sb.append(' '); 669 sb.append(type.getTypeDefinition()); 670 } 671 672 sb.append(')'); 673 674 return sb.toString(); 675 } 676 isConnected()677 public boolean isConnected() { 678 return true; 679 } 680 681 /** 682 * compares two full table rows based on a set of columns 683 * 684 * @param a a full row 685 * @param b a full row 686 * @param cols array of column indexes to compare 687 * @param coltypes array of column types for the full row 688 * 689 * @return comparison result, -1,0,+1 690 */ compareRows(Session session, Object[] a, Object[] b, int[] cols, Type[] coltypes)691 public static int compareRows(Session session, Object[] a, Object[] b, 692 int[] cols, Type[] coltypes) { 693 694 int fieldcount = cols.length; 695 696 for (int j = 0; j < fieldcount; j++) { 697 int i = coltypes[cols[j]].compare(session, a[cols[j]], b[cols[j]]); 698 699 if (i != 0) { 700 return i; 701 } 702 } 703 704 return 0; 705 } 706 707 /** 708 * Used to create row id's 709 */ getId()710 public int getId() { 711 return tableName.hashCode(); 712 } 713 getTableTypeString()714 public String getTableTypeString() { 715 716 switch (tableType) { 717 718 case TableBase.MEMORY_TABLE : 719 return Tokens.T_MEMORY; 720 721 case TableBase.CACHED_TABLE : 722 return Tokens.T_CACHED; 723 724 case TableBase.TEXT_TABLE : 725 return Tokens.T_TEXT; 726 727 case TableBase.FUNCTION_TABLE : 728 return Tokens.T_FUNCTION; 729 730 case TableBase.INFO_SCHEMA_TABLE : 731 case TableBase.VIEW_TABLE : 732 return Tokens.T_VIEW; 733 734 case TableBase.TEMP_TABLE : 735 return Tokens.T_TEMP; 736 737 case TableBase.SYSTEM_SUBQUERY : 738 default : 739 return "SUBQUERY"; 740 } 741 } 742 isSchemaBaseTable()743 public final boolean isSchemaBaseTable() { 744 745 switch (tableType) { 746 747 case TableBase.MEMORY_TABLE : 748 case TableBase.CACHED_TABLE : 749 case TableBase.TEXT_TABLE : 750 return true; 751 752 default : 753 return false; 754 } 755 } 756 isWithDataSource()757 public final boolean isWithDataSource() { 758 return isWithDataSource; 759 } 760 isText()761 public final boolean isText() { 762 return isText; 763 } 764 isTemp()765 public final boolean isTemp() { 766 return isTemp; 767 } 768 isReadOnly()769 public final boolean isReadOnly() { 770 return isReadOnly; 771 } 772 isView()773 public final boolean isView() { 774 return isView; 775 } 776 isQueryBased()777 public boolean isQueryBased() { 778 return false; 779 } 780 isCached()781 public boolean isCached() { 782 return isCached; 783 } 784 isDataReadOnly()785 public boolean isDataReadOnly() { 786 return isReadOnly; 787 } 788 isDropped()789 public boolean isDropped() { 790 return isDropped; 791 } 792 793 /** 794 * returns false if the table has to be recreated in order to add / drop 795 * indexes. Only CACHED tables return false. 796 */ isIndexingMutable()797 final boolean isIndexingMutable() { 798 return !isCached; 799 } 800 801 /** 802 * Used by INSERT, DELETE, UPDATE operations 803 */ checkDataReadOnly()804 public void checkDataReadOnly() { 805 806 if (isDataReadOnly()) { 807 throw Error.error(ErrorCode.DATA_IS_READONLY); 808 } 809 } 810 811 // ---------------------------------------------------------------------------- 812 // akede@users - 1.7.2 patch Files readonly setDataReadOnly(boolean value)813 public void setDataReadOnly(boolean value) { 814 815 // Changing the Read-Only mode for the table is only allowed if the 816 // the database can realize it. 817 if (!value) { 818 if (database.isFilesReadOnly() && isFileBased()) { 819 throw Error.error(ErrorCode.DATA_IS_READONLY); 820 } else if (database.getType() == DatabaseType.DB_MEM && isText) { 821 throw Error.error(ErrorCode.DATA_IS_READONLY); 822 } 823 } 824 825 isReadOnly = value; 826 } 827 828 /** 829 * Text or Cached Tables are normally file based 830 */ isFileBased()831 public boolean isFileBased() { 832 return isCached || isText; 833 } 834 835 /** 836 * Adds a constraint. 837 */ addConstraint(Constraint c)838 public void addConstraint(Constraint c) { 839 840 int index = c.getConstraintType() 841 == SchemaObject.ConstraintTypes.PRIMARY_KEY ? 0 842 : constraintList 843 .length; 844 845 constraintList = 846 (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, c, index, 847 1); 848 849 updateConstraintLists(); 850 } 851 updateConstraintLists()852 void updateConstraintLists() { 853 854 int fkCount = 0; 855 int mainCount = 0; 856 int checkCount = 0; 857 858 referentialActions = 0; 859 cascadingDeletes = 0; 860 861 for (int i = 0; i < constraintList.length; i++) { 862 switch (constraintList[i].getConstraintType()) { 863 864 case SchemaObject.ConstraintTypes.FOREIGN_KEY : 865 fkCount++; 866 break; 867 868 case SchemaObject.ConstraintTypes.MAIN : 869 mainCount++; 870 break; 871 872 case SchemaObject.ConstraintTypes.CHECK : 873 if (constraintList[i].isNotNull()) { 874 break; 875 } 876 877 checkCount++; 878 break; 879 } 880 } 881 882 fkConstraints = fkCount == 0 ? Constraint.emptyArray 883 : new Constraint[fkCount]; 884 fkCount = 0; 885 fkMainConstraints = mainCount == 0 ? Constraint.emptyArray 886 : new Constraint[mainCount]; 887 mainCount = 0; 888 checkConstraints = checkCount == 0 ? Constraint.emptyArray 889 : new Constraint[checkCount]; 890 checkCount = 0; 891 colRefFK = new boolean[columnCount]; 892 colMainFK = new boolean[columnCount]; 893 894 for (int i = 0; i < constraintList.length; i++) { 895 switch (constraintList[i].getConstraintType()) { 896 897 case SchemaObject.ConstraintTypes.FOREIGN_KEY : 898 fkConstraints[fkCount] = constraintList[i]; 899 900 ArrayUtil.intIndexesToBooleanArray( 901 constraintList[i].getRefColumns(), colRefFK); 902 903 fkCount++; 904 break; 905 906 case SchemaObject.ConstraintTypes.MAIN : 907 fkMainConstraints[mainCount] = constraintList[i]; 908 909 ArrayUtil.intIndexesToBooleanArray( 910 constraintList[i].getMainColumns(), colMainFK); 911 912 if (constraintList[i].hasCoreTriggeredAction()) { 913 referentialActions++; 914 915 if (constraintList[i].getDeleteAction() 916 == SchemaObject.ReferentialAction.CASCADE) { 917 cascadingDeletes++; 918 } 919 } 920 921 mainCount++; 922 break; 923 924 case SchemaObject.ConstraintTypes.CHECK : 925 if (constraintList[i].isNotNull()) { 926 break; 927 } 928 929 checkConstraints[checkCount] = constraintList[i]; 930 931 checkCount++; 932 break; 933 } 934 } 935 } 936 verifyConstraintsIntegrity()937 void verifyConstraintsIntegrity() { 938 939 for (int i = 0; i < constraintList.length; i++) { 940 Constraint c = constraintList[i]; 941 942 if (c.getConstraintType() == SchemaObject.ConstraintTypes 943 .FOREIGN_KEY || c.getConstraintType() == SchemaObject 944 .ConstraintTypes.MAIN) { 945 if (c.getMain() 946 != database.schemaManager.findUserTable( 947 c.getMain().getName().name, 948 c.getMain().getName().schema.name)) { 949 throw Error.runtimeError(ErrorCode.U_S0500, 950 "FK mismatch : " 951 + c.getName().name); 952 } 953 954 if (c.getRef() 955 != database.schemaManager.findUserTable( 956 c.getRef().getName().name, 957 c.getRef().getName().schema.name)) { 958 throw Error.runtimeError(ErrorCode.U_S0500, 959 "FK mismatch : " 960 + c.getName().name); 961 } 962 } 963 } 964 } 965 966 /** 967 * Returns the list of constraints. 968 */ getConstraints()969 public Constraint[] getConstraints() { 970 return constraintList; 971 } 972 973 /** 974 * Returns the list of FK constraints. 975 */ getFKConstraints()976 public Constraint[] getFKConstraints() { 977 return fkConstraints; 978 } 979 980 /** 981 * Returns the primary constraint. 982 */ getPrimaryConstraint()983 public Constraint getPrimaryConstraint() { 984 return hasPrimaryKey() ? constraintList[0] 985 : null; 986 } 987 988 /** columnMap is null for deletes */ collectFKReadLocks(int[] columnMap, OrderedHashSet set)989 void collectFKReadLocks(int[] columnMap, OrderedHashSet set) { 990 991 for (int i = 0; i < fkMainConstraints.length; i++) { 992 Constraint constraint = fkMainConstraints[i]; 993 Table ref = constraint.getRef(); 994 int[] mainColumns = constraint.getMainColumns(); 995 996 if (ref == this) { 997 continue; 998 } 999 1000 if (columnMap == null) { 1001 if (constraint.core.hasDeleteAction) { 1002 int[] cols = 1003 constraint.getDeleteAction() 1004 == SchemaObject.ReferentialAction.CASCADE ? null 1005 : constraint 1006 .getRefColumns(); 1007 1008 if (set.add(ref.getName())) { 1009 ref.collectFKReadLocks(cols, set); 1010 } 1011 } 1012 } else if (ArrayUtil.haveCommonElement(columnMap, mainColumns)) { 1013 if (set.add(ref.getName())) { 1014 ref.collectFKReadLocks(constraint.getRefColumns(), set); 1015 } 1016 } 1017 } 1018 } 1019 1020 /** columnMap is null for deletes */ collectFKWriteLocks(int[] columnMap, OrderedHashSet set)1021 void collectFKWriteLocks(int[] columnMap, OrderedHashSet set) { 1022 1023 for (int i = 0; i < fkMainConstraints.length; i++) { 1024 Constraint constraint = fkMainConstraints[i]; 1025 Table ref = constraint.getRef(); 1026 int[] mainColumns = constraint.getMainColumns(); 1027 1028 if (ref == this) { 1029 continue; 1030 } 1031 1032 if (columnMap == null) { 1033 if (constraint.core.hasDeleteAction) { 1034 int[] cols = 1035 constraint.getDeleteAction() 1036 == SchemaObject.ReferentialAction.CASCADE ? null 1037 : constraint 1038 .getRefColumns(); 1039 1040 if (set.add(ref.getName())) { 1041 ref.collectFKWriteLocks(cols, set); 1042 } 1043 } 1044 } else if (ArrayUtil.haveCommonElement(columnMap, mainColumns)) { 1045 if (constraint.core.hasUpdateAction) { 1046 if (set.add(ref.getName())) { 1047 ref.collectFKWriteLocks(constraint.getRefColumns(), 1048 set); 1049 } 1050 } 1051 } 1052 } 1053 } 1054 getNotNullConstraintForColumn(int colIndex)1055 Constraint getNotNullConstraintForColumn(int colIndex) { 1056 1057 for (int i = 0, size = constraintList.length; i < size; i++) { 1058 Constraint c = constraintList[i]; 1059 1060 if (c.isNotNull() && c.notNullColumnIndex == colIndex) { 1061 return c; 1062 } 1063 } 1064 1065 return null; 1066 } 1067 1068 /** 1069 * Returns the UNIQUE or PK constraint with the given column signature. 1070 */ getUniqueConstraintForColumns(int[] cols)1071 Constraint getUniqueConstraintForColumns(int[] cols) { 1072 1073 for (int i = 0, size = constraintList.length; i < size; i++) { 1074 Constraint c = constraintList[i]; 1075 1076 if (c.isUniqueWithColumns(cols)) { 1077 return c; 1078 } 1079 } 1080 1081 return null; 1082 } 1083 1084 /** 1085 * Returns any foreign key constraint equivalent to the column sets 1086 */ getFKConstraintForColumns(Table tableMain, int[] mainCols, int[] refCols)1087 Constraint getFKConstraintForColumns(Table tableMain, int[] mainCols, 1088 int[] refCols) { 1089 1090 for (int i = 0, size = constraintList.length; i < size; i++) { 1091 Constraint c = constraintList[i]; 1092 1093 if (c.isEquivalent(tableMain, mainCols, this, refCols)) { 1094 return c; 1095 } 1096 } 1097 1098 return null; 1099 } 1100 1101 /** 1102 * Returns any unique Constraint using this index 1103 * 1104 * @param index 1105 */ getUniqueOrPKConstraintForIndex(Index index)1106 public Constraint getUniqueOrPKConstraintForIndex(Index index) { 1107 1108 for (int i = 0, size = constraintList.length; i < size; i++) { 1109 Constraint c = constraintList[i]; 1110 1111 if (c.getMainIndex() == index && (c 1112 .getConstraintType() == SchemaObject.ConstraintTypes 1113 .UNIQUE || c.getConstraintType() == SchemaObject 1114 .ConstraintTypes.PRIMARY_KEY)) { 1115 return c; 1116 } 1117 } 1118 1119 return null; 1120 } 1121 1122 /** 1123 * Returns the next constraint of a given type 1124 * 1125 * @param from 1126 * @param type 1127 */ getNextConstraintIndex(int from, int type)1128 int getNextConstraintIndex(int from, int type) { 1129 1130 for (int i = from, size = constraintList.length; i < size; i++) { 1131 Constraint c = constraintList[i]; 1132 1133 if (c.getConstraintType() == type) { 1134 return i; 1135 } 1136 } 1137 1138 return -1; 1139 } 1140 1141 /** 1142 * Performs the table level checks and adds a column to the table at the 1143 * DDL level. Only used at table creation, not at alter column. 1144 */ addColumn(ColumnSchema column)1145 public void addColumn(ColumnSchema column) { 1146 1147 String name = column.getName().name; 1148 1149 if (findColumn(name) >= 0) { 1150 throw Error.error(ErrorCode.X_42504, name); 1151 } 1152 1153 if (column.isIdentity()) { 1154 if (identityColumn != -1) { 1155 throw Error.error(ErrorCode.X_42525, name); 1156 } 1157 1158 identityColumn = columnCount; 1159 identitySequence = column.getIdentitySequence(); 1160 } 1161 1162 addColumnNoCheck(column); 1163 } 1164 addColumnNoCheck(ColumnSchema column)1165 public void addColumnNoCheck(ColumnSchema column) { 1166 1167 columnList.add(column.getName().name, column); 1168 1169 columnCount++; 1170 } 1171 hasGeneratedColumn()1172 public boolean hasGeneratedColumn() { 1173 return hasGeneratedValues; 1174 } 1175 hasUpdatedColumn(int[] colMap)1176 public boolean hasUpdatedColumn(int[] colMap) { 1177 return hasUpdatedValues 1178 && !ArrayUtil.isAnyIntIndexInBooleanArray(colMap, colUpdated); 1179 } 1180 hasLobColumn()1181 public boolean hasLobColumn() { 1182 return hasLobColumn; 1183 } 1184 hasIdentityColumn()1185 public boolean hasIdentityColumn() { 1186 return identityColumn != -1; 1187 } 1188 getNextIdentity()1189 public long getNextIdentity() { 1190 return identitySequence.peek(); 1191 } 1192 1193 /** 1194 * Match two valid, equal length, columns arrays for type of columns for 1195 * referential constraints 1196 * 1197 * @param col column array from this Table 1198 * @param other the other Table object 1199 * @param othercol column array from the other Table 1200 */ checkReferentialColumnsMatch(int[] col, Table other, int[] othercol)1201 void checkReferentialColumnsMatch(int[] col, Table other, int[] othercol) { 1202 1203 for (int i = 0; i < col.length; i++) { 1204 Type type = colTypes[col[i]]; 1205 Type otherType = other.colTypes[othercol[i]]; 1206 1207 if (!type.canCompareDirect(otherType)) { 1208 throw Error.error(ErrorCode.X_42562); 1209 } 1210 } 1211 } 1212 1213 /** 1214 * For removal or addition of columns, constraints and indexes 1215 * 1216 * HsqlName objects are used from the old tables but no object is reused. 1217 * 1218 * Does not work in this form for FK's as Constraint.ConstraintCore 1219 * is not transferred to a referencing or referenced table 1220 */ moveDefinition(Session session, int newType, ColumnSchema column, Constraint constraint, Index index, int colIndex, int adjust, OrderedHashSet dropConstraints, OrderedHashSet dropIndexes)1221 Table moveDefinition(Session session, int newType, ColumnSchema column, 1222 Constraint constraint, Index index, int colIndex, 1223 int adjust, OrderedHashSet dropConstraints, 1224 OrderedHashSet dropIndexes) { 1225 1226 boolean newPK = false; 1227 1228 if (constraint != null 1229 && constraint.getConstraintType() 1230 == SchemaObject.ConstraintTypes.PRIMARY_KEY) { 1231 newPK = true; 1232 } 1233 1234 Table tn; 1235 1236 if (isText) { 1237 tn = new TextTable(database, tableName, newType); 1238 ((TextTable) tn).dataSource = ((TextTable) this).dataSource; 1239 ((TextTable) tn).isReversed = ((TextTable) this).isReversed; 1240 ((TextTable) tn).isConnected = ((TextTable) this).isConnected; 1241 } else { 1242 tn = new Table(database, tableName, newType); 1243 } 1244 1245 if (tableType == TEMP_TABLE) { 1246 tn.persistenceScope = persistenceScope; 1247 } 1248 1249 tn.tableSpace = tableSpace; 1250 1251 for (int i = 0; i < columnCount; i++) { 1252 ColumnSchema col = (ColumnSchema) columnList.get(i); 1253 1254 if (i == colIndex) { 1255 if (column != null) { 1256 tn.addColumn(column); 1257 } 1258 1259 if (adjust <= 0) { 1260 continue; 1261 } 1262 } 1263 1264 col = col.duplicate(); 1265 1266 col.setPrimaryKey(false); 1267 tn.addColumn(col); 1268 } 1269 1270 if (columnCount == colIndex) { 1271 tn.addColumn(column); 1272 } 1273 1274 int[] pkCols = null; 1275 1276 if (hasPrimaryKey() 1277 && !dropConstraints.contains( 1278 getPrimaryConstraint().getName())) { 1279 pkCols = getPrimaryKey(); 1280 pkCols = ArrayUtil.toAdjustedColumnArray(pkCols, colIndex, adjust); 1281 } else if (newPK) { 1282 pkCols = constraint.getMainColumns(); 1283 } 1284 1285 tn.createPrimaryKey(getIndex(0).getName(), pkCols, false); 1286 1287 for (int i = 1; i < indexList.length; i++) { 1288 Index idx = indexList[i]; 1289 1290 if (dropIndexes.contains(idx.getName())) { 1291 continue; 1292 } 1293 1294 int[] colarr = ArrayUtil.toAdjustedColumnArray(idx.getColumns(), 1295 colIndex, adjust); 1296 Index newIdx = tn.createIndexStructure(idx.getName(), colarr, 1297 idx.getColumnDesc(), null, 1298 idx.isUnique(), 1299 idx.isConstraint(), 1300 idx.isForward()); 1301 1302 newIdx.setClustered(idx.isClustered()); 1303 tn.addIndexStructure(newIdx); 1304 } 1305 1306 if (index != null) { 1307 index.setTable(tn); 1308 tn.addIndexStructure(index); 1309 } 1310 1311 HsqlArrayList newList = new HsqlArrayList(); 1312 1313 if (newPK) { 1314 constraint.core.mainIndex = tn.indexList[0]; 1315 constraint.core.mainTable = tn; 1316 constraint.core.mainTableName = tn.tableName; 1317 1318 newList.add(constraint); 1319 } 1320 1321 for (int i = 0; i < constraintList.length; i++) { 1322 Constraint c = constraintList[i]; 1323 1324 if (dropConstraints.contains(c.getName())) { 1325 continue; 1326 } 1327 1328 c = c.duplicate(); 1329 1330 c.updateTable(session, this, tn, colIndex, adjust); 1331 newList.add(c); 1332 } 1333 1334 if (!newPK && constraint != null) { 1335 constraint.updateTable(session, this, tn, -1, 0); 1336 newList.add(constraint); 1337 } 1338 1339 tn.constraintList = new Constraint[newList.size()]; 1340 1341 newList.toArray(tn.constraintList); 1342 tn.updateConstraintLists(); 1343 tn.setBestRowIdentifiers(); 1344 1345 tn.triggerList = triggerList; 1346 tn.triggerLists = triggerLists; 1347 1348 for (int i = 0; i < tn.constraintList.length; i++) { 1349 tn.constraintList[i].compile(session, tn); 1350 } 1351 1352 for (int i = 0; i < tn.columnCount; i++) { 1353 tn.getColumn(i).compile(session, tn); 1354 } 1355 1356 return tn; 1357 } 1358 1359 /** 1360 * Used for drop / retype column. 1361 */ checkColumnInCheckConstraint(int colIndex)1362 void checkColumnInCheckConstraint(int colIndex) { 1363 1364 for (int i = 0, size = constraintList.length; i < size; i++) { 1365 Constraint c = constraintList[i]; 1366 1367 if (c.getConstraintType() == SchemaObject.ConstraintTypes.CHECK 1368 && !c.isNotNull() && c.hasColumn(colIndex)) { 1369 HsqlName name = c.getName(); 1370 1371 throw Error.error(ErrorCode.X_42502, 1372 name.getSchemaQualifiedStatementName()); 1373 } 1374 } 1375 } 1376 1377 /** 1378 * Used for retype column. Checks whether column is in an FK or is 1379 * referenced by a FK 1380 * @param colIndex index 1381 */ checkColumnInFKConstraint(int colIndex)1382 void checkColumnInFKConstraint(int colIndex) { 1383 1384 for (int i = 0, size = constraintList.length; i < size; i++) { 1385 Constraint c = constraintList[i]; 1386 1387 if (c.hasColumn(colIndex) && (c.getConstraintType() == SchemaObject 1388 .ConstraintTypes.MAIN || c 1389 .getConstraintType() == SchemaObject.ConstraintTypes 1390 .FOREIGN_KEY)) { 1391 HsqlName name = c.getName(); 1392 1393 throw Error.error(ErrorCode.X_42533, 1394 name.getSchemaQualifiedStatementName()); 1395 } 1396 } 1397 } 1398 1399 /** 1400 * Returns list of constraints dependent only on one column 1401 */ getDependentConstraints(int colIndex)1402 OrderedHashSet getDependentConstraints(int colIndex) { 1403 1404 OrderedHashSet set = new OrderedHashSet(); 1405 1406 for (int i = 0, size = constraintList.length; i < size; i++) { 1407 Constraint c = constraintList[i]; 1408 1409 if (c.hasColumnOnly(colIndex)) { 1410 set.add(c); 1411 } 1412 } 1413 1414 return set; 1415 } 1416 1417 /** 1418 * Returns list of constraints dependent on more than one column 1419 */ getContainingConstraints(int colIndex)1420 OrderedHashSet getContainingConstraints(int colIndex) { 1421 1422 OrderedHashSet set = new OrderedHashSet(); 1423 1424 for (int i = 0, size = constraintList.length; i < size; i++) { 1425 Constraint c = constraintList[i]; 1426 1427 if (c.hasColumnPlus(colIndex)) { 1428 set.add(c); 1429 } 1430 } 1431 1432 return set; 1433 } 1434 getContainingIndexNames(int colIndex)1435 OrderedHashSet getContainingIndexNames(int colIndex) { 1436 1437 OrderedHashSet set = new OrderedHashSet(); 1438 1439 for (int i = 0, size = indexList.length; i < size; i++) { 1440 Index index = indexList[i]; 1441 1442 if (ArrayUtil.find(index.getColumns(), colIndex) != -1) { 1443 set.add(index.getName()); 1444 } 1445 } 1446 1447 return set; 1448 } 1449 1450 /** 1451 * Returns list of MAIN constraints dependent on this PK or UNIQUE constraint 1452 */ getDependentConstraints(Constraint constraint)1453 OrderedHashSet getDependentConstraints(Constraint constraint) { 1454 1455 OrderedHashSet set = new OrderedHashSet(); 1456 1457 for (int i = 0, size = fkMainConstraints.length; i < size; i++) { 1458 Constraint c = fkMainConstraints[i]; 1459 1460 if (c.core.uniqueName == constraint.getName()) { 1461 set.add(c); 1462 } 1463 } 1464 1465 return set; 1466 } 1467 getDependentExternalConstraints()1468 public OrderedHashSet getDependentExternalConstraints() { 1469 1470 OrderedHashSet set = new OrderedHashSet(); 1471 1472 for (int i = 0, size = constraintList.length; i < size; i++) { 1473 Constraint c = constraintList[i]; 1474 1475 if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN 1476 || c.getConstraintType() 1477 == SchemaObject.ConstraintTypes.FOREIGN_KEY) { 1478 if (c.core.mainTable != c.core.refTable) { 1479 set.add(c); 1480 } 1481 } 1482 } 1483 1484 return set; 1485 } 1486 getUniquePKConstraintNames()1487 public OrderedHashSet getUniquePKConstraintNames() { 1488 1489 OrderedHashSet set = new OrderedHashSet(); 1490 1491 for (int i = 0, size = constraintList.length; i < size; i++) { 1492 Constraint c = constraintList[i]; 1493 1494 if (c.getConstraintType() == SchemaObject.ConstraintTypes.UNIQUE 1495 || c.getConstraintType() 1496 == SchemaObject.ConstraintTypes.PRIMARY_KEY) { 1497 set.add(c.getName()); 1498 } 1499 } 1500 1501 return set; 1502 } 1503 1504 /** 1505 * Used for column defaults and nullability. Checks whether column is in an 1506 * FK with a given referential action type. 1507 * 1508 * @param colIndex index of column 1509 * @param actionType referential action of the FK 1510 */ checkColumnInFKConstraint(int colIndex, int actionType)1511 void checkColumnInFKConstraint(int colIndex, int actionType) { 1512 1513 for (int i = 0, size = constraintList.length; i < size; i++) { 1514 Constraint c = constraintList[i]; 1515 1516 if (c.getConstraintType() == SchemaObject.ConstraintTypes 1517 .FOREIGN_KEY && c 1518 .hasColumn(colIndex) && (actionType == c 1519 .getUpdateAction() || actionType == c 1520 .getDeleteAction())) { 1521 HsqlName name = c.getName(); 1522 1523 throw Error.error(ErrorCode.X_42533, 1524 name.getSchemaQualifiedStatementName()); 1525 } 1526 } 1527 } 1528 1529 /** 1530 * Returns the identity column index. 1531 */ getIdentityColumnIndex()1532 int getIdentityColumnIndex() { 1533 return identityColumn; 1534 } 1535 1536 /** 1537 * Returns the index of given column name or throws if not found 1538 */ getColumnIndex(String name)1539 public int getColumnIndex(String name) { 1540 1541 int i = findColumn(name); 1542 1543 if (i == -1) { 1544 throw Error.error(ErrorCode.X_42501, name); 1545 } 1546 1547 return i; 1548 } 1549 1550 /** 1551 * Returns the index of given column name or -1 if not found. 1552 */ findColumn(String name)1553 public int findColumn(String name) { 1554 1555 int index = columnList.getIndex(name); 1556 1557 return index; 1558 } 1559 1560 /** 1561 * sets the flag for the presence of any default expression 1562 */ resetDefaultsFlag()1563 void resetDefaultsFlag() { 1564 1565 hasDefaultValues = false; 1566 hasGeneratedValues = false; 1567 hasUpdatedValues = false; 1568 hasNotNullColumns = false; 1569 hasDomainColumns = false; 1570 hasLobColumn = false; 1571 1572 for (int i = 0; i < columnCount; i++) { 1573 hasDefaultValues |= colDefaults[i] != null; 1574 hasGeneratedValues |= colGenerated[i]; 1575 hasUpdatedValues |= colUpdated[i]; 1576 hasNotNullColumns |= colNotNull[i]; 1577 1578 if (colTypes[i].isDomainType()) { 1579 hasDomainColumns = true; 1580 } 1581 1582 if (colTypes[i].isLobType()) { 1583 hasLobColumn = true; 1584 } 1585 } 1586 } 1587 getBestRowIdentifiers()1588 public int[] getBestRowIdentifiers() { 1589 return bestRowIdentifierCols; 1590 } 1591 isBestRowIdentifiersStrict()1592 public boolean isBestRowIdentifiersStrict() { 1593 return bestRowIdentifierStrict; 1594 } 1595 getClusteredIndex()1596 public Index getClusteredIndex() { 1597 1598 for (int i = 0; i < indexList.length; i++) { 1599 if (indexList[i].isClustered()) { 1600 return indexList[i]; 1601 } 1602 } 1603 1604 return null; 1605 } 1606 1607 /** 1608 * Finds an existing index for a column 1609 */ getIndexForColumn(Session session, int col)1610 synchronized Index getIndexForColumn(Session session, int col) { 1611 1612 int i = bestIndexForColumn[col]; 1613 1614 if (i > -1) { 1615 return indexList[i]; 1616 } 1617 1618 switch (tableType) { 1619 1620 case TableBase.FUNCTION_TABLE : 1621 case TableBase.SYSTEM_SUBQUERY : 1622 case TableBase.INFO_SCHEMA_TABLE : 1623 case TableBase.VIEW_TABLE : 1624 case TableBase.TEMP_TABLE : { 1625 Index index = createIndexForColumns(session, new int[]{ col }); 1626 1627 return index; 1628 } 1629 } 1630 1631 return null; 1632 } 1633 isIndexed(int colIndex)1634 boolean isIndexed(int colIndex) { 1635 return bestIndexForColumn[colIndex] != -1; 1636 } 1637 getUniqueNotNullColumnGroup(boolean[] usedColumns)1638 int[] getUniqueNotNullColumnGroup(boolean[] usedColumns) { 1639 1640 for (int i = 0, count = constraintList.length; i < count; i++) { 1641 Constraint constraint = constraintList[i]; 1642 1643 if (constraint.getConstraintType() 1644 == SchemaObject.ConstraintTypes.UNIQUE) { 1645 int[] indexCols = constraint.getMainColumns(); 1646 1647 if (ArrayUtil.areAllIntIndexesInBooleanArray( 1648 indexCols, colNotNull) && ArrayUtil 1649 .areAllIntIndexesInBooleanArray( 1650 indexCols, usedColumns)) { 1651 return indexCols; 1652 } 1653 } else if (constraint.getConstraintType() 1654 == SchemaObject.ConstraintTypes.PRIMARY_KEY) { 1655 int[] indexCols = constraint.getMainColumns(); 1656 1657 if (ArrayUtil.areAllIntIndexesInBooleanArray(indexCols, 1658 usedColumns)) { 1659 return indexCols; 1660 } 1661 } 1662 } 1663 1664 return null; 1665 } 1666 areColumnsNotNull(int[] indexes)1667 boolean areColumnsNotNull(int[] indexes) { 1668 return ArrayUtil.areAllIntIndexesInBooleanArray(indexes, colNotNull); 1669 } 1670 1671 /** 1672 * Shortcut for creating default PK's. 1673 */ createPrimaryKey()1674 public void createPrimaryKey() { 1675 createPrimaryKey(null, ValuePool.emptyIntArray, false); 1676 } 1677 1678 /** 1679 * Creates a single or multi-column primary key and index. sets the 1680 * colTypes array. Finalises the creation of the table. (fredt@users) 1681 */ createPrimaryKey(HsqlName indexName, int[] columns, boolean columnsNotNull)1682 public void createPrimaryKey(HsqlName indexName, int[] columns, 1683 boolean columnsNotNull) { 1684 1685 if (columns == null) { 1686 columns = ValuePool.emptyIntArray; 1687 } 1688 1689 for (int i = 0; i < columns.length; i++) { 1690 getColumn(columns[i]).setPrimaryKey(true); 1691 } 1692 1693 setColumnStructures(); 1694 1695 Type[] primaryKeyTypes = new Type[columns.length]; 1696 1697 ArrayUtil.projectRow(colTypes, columns, primaryKeyTypes); 1698 1699 HsqlName name = indexName; 1700 1701 if (name == null) { 1702 name = database.nameManager.newAutoName("IDX", getSchemaName(), 1703 getName(), SchemaObject.INDEX); 1704 } 1705 1706 createPrimaryIndex(columns, primaryKeyTypes, name); 1707 setBestRowIdentifiers(); 1708 } 1709 createPrimaryKeyConstraint(HsqlName indexName, int[] columns, boolean columnsNotNull)1710 public void createPrimaryKeyConstraint(HsqlName indexName, int[] columns, 1711 boolean columnsNotNull) { 1712 1713 createPrimaryKey(indexName, columns, columnsNotNull); 1714 1715 Constraint c = 1716 new Constraint(indexName, this, getPrimaryIndex(), 1717 SchemaObject.ConstraintTypes.PRIMARY_KEY); 1718 1719 addConstraint(c); 1720 } 1721 setColumnStructures()1722 void setColumnStructures() { 1723 1724 if (colTypes == null) { 1725 colTypes = new Type[columnCount]; 1726 } 1727 1728 colDefaults = new Expression[columnCount]; 1729 colNotNull = new boolean[columnCount]; 1730 colGenerated = new boolean[columnCount]; 1731 colUpdated = new boolean[columnCount]; 1732 defaultColumnMap = new int[columnCount]; 1733 1734 for (int i = 0; i < columnCount; i++) { 1735 setSingleColumnTypeVars(i); 1736 } 1737 1738 resetDefaultsFlag(); 1739 } 1740 setColumnTypeVars(int i)1741 void setColumnTypeVars(int i) { 1742 setSingleColumnTypeVars(i); 1743 resetDefaultsFlag(); 1744 } 1745 setSingleColumnTypeVars(int i)1746 private void setSingleColumnTypeVars(int i) { 1747 1748 ColumnSchema column = getColumn(i); 1749 Type dataType = column.getDataType(); 1750 1751 colTypes[i] = dataType; 1752 colNotNull[i] = column.isPrimaryKey() || !column.isNullable(); 1753 defaultColumnMap[i] = i; 1754 1755 if (column.isIdentity()) { 1756 identitySequence = column.getIdentitySequence(); 1757 identityColumn = i; 1758 } else if (identityColumn == i) { 1759 identitySequence = null; 1760 identityColumn = -1; 1761 } 1762 1763 colDefaults[i] = column.getDefaultExpression(); 1764 colGenerated[i] = column.isGenerated(); 1765 colUpdated[i] = column.isAutoUpdate(); 1766 } 1767 1768 /** 1769 * Returns direct mapping array. 1770 */ getColumnMap()1771 int[] getColumnMap() { 1772 return defaultColumnMap; 1773 } 1774 1775 /** 1776 * Returns empty mapping array. 1777 */ getNewColumnMap()1778 int[] getNewColumnMap() { 1779 return new int[columnCount]; 1780 } 1781 getColumnCheckList(int[] columnIndexes)1782 boolean[] getColumnCheckList(int[] columnIndexes) { 1783 1784 boolean[] columnCheckList = new boolean[columnCount]; 1785 1786 for (int i = 0; i < columnIndexes.length; i++) { 1787 int index = columnIndexes[i]; 1788 1789 if (index > -1) { 1790 columnCheckList[index] = true; 1791 } 1792 } 1793 1794 return columnCheckList; 1795 } 1796 getColumnIndexes(String[] list)1797 int[] getColumnIndexes(String[] list) { 1798 1799 int[] cols = new int[list.length]; 1800 1801 for (int i = 0; i < cols.length; i++) { 1802 cols[i] = getColumnIndex(list[i]); 1803 } 1804 1805 return cols; 1806 } 1807 getColumnIndexes(OrderedHashSet set)1808 int[] getColumnIndexes(OrderedHashSet set) { 1809 1810 int[] cols = new int[set.size()]; 1811 1812 for (int i = 0; i < cols.length; i++) { 1813 cols[i] = getColumnIndex((String) set.get(i)); 1814 1815 if (cols[i] == -1) { 1816 throw Error.error(ErrorCode.X_42501, (String) set.get(i)); 1817 } 1818 } 1819 1820 return cols; 1821 } 1822 getColumnIndexes(HashMappedList list)1823 int[] getColumnIndexes(HashMappedList list) { 1824 1825 int[] cols = new int[list.size()]; 1826 1827 for (int i = 0; i < cols.length; i++) { 1828 cols[i] = ((Integer) list.get(i)).intValue(); 1829 } 1830 1831 return cols; 1832 } 1833 1834 /** 1835 * Returns the Column object at the given index 1836 */ getColumn(int i)1837 public ColumnSchema getColumn(int i) { 1838 return (ColumnSchema) columnList.get(i); 1839 } 1840 getColumnNameSet(int[] columnIndexes)1841 public OrderedHashSet getColumnNameSet(int[] columnIndexes) { 1842 1843 OrderedHashSet set = new OrderedHashSet(); 1844 1845 for (int i = 0; i < columnIndexes.length; i++) { 1846 set.add(((ColumnSchema) columnList.get(i)).getName()); 1847 } 1848 1849 return set; 1850 } 1851 getColumnNameSet(boolean[] columnCheckList)1852 public OrderedHashSet getColumnNameSet(boolean[] columnCheckList) { 1853 1854 OrderedHashSet set = new OrderedHashSet(); 1855 1856 for (int i = 0; i < columnCheckList.length; i++) { 1857 if (columnCheckList[i]) { 1858 set.add(columnList.get(i)); 1859 } 1860 } 1861 1862 return set; 1863 } 1864 getColumnNames(boolean[] columnCheckList, Set set)1865 public void getColumnNames(boolean[] columnCheckList, Set set) { 1866 1867 for (int i = 0; i < columnCheckList.length; i++) { 1868 if (columnCheckList[i]) { 1869 set.add(((ColumnSchema) columnList.get(i)).getName()); 1870 } 1871 } 1872 } 1873 getColumnNameSet()1874 public OrderedHashSet getColumnNameSet() { 1875 1876 OrderedHashSet set = new OrderedHashSet(); 1877 1878 for (int i = 0; i < columnCount; i++) { 1879 set.add(((ColumnSchema) columnList.get(i)).getName()); 1880 } 1881 1882 return set; 1883 } 1884 1885 /** 1886 * Returns array for a new row with SQL DEFAULT value for each column n 1887 * where exists[n] is false. This provides default values only where 1888 * required and avoids evaluating these values where they will be 1889 * overwritten. 1890 */ getNewRowData(Session session)1891 public Object[] getNewRowData(Session session) { 1892 1893 Object[] data = new Object[columnCount]; 1894 int i; 1895 1896 if (hasDefaultValues) { 1897 for (i = 0; i < columnCount; i++) { 1898 Expression def = colDefaults[i]; 1899 1900 if (def != null) { 1901 data[i] = def.getValue(session, colTypes[i]); 1902 } 1903 } 1904 } 1905 1906 return data; 1907 } 1908 hasTrigger(int trigVecIndex)1909 boolean hasTrigger(int trigVecIndex) { 1910 return triggerLists[trigVecIndex].length != 0; 1911 } 1912 1913 /** 1914 * Adds a trigger. 1915 */ addTrigger(TriggerDef td, HsqlName otherName)1916 void addTrigger(TriggerDef td, HsqlName otherName) { 1917 1918 int index = triggerList.length; 1919 1920 if (otherName != null) { 1921 int pos = getTriggerIndex(otherName.name); 1922 1923 if (pos != -1) { 1924 index = pos + 1; 1925 } 1926 } 1927 1928 triggerList = (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList, 1929 td, index, 1); 1930 1931 TriggerDef[] list = triggerLists[td.triggerType]; 1932 1933 index = list.length; 1934 1935 if (otherName != null) { 1936 for (int i = 0; i < list.length; i++) { 1937 TriggerDef trigger = list[i]; 1938 1939 if (trigger.getName().name.equals(otherName.name)) { 1940 index = i + 1; 1941 1942 break; 1943 } 1944 } 1945 } 1946 1947 list = (TriggerDef[]) ArrayUtil.toAdjustedArray(list, td, index, 1); 1948 triggerLists[td.triggerType] = list; 1949 } 1950 1951 /** 1952 * Returns a trigger. 1953 */ getTrigger(String name)1954 TriggerDef getTrigger(String name) { 1955 1956 for (int i = triggerList.length - 1; i >= 0; i--) { 1957 if (triggerList[i].getName().name.equals(name)) { 1958 return triggerList[i]; 1959 } 1960 } 1961 1962 return null; 1963 } 1964 getTriggerIndex(String name)1965 public int getTriggerIndex(String name) { 1966 1967 for (int i = 0; i < triggerList.length; i++) { 1968 if (triggerList[i].getName().name.equals(name)) { 1969 return i; 1970 } 1971 } 1972 1973 return -1; 1974 } 1975 1976 /** 1977 * Drops a trigger. 1978 */ removeTrigger(TriggerDef trigger)1979 void removeTrigger(TriggerDef trigger) { 1980 1981 TriggerDef td = null; 1982 1983 for (int i = 0; i < triggerList.length; i++) { 1984 td = triggerList[i]; 1985 1986 if (td.getName().name.equals(trigger.getName().name)) { 1987 td.terminate(); 1988 1989 triggerList = 1990 (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList, 1991 null, i, -1); 1992 1993 break; 1994 } 1995 } 1996 1997 if (td == null) { 1998 return; 1999 } 2000 2001 int index = td.triggerType; 2002 2003 // look in each trigger in list 2004 for (int j = 0; j < triggerLists[index].length; j++) { 2005 td = triggerLists[index][j]; 2006 2007 if (td.getName().name.equals(trigger.getName().name)) { 2008 triggerLists[index] = (TriggerDef[]) ArrayUtil.toAdjustedArray( 2009 triggerLists[index], null, j, -1); 2010 2011 break; 2012 } 2013 } 2014 } 2015 2016 /** 2017 * Used when dropping all triggers. 2018 */ releaseTriggers()2019 void releaseTriggers() { 2020 2021 // look in each trigger list of each type of trigger 2022 for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) { 2023 for (int j = 0; j < triggerLists[i].length; j++) { 2024 triggerLists[i][j].terminate(); 2025 } 2026 2027 triggerLists[i] = TriggerDef.emptyArray; 2028 } 2029 2030 triggerList = TriggerDef.emptyArray; 2031 } 2032 terminateTriggers()2033 void terminateTriggers() { 2034 2035 // look in each trigger list of each type of trigger 2036 for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) { 2037 for (int j = 0; j < triggerLists[i].length; j++) { 2038 triggerLists[i][j].terminate(); 2039 } 2040 } 2041 } 2042 2043 /** 2044 * Returns the index of the Index object of the given name or -1 if not found. 2045 */ getIndexIndex(String indexName)2046 int getIndexIndex(String indexName) { 2047 2048 Index[] indexes = indexList; 2049 2050 for (int i = 0; i < indexes.length; i++) { 2051 if (indexName.equals(indexes[i].getName().name)) { 2052 return i; 2053 } 2054 } 2055 2056 // no such index 2057 return -1; 2058 } 2059 2060 /** 2061 * Returns the Index object of the given name or null if not found. 2062 */ getIndex(String indexName)2063 Index getIndex(String indexName) { 2064 2065 Index[] indexes = indexList; 2066 int i = getIndexIndex(indexName); 2067 2068 return i == -1 ? null 2069 : indexes[i]; 2070 } 2071 2072 /** 2073 * Return the position of the constraint within the list 2074 */ getConstraintIndex(String constraintName)2075 int getConstraintIndex(String constraintName) { 2076 2077 for (int i = 0, size = constraintList.length; i < size; i++) { 2078 if (constraintList[i].getName().name.equals(constraintName)) { 2079 return i; 2080 } 2081 } 2082 2083 return -1; 2084 } 2085 2086 /** 2087 * return the named constraint 2088 */ getConstraint(String constraintName)2089 public Constraint getConstraint(String constraintName) { 2090 2091 int i = getConstraintIndex(constraintName); 2092 2093 return (i < 0) ? null 2094 : constraintList[i]; 2095 } 2096 2097 /** 2098 * Returns any unique Constraint using this index 2099 * 2100 * @param index 2101 */ getUniqueConstraintForIndex(Index index)2102 public Constraint getUniqueConstraintForIndex(Index index) { 2103 2104 for (int i = 0, size = constraintList.length; i < size; i++) { 2105 Constraint c = constraintList[i]; 2106 2107 if (c.getMainIndex() == index) { 2108 if (c.getConstraintType() == SchemaObject.ConstraintTypes 2109 .PRIMARY_KEY || c.getConstraintType() == SchemaObject 2110 .ConstraintTypes.UNIQUE) { 2111 return c; 2112 } 2113 } 2114 } 2115 2116 return null; 2117 } 2118 2119 /** 2120 * remove a named constraint 2121 */ removeConstraint(String name)2122 void removeConstraint(String name) { 2123 2124 int index = getConstraintIndex(name); 2125 2126 if (index != -1) { 2127 removeConstraint(index); 2128 } 2129 } 2130 removeConstraint(int index)2131 void removeConstraint(int index) { 2132 2133 constraintList = 2134 (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, null, 2135 index, -1); 2136 2137 updateConstraintLists(); 2138 } 2139 renameColumn(ColumnSchema column, String newName, boolean isquoted)2140 void renameColumn(ColumnSchema column, String newName, boolean isquoted) { 2141 2142 String oldname = column.getName().name; 2143 int i = getColumnIndex(oldname); 2144 2145 columnList.setKey(i, newName); 2146 column.getName().rename(newName, isquoted); 2147 } 2148 renameColumn(ColumnSchema column, HsqlName newName)2149 void renameColumn(ColumnSchema column, HsqlName newName) { 2150 2151 String oldname = column.getName().name; 2152 int i = getColumnIndex(oldname); 2153 2154 if (findColumn(newName.name) != -1) { 2155 throw Error.error(ErrorCode.X_42504); 2156 } 2157 2158 columnList.setKey(i, newName.name); 2159 column.getName().rename(newName); 2160 } 2161 getTriggers()2162 public TriggerDef[] getTriggers() { 2163 return triggerList; 2164 } 2165 isWritable()2166 public boolean isWritable() { 2167 return !isReadOnly && !database.databaseReadOnly 2168 && !(database.isFilesReadOnly() && (isCached || isText)); 2169 } 2170 isInsertable()2171 public boolean isInsertable() { 2172 return isWritable(); 2173 } 2174 isUpdatable()2175 public boolean isUpdatable() { 2176 return isWritable(); 2177 } 2178 isTriggerInsertable()2179 public boolean isTriggerInsertable() { 2180 return false; 2181 } 2182 isTriggerUpdatable()2183 public boolean isTriggerUpdatable() { 2184 return false; 2185 } 2186 isTriggerDeletable()2187 public boolean isTriggerDeletable() { 2188 return false; 2189 } 2190 getUpdatableColumns()2191 public int[] getUpdatableColumns() { 2192 return defaultColumnMap; 2193 } 2194 getBaseTable()2195 public Table getBaseTable() { 2196 return this; 2197 } 2198 getBaseTableColumnMap()2199 public int[] getBaseTableColumnMap() { 2200 return defaultColumnMap; 2201 } 2202 2203 /** 2204 * Used to create an index automatically for system and temp tables. 2205 * Used for internal operation tables with null Session param. 2206 */ createIndexForColumns(Session session, int[] columns)2207 Index createIndexForColumns(Session session, int[] columns) { 2208 2209 Index index = null; 2210 HsqlName indexName = database.nameManager.newAutoName("IDX_T", 2211 getSchemaName(), getName(), SchemaObject.INDEX); 2212 2213 try { 2214 index = createAndAddIndexStructure(session, indexName, columns, 2215 null, null, false, false, 2216 false); 2217 } catch (Throwable t) { 2218 return null; 2219 } 2220 2221 return index; 2222 } 2223 fireTriggers(Session session, int trigVecIndex, RowSetNavigatorDataChange rowSet)2224 void fireTriggers(Session session, int trigVecIndex, 2225 RowSetNavigatorDataChange rowSet) { 2226 2227 if (!database.isReferentialIntegrity()) { 2228 return; 2229 } 2230 2231 TriggerDef[] trigVec = triggerLists[trigVecIndex]; 2232 2233 for (int i = 0, size = trigVec.length; i < size; i++) { 2234 TriggerDef td = trigVec[i]; 2235 boolean sqlTrigger = td instanceof TriggerDefSQL; 2236 2237 if (td.hasOldTable()) { 2238 2239 // 2240 } 2241 2242 td.pushPair(session, null, null); 2243 } 2244 } 2245 fireTriggers(Session session, int trigVecIndex, RowSetNavigator rowSet)2246 void fireTriggers(Session session, int trigVecIndex, 2247 RowSetNavigator rowSet) { 2248 2249 if (!database.isReferentialIntegrity()) { 2250 return; 2251 } 2252 2253 TriggerDef[] trigVec = triggerLists[trigVecIndex]; 2254 2255 for (int i = 0, size = trigVec.length; i < size; i++) { 2256 TriggerDef td = trigVec[i]; 2257 boolean sqlTrigger = td instanceof TriggerDefSQL; 2258 2259 if (td.hasOldTable()) { 2260 2261 // 2262 } 2263 2264 td.pushPair(session, null, null); 2265 } 2266 } 2267 2268 /** 2269 * Fires all row-level triggers of the given set (trigger type) 2270 * 2271 */ fireTriggers(Session session, int trigVecIndex, Object[] oldData, Object[] newData, int[] cols)2272 void fireTriggers(Session session, int trigVecIndex, Object[] oldData, 2273 Object[] newData, int[] cols) { 2274 2275 if (!database.isReferentialIntegrity()) { 2276 return; 2277 } 2278 2279 TriggerDef[] trigVec = triggerLists[trigVecIndex]; 2280 2281 for (int i = 0, size = trigVec.length; i < size; i++) { 2282 TriggerDef td = trigVec[i]; 2283 boolean sqlTrigger = td instanceof TriggerDefSQL; 2284 2285 if (cols != null && td.getUpdateColumnIndexes() != null 2286 && !ArrayUtil.haveCommonElement( 2287 td.getUpdateColumnIndexes(), cols)) { 2288 continue; 2289 } 2290 2291 if (td.isForEachRow()) { 2292 switch (td.triggerType) { 2293 2294 case Trigger.INSERT_BEFORE_ROW : 2295 break; 2296 2297 case Trigger.INSERT_AFTER_ROW : 2298 if (!sqlTrigger) { 2299 newData = 2300 (Object[]) ArrayUtil.duplicateArray(newData); 2301 } 2302 break; 2303 2304 case Trigger.UPDATE_AFTER_ROW : 2305 if (!sqlTrigger) { 2306 oldData = 2307 (Object[]) ArrayUtil.duplicateArray(oldData); 2308 newData = 2309 (Object[]) ArrayUtil.duplicateArray(newData); 2310 } 2311 break; 2312 2313 case Trigger.UPDATE_BEFORE_ROW : 2314 case Trigger.DELETE_BEFORE_ROW : 2315 case Trigger.DELETE_AFTER_ROW : 2316 if (!sqlTrigger) { 2317 oldData = 2318 (Object[]) ArrayUtil.duplicateArray(oldData); 2319 } 2320 break; 2321 } 2322 2323 td.pushPair(session, oldData, newData); 2324 } else { 2325 td.pushPair(session, null, null); 2326 } 2327 } 2328 } 2329 2330 /** 2331 * Enforce max field sizes according to SQL column definition. 2332 * SQL92 13.8 2333 */ enforceRowConstraints(Session session, Object[] data)2334 public void enforceRowConstraints(Session session, Object[] data) { 2335 2336 for (int i = 0; i < columnCount; i++) { 2337 Type type = colTypes[i]; 2338 ColumnSchema column; 2339 2340 if (hasDomainColumns && type.isDomainType()) { 2341 Constraint[] constraints = 2342 type.userTypeModifier.getConstraints(); 2343 2344 column = getColumn(i); 2345 2346 for (int j = 0; j < constraints.length; j++) { 2347 constraints[j].checkCheckConstraint(session, this, column, 2348 data[i]); 2349 } 2350 } 2351 2352 if (colNotNull[i] && data[i] == null) { 2353 String constraintName; 2354 Constraint c = getNotNullConstraintForColumn(i); 2355 2356 if (c == null) { 2357 if (ArrayUtil.find(getPrimaryKey(), i) > -1) { 2358 c = getPrimaryConstraint(); 2359 } 2360 } 2361 2362 constraintName = c == null ? "" 2363 : c.getName().name; 2364 column = getColumn(i); 2365 2366 String[] info = new String[] { 2367 constraintName, tableName.statementName, 2368 column.getName().statementName 2369 }; 2370 2371 throw Error.error(null, ErrorCode.X_23502, 2372 ErrorCode.COLUMN_CONSTRAINT, info); 2373 } 2374 } 2375 } 2376 enforceTypeLimits(Session session, Object[] data)2377 public void enforceTypeLimits(Session session, Object[] data) { 2378 2379 int i = 0; 2380 2381 try { 2382 for (; i < columnCount; i++) { 2383 data[i] = colTypes[i].convertToTypeLimits(session, data[i]); 2384 } 2385 } catch (HsqlException e) { 2386 int code = e.getErrorCode(); 2387 2388 if (code == -ErrorCode.X_22001 || code == -ErrorCode.X_22003 2389 || code == -ErrorCode.X_22008) { 2390 ColumnSchema column = getColumn(i); 2391 String[] info = new String[] { 2392 "", tableName.statementName, column.getName().statementName 2393 }; 2394 2395 throw Error.error(e, code, ErrorCode.COLUMN_CONSTRAINT, info); 2396 } 2397 2398 throw e; 2399 } 2400 } 2401 indexTypeForColumn(Session session, int col)2402 int indexTypeForColumn(Session session, int col) { 2403 2404 int i = bestIndexForColumn[col]; 2405 2406 if (i > -1) { 2407 return indexList[i].isUnique() 2408 && indexList[i].getColumnCount() == 1 ? Index.INDEX_UNIQUE 2409 : Index 2410 .INDEX_NON_UNIQUE; 2411 } 2412 2413 switch (tableType) { 2414 2415 // case TableBase.MEMORY_TABLE : 2416 case TableBase.FUNCTION_TABLE : 2417 case TableBase.SYSTEM_SUBQUERY : 2418 case TableBase.INFO_SCHEMA_TABLE : 2419 case TableBase.VIEW_TABLE : 2420 case TableBase.TEMP_TABLE : { 2421 return Index.INDEX_NON_UNIQUE; 2422 } 2423 } 2424 2425 return Index.INDEX_NONE; 2426 } 2427 2428 /** 2429 * Finds an existing index for a column group 2430 */ getIndexForColumns(Session session, int[] cols)2431 synchronized Index getIndexForColumns(Session session, int[] cols) { 2432 2433 int i = bestIndexForColumn[cols[0]]; 2434 2435 if (i > -1) { 2436 return indexList[i]; 2437 } 2438 2439 switch (tableType) { 2440 2441 // case TableBase.MEMORY_TABLE : 2442 case TableBase.FUNCTION_TABLE : 2443 case TableBase.SYSTEM_SUBQUERY : 2444 case TableBase.INFO_SCHEMA_TABLE : 2445 case TableBase.VIEW_TABLE : 2446 case TableBase.TEMP_TABLE : { 2447 Index index = createIndexForColumns(session, cols); 2448 2449 return index; 2450 } 2451 } 2452 2453 return null; 2454 } 2455 2456 /** 2457 * Finds an existing index for an ordered full column group 2458 */ getFullIndexForColumns(int[] cols)2459 Index getFullIndexForColumns(int[] cols) { 2460 2461 for (int i = 0; i < indexList.length; i++) { 2462 if (ArrayUtil.haveEqualArrays(indexList[i].getColumns(), cols, 2463 cols.length)) { 2464 return indexList[i]; 2465 } 2466 } 2467 2468 return null; 2469 } 2470 2471 /** 2472 * Finds an existing index for an unordered full column group 2473 */ getIndexForColumns(int[] cols)2474 Index getIndexForColumns(int[] cols) { 2475 2476 for (int i = 0; i < indexList.length; i++) { 2477 if (ArrayUtil.haveEqualSets(indexList[i].getColumns(), cols, 2478 cols.length)) { 2479 return indexList[i]; 2480 } 2481 } 2482 2483 return null; 2484 } 2485 2486 /** 2487 * Finds an existing index for a column set or create one for temporary 2488 * tables. 2489 * 2490 * synchronized required for shared INFORMATION_SCHEMA etc. tables 2491 */ getIndexForColumns(Session session, OrderedIntHashSet set, int opType, boolean ordered)2492 synchronized IndexUse[] getIndexForColumns(Session session, 2493 OrderedIntHashSet set, int opType, boolean ordered) { 2494 2495 if (set.isEmpty()) { 2496 return Index.emptyUseArray; 2497 } 2498 2499 IndexUse[] indexUse = findIndexForColumns(session, set, opType, 2500 ordered); 2501 2502 if (indexUse.length == 0) { 2503 2504 // index is not full; 2505 switch (tableType) { 2506 2507 case TableBase.FUNCTION_TABLE : 2508 case TableBase.SYSTEM_SUBQUERY : 2509 case TableBase.INFO_SCHEMA_TABLE : 2510 case TableBase.VIEW_TABLE : 2511 case TableBase.TEMP_TABLE : { 2512 Index selected = createIndexForColumns(session, 2513 set.toArray()); 2514 2515 if (selected != null) { 2516 indexUse = selected.asArray(); 2517 } 2518 } 2519 } 2520 } 2521 2522 return indexUse; 2523 } 2524 findIndexForColumns(Session session, OrderedIntHashSet set, int opType, boolean ordered)2525 IndexUse[] findIndexForColumns(Session session, OrderedIntHashSet set, 2526 int opType, boolean ordered) { 2527 2528 IndexUse[] indexUse = Index.emptyUseArray; 2529 2530 if (set.isEmpty()) { 2531 return Index.emptyUseArray; 2532 } 2533 2534 for (int i = 0, count = indexList.length; i < count; i++) { 2535 Index currentIndex = getIndex(i); 2536 int[] indexcols = currentIndex.getColumns(); 2537 int matchCount = ordered ? set.getOrderedStartMatchCount(indexcols) 2538 : set.getStartMatchCount(indexcols); 2539 2540 if (matchCount == 0) { 2541 continue; 2542 } 2543 2544 if (matchCount == set.size()) { 2545 return currentIndex.asArray(); 2546 } 2547 2548 if (matchCount == currentIndex.getColumnCount()) { 2549 if (currentIndex.isUnique()) { 2550 return currentIndex.asArray(); 2551 } 2552 } 2553 2554 if (indexUse.length == 0 2555 && matchCount == currentIndex.getColumnCount()) { 2556 indexUse = currentIndex.asArray(); 2557 } else { 2558 IndexUse[] newList = new IndexUse[indexUse.length + 1]; 2559 2560 ArrayUtil.copyArray(indexUse, newList, indexUse.length); 2561 2562 newList[newList.length - 1] = new IndexUse(currentIndex, 2563 matchCount); 2564 indexUse = newList; 2565 } 2566 } 2567 2568 return indexUse; 2569 } 2570 2571 /** 2572 * Returns an index on all the columns 2573 */ getFullIndex(Session session)2574 public Index getFullIndex(Session session) { 2575 2576 if (fullIndex == null) { 2577 fullIndex = getFullIndexForColumns(defaultColumnMap); 2578 2579 if (fullIndex == null) { 2580 fullIndex = createIndexForColumns(session, defaultColumnMap); 2581 } 2582 } 2583 2584 return fullIndex; 2585 } 2586 2587 /** 2588 * Return the list of file pointers to root nodes for this table's 2589 * indexes. 2590 */ getIndexRootsArray()2591 public final long[] getIndexRootsArray() { 2592 2593 PersistentStore store = 2594 database.persistentStoreCollection.getStore(this); 2595 long[] roots = new long[indexList.length]; 2596 2597 for (int index = 0; index < indexList.length; index++) { 2598 CachedObject accessor = store.getAccessor(indexList[index]); 2599 2600 roots[index] = accessor == null ? -1 2601 : accessor.getPos(); 2602 } 2603 2604 return roots; 2605 } 2606 2607 /** 2608 * Sets the index roots of a cached table to specified file 2609 * pointers. If a 2610 * file pointer is -1 then the particular index root is null. A null index 2611 * root signifies an empty table. Accordingly, all index roots should be 2612 * null or all should be a valid file pointer/reference. 2613 */ setIndexRoots(long[] roots, long[] uniqueSize, long cardinality)2614 public void setIndexRoots(long[] roots, long[] uniqueSize, 2615 long cardinality) { 2616 2617 if (!isCached) { 2618 throw Error.error(ErrorCode.X_42501, tableName.name); 2619 } 2620 2621 PersistentStore store = 2622 database.persistentStoreCollection.getStore(this); 2623 2624 for (int index = 0; index < indexList.length; index++) { 2625 store.setAccessor(indexList[index], roots[index]); 2626 } 2627 2628 for (int index = 0; index < indexList.length; index++) { 2629 store.setElementCount(indexList[index], cardinality, 2630 uniqueSize[index]); 2631 } 2632 } 2633 setIndexRoots(long[] roots)2634 public void setIndexRoots(long[] roots) { 2635 2636 if (!isCached) { 2637 throw Error.error(ErrorCode.X_42501, tableName.name); 2638 } 2639 2640 PersistentStore store = 2641 database.persistentStoreCollection.getStore(this); 2642 2643 for (int index = 0; index < indexList.length; index++) { 2644 store.setAccessor(indexList[index], roots[index]); 2645 } 2646 } 2647 2648 /** 2649 * Sets the index roots. 2650 */ setIndexRoots(Session session, String s)2651 void setIndexRoots(Session session, String s) { 2652 2653 if (!isCached) { 2654 throw Error.error(ErrorCode.X_42501, tableName.name); 2655 } 2656 2657 int indexCount = getIndexCount(); 2658 ParserDQL p = new ParserDQL(session, new Scanner(session, s), null); 2659 long[] roots = new long[indexCount]; 2660 long[] uniqueSize = new long[indexCount]; 2661 long cardinality = -1; 2662 2663 p.read(); 2664 2665 for (int index = 0; index < indexCount; index++) { 2666 long v = p.readBigint(); 2667 2668 roots[index] = v; 2669 } 2670 2671 try { 2672 for (int index = 0; index < indexCount; index++) { 2673 long v = p.readBigint(); 2674 2675 uniqueSize[index] = v; 2676 } 2677 2678 cardinality = p.readBigint(); 2679 } catch (Exception e) { 2680 2681 // version 1.x database 2682 } 2683 2684 setIndexRoots(roots, uniqueSize, cardinality); 2685 } 2686 generateAndCheckData(Session session, Object[] data)2687 void generateAndCheckData(Session session, Object[] data) { 2688 2689 if (hasGeneratedValues) { 2690 setGeneratedColumns(session, data); 2691 } 2692 2693 enforceTypeLimits(session, data); 2694 2695 if (hasDomainColumns || hasNotNullColumns) { 2696 enforceRowConstraints(session, data); 2697 } 2698 } 2699 2700 /** 2701 * Mid level method for inserting single rows. Performs constraint checks and 2702 * fires row level triggers. 2703 */ insertSingleRow(Session session, PersistentStore store, Object[] data, int[] changedCols)2704 Row insertSingleRow(Session session, PersistentStore store, Object[] data, 2705 int[] changedCols) { 2706 2707 generateAndCheckData(session, data); 2708 2709 if (isView) { 2710 2711 // may have domain column 2712 return null; 2713 } 2714 2715 Row row = (Row) store.getNewCachedObject(session, data, true); 2716 2717 session.addInsertAction(this, store, row, changedCols); 2718 2719 return row; 2720 } 2721 2722 /** 2723 * Multi-row insert method. Used for CREATE TABLE AS ... queries. 2724 */ insertIntoTable(Session session, Result result)2725 void insertIntoTable(Session session, Result result) { 2726 2727 PersistentStore store = getRowStore(session); 2728 RowSetNavigator nav = result.initialiseNavigator(); 2729 2730 while (nav.hasNext()) { 2731 Object[] data = nav.getNext(); 2732 Object[] newData = 2733 (Object[]) ArrayUtil.resizeArrayIfDifferent(data, columnCount); 2734 2735 insertData(session, store, newData); 2736 } 2737 } 2738 2739 /** 2740 * 2741 */ insertNoCheckFromLog(Session session, Object[] data)2742 public void insertNoCheckFromLog(Session session, Object[] data) { 2743 2744 systemUpdateIdentityValue(data); 2745 2746 PersistentStore store = getRowStore(session); 2747 Row row = (Row) store.getNewCachedObject(session, data, true); 2748 2749 session.addInsertAction(this, store, row, null); 2750 } 2751 2752 /** 2753 * Used for system table inserts. No checks. No identity 2754 * columns. 2755 */ insertSys(Session session, PersistentStore store, Result ins)2756 public int insertSys(Session session, PersistentStore store, Result ins) { 2757 2758 RowSetNavigator nav = ins.getNavigator(); 2759 int count = 0; 2760 2761 while (nav.hasNext()) { 2762 insertSys(session, store, nav.getNext()); 2763 2764 count++; 2765 } 2766 2767 return count; 2768 } 2769 2770 /** 2771 * Used for subquery inserts. No checks. No identity 2772 * columns. 2773 */ insertResult(Session session, PersistentStore store, Result ins)2774 void insertResult(Session session, PersistentStore store, Result ins) { 2775 2776 RowSetNavigator nav = ins.initialiseNavigator(); 2777 2778 while (nav.hasNext()) { 2779 Object[] data = nav.getNext(); 2780 Object[] newData = 2781 (Object[]) ArrayUtil.resizeArrayIfDifferent(data, columnCount); 2782 2783 insertData(session, store, newData); 2784 } 2785 } 2786 2787 /** 2788 * Not for general use. 2789 * Used by ScriptReader to unconditionally insert a row into 2790 * the table when the .script file is read. 2791 */ insertFromScript(Session session, PersistentStore store, Object[] data)2792 public void insertFromScript(Session session, PersistentStore store, 2793 Object[] data) { 2794 2795 systemUpdateIdentityValue(data); 2796 2797 if (session.database.getProperties().isVersion18()) { 2798 for (int i = 0; i < columnCount; i++) { 2799 if (data[i] != null) { 2800 int length; 2801 2802 if (colTypes[i].isCharacterType() 2803 || colTypes[i].isBinaryType()) { 2804 if (data[i] instanceof String) { 2805 length = ((String) data[i]).length(); 2806 } else if (data[i] instanceof BinaryData) { 2807 length = 2808 (int) ((BinaryData) data[i]).length(session); 2809 } else { 2810 throw Error.runtimeError(ErrorCode.X_07000, 2811 "Table"); 2812 } 2813 2814 if (length > colTypes[i].precision) { 2815 length = ((length / 10) + 1) * 10; 2816 colTypes[i] = 2817 Type.getType(colTypes[i].typeCode, 2818 colTypes[i].getCharacterSet(), 2819 colTypes[i].getCollation(), 2820 length, 0); 2821 2822 ColumnSchema column = getColumn(i); 2823 2824 column.setType(colTypes[i]); 2825 } 2826 } 2827 } 2828 } 2829 } 2830 2831 insertData(session, store, data); 2832 } 2833 2834 /** 2835 * For system operations outside transaction control 2836 */ insertData(Session session, PersistentStore store, Object[] data)2837 public void insertData(Session session, PersistentStore store, 2838 Object[] data) { 2839 2840 Row row = (Row) store.getNewCachedObject(session, data, false); 2841 2842 store.indexRow(session, row); 2843 } 2844 2845 /** 2846 * Used by the system tables only 2847 */ insertSys(Session session, PersistentStore store, Object[] data)2848 public void insertSys(Session session, PersistentStore store, 2849 Object[] data) { 2850 2851 Row row = (Row) store.getNewCachedObject(session, data, false); 2852 2853 store.indexRow(session, row); 2854 } 2855 2856 /** 2857 * If there is an identity column in the table, sets 2858 * the value and/or adjusts the identity value for the table. 2859 */ setIdentityColumn(Session session, Object[] data)2860 protected void setIdentityColumn(Session session, Object[] data) { 2861 2862 if (identityColumn != -1) { 2863 Number id = (Number) data[identityColumn]; 2864 2865 if (identitySequence.getName() == null) { 2866 if (id == null) { 2867 id = (Number) identitySequence.getValueObject(); 2868 data[identityColumn] = id; 2869 } else { 2870 identitySequence.userUpdate(id.longValue()); 2871 } 2872 } else { 2873 if (id == null) { 2874 id = (Number) session.sessionData.getSequenceValue( 2875 identitySequence); 2876 data[identityColumn] = id; 2877 } 2878 } 2879 2880 if (session != null) { 2881 session.setLastIdentity(id); 2882 } 2883 } 2884 } 2885 setGeneratedColumns(Session session, Object[] data)2886 public void setGeneratedColumns(Session session, Object[] data) { 2887 2888 if (hasGeneratedValues) { 2889 for (int i = 0; i < colGenerated.length; i++) { 2890 if (colGenerated[i]) { 2891 Expression e = getColumn(i).getGeneratingExpression(); 2892 RangeIteratorBase range = 2893 session.sessionContext.getCheckIterator( 2894 getDefaultRanges()[0]); 2895 2896 range.setCurrent(data); 2897 2898 data[i] = e.getValue(session, colTypes[i]); 2899 } 2900 } 2901 } 2902 } 2903 setUpdatedColumns(Session session, Object[] data)2904 public void setUpdatedColumns(Session session, Object[] data) { 2905 2906 if (hasUpdatedValues) { 2907 for (int i = 0; i < colUpdated.length; i++) { 2908 if (colUpdated[i]) { 2909 Expression e = getColumn(i).getUpdateExpression(); 2910 2911 data[i] = e.getValue(session, colTypes[i]); 2912 } 2913 } 2914 } 2915 } 2916 systemSetIdentityColumn(Session session, Object[] data)2917 public void systemSetIdentityColumn(Session session, Object[] data) { 2918 2919 if (identityColumn != -1) { 2920 Number id = (Number) data[identityColumn]; 2921 2922 if (id == null) { 2923 id = (Number) identitySequence.getValueObject(); 2924 data[identityColumn] = id; 2925 } else { 2926 identitySequence.userUpdate(id.longValue()); 2927 } 2928 } 2929 } 2930 2931 /** 2932 * If there is an identity column in the table, sets 2933 * the max identity value. 2934 */ systemUpdateIdentityValue(Object[] data)2935 public void systemUpdateIdentityValue(Object[] data) { 2936 2937 if (identityColumn != -1) { 2938 Number id = (Number) data[identityColumn]; 2939 2940 if (id != null) { 2941 identitySequence.systemUpdate(id.longValue()); 2942 } 2943 } 2944 } 2945 2946 /** 2947 * For log statements. Find a single row to delete. 2948 */ getDeleteRowFromLog(Session session, Object[] data)2949 public Row getDeleteRowFromLog(Session session, Object[] data) { 2950 2951 Row row = null; 2952 PersistentStore store = getRowStore(session); 2953 2954 if (hasPrimaryKey()) { 2955 Index index = getPrimaryIndex(); 2956 int[] colsSequence = index.getDefaultColumnMap(); 2957 RowIterator it = index.findFirstRow(session, store, data, 2958 colsSequence); 2959 2960 row = it.getNextRow(); 2961 2962 it.release(); 2963 } else if (bestIndex == null) { 2964 RowIterator it = rowIterator(session); 2965 2966 while (true) { 2967 row = it.getNextRow(); 2968 2969 if (row == null) { 2970 break; 2971 } 2972 2973 if (Table.compareRows( 2974 session, row.getData(), data, defaultColumnMap, 2975 colTypes) == 0) { 2976 break; 2977 } 2978 } 2979 2980 it.release(); 2981 } else { 2982 RowIterator it = bestIndex.findFirstRow(session, store, data); 2983 2984 while (true) { 2985 row = it.getNextRow(); 2986 2987 if (row == null) { 2988 break; 2989 } 2990 2991 Object[] rowdata = row.getData(); 2992 2993 // reached end of range 2994 if (bestIndex.compareRowNonUnique( 2995 session, rowdata, data, bestIndex.getColumns()) != 0) { 2996 row = null; 2997 2998 break; 2999 } 3000 3001 if (Table.compareRows( 3002 session, rowdata, data, defaultColumnMap, 3003 colTypes) == 0) { 3004 break; 3005 } 3006 } 3007 3008 it.release(); 3009 } 3010 3011 return row; 3012 } 3013 rowIteratorClustered(Session session)3014 public RowIterator rowIteratorClustered(Session session) { 3015 3016 PersistentStore store = getRowStore(session); 3017 Index index = getClusteredIndex(); 3018 3019 if (index == null) { 3020 index = getPrimaryIndex(); 3021 } 3022 3023 return index.firstRow(session, store, 0, null); 3024 } 3025 rowIteratorClustered(PersistentStore store)3026 public RowIterator rowIteratorClustered(PersistentStore store) { 3027 3028 Index index = getClusteredIndex(); 3029 3030 if (index == null) { 3031 index = getPrimaryIndex(); 3032 } 3033 3034 return index.firstRow(store); 3035 } 3036 clearAllData(Session session)3037 public void clearAllData(Session session) { 3038 3039 super.clearAllData(session); 3040 3041 if (identitySequence != null) { 3042 identitySequence.reset(); 3043 } 3044 } 3045 clearAllData(PersistentStore store)3046 public void clearAllData(PersistentStore store) { 3047 3048 super.clearAllData(store); 3049 3050 if (identitySequence != null) { 3051 identitySequence.reset(); 3052 } 3053 } 3054 3055 /** 3056 * Path used for all stores 3057 */ getRowStore(Session session)3058 public PersistentStore getRowStore(Session session) { 3059 3060 if (store != null) { 3061 return store; 3062 } 3063 3064 if (isSessionBased) { 3065 return session.sessionData.persistentStoreCollection.getStore( 3066 this); 3067 } 3068 3069 return database.persistentStoreCollection.getStore(this); 3070 } 3071 getQueryExpression()3072 public QueryExpression getQueryExpression() { 3073 return null; 3074 } 3075 getDataExpression()3076 public Expression getDataExpression() { 3077 return null; 3078 } 3079 prepareTable(Session session)3080 public void prepareTable(Session session) {} 3081 materialise(Session session)3082 public void materialise(Session session) {} 3083 materialiseCorrelated(Session session)3084 public void materialiseCorrelated(Session session) {} 3085 } 3086