1 /* 2 * For work developed by the HSQL Development Group: 3 * 4 * Copyright (c) 2001-2016, The HSQL Development Group 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * Redistributions of source code must retain the above copyright notice, this 11 * list of conditions and the following disclaimer. 12 * 13 * Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * Neither the name of the HSQL Development Group nor the names of its 18 * contributors may be used to endorse or promote products derived from this 19 * software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 25 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * 34 * 35 * For work originally developed by the Hypersonic SQL Group: 36 * 37 * Copyright (c) 1995-2000, The Hypersonic SQL Group. 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions are met: 42 * 43 * Redistributions of source code must retain the above copyright notice, this 44 * list of conditions and the following disclaimer. 45 * 46 * Redistributions in binary form must reproduce the above copyright notice, 47 * this list of conditions and the following disclaimer in the documentation 48 * and/or other materials provided with the distribution. 49 * 50 * Neither the name of the Hypersonic SQL Group nor the names of its 51 * contributors may be used to endorse or promote products derived from this 52 * software without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 55 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, 58 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 59 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 60 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 61 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 62 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 63 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 64 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 65 * 66 * This software consists of voluntary contributions made by many individuals 67 * on behalf of the Hypersonic SQL Group. 68 */ 69 70 71 package org.hsqldb.index; 72 73 import org.hsqldb.Constraint; 74 import org.hsqldb.HsqlNameManager.HsqlName; 75 import org.hsqldb.OpTypes; 76 import org.hsqldb.Row; 77 import org.hsqldb.RowAVL; 78 import org.hsqldb.SchemaObject; 79 import org.hsqldb.Session; 80 import org.hsqldb.Table; 81 import org.hsqldb.TableBase; 82 import org.hsqldb.Tokens; 83 import org.hsqldb.TransactionManager; 84 import org.hsqldb.error.Error; 85 import org.hsqldb.error.ErrorCode; 86 import org.hsqldb.lib.ArrayUtil; 87 import org.hsqldb.lib.OrderedHashSet; 88 import org.hsqldb.navigator.RowIterator; 89 import org.hsqldb.persist.PersistentStore; 90 import org.hsqldb.rights.Grantee; 91 import org.hsqldb.types.Type; 92 93 // fredt@users 20020221 - patch 513005 by sqlbob@users - corrections 94 // fredt@users - patch 1.8.0 - reworked the interface and comparison methods 95 // fredt@users - patch 1.8.0 - improved reliability for cached indexes 96 // fredt@users - patch 1.9.0 - iterators and concurrency 97 // fredt@users - patch 2.0.0 - enhanced selection and iterators 98 99 /** 100 * Implementation of an AVL tree with parent pointers in nodes. Subclasses 101 * of Node implement the tree node objects for memory or disk storage. An 102 * Index has a root Node that is linked with other nodes using Java Object 103 * references or file pointers, depending on Node implementation.<p> 104 * An Index object also holds information on table columns (in the form of int 105 * indexes) that are covered by it.<p> 106 * 107 * New class derived from Hypersonic SQL code and enhanced in HSQLDB. <p> 108 * 109 * @author Thomas Mueller (Hypersonic SQL Group) 110 * @author Fred Toussi (fredt@users dot sourceforge.net) 111 * @version 2.3.3 112 * @since Hypersonic SQL 113 */ 114 public class IndexAVL implements Index { 115 116 private static final IndexRowIterator emptyIterator = 117 new IndexRowIterator(null, (PersistentStore) null, null, null, 0, 118 false, false); 119 120 // fields 121 private final long persistenceId; 122 protected final HsqlName name; 123 private final boolean[] colCheck; 124 final int[] colIndex; 125 private final int[] defaultColMap; 126 final Type[] colTypes; 127 private final boolean[] colDesc; 128 private final boolean[] nullsLast; 129 final boolean isSimpleOrder; 130 final boolean isSimple; 131 protected final boolean isPK; // PK with or without columns 132 protected final boolean isUnique; // DDL uniqueness 133 protected final boolean isConstraint; 134 private final boolean isForward; 135 private boolean isClustered; 136 protected TableBase table; 137 int position; 138 private IndexUse[] asArray; 139 140 // 141 Object[] nullData; 142 143 /** 144 * Constructor declaration 145 * 146 * @param name HsqlName of the index 147 * @param id persistnece id 148 * @param table table of the index 149 * @param columns array of column indexes 150 * @param descending boolean[] 151 * @param nullsLast boolean[] 152 * @param colTypes array of column types 153 * @param pk if index is for a primary key 154 * @param unique is this a unique index 155 * @param constraint does this index belonging to a constraint 156 * @param forward is this an auto-index for an FK that refers to a table 157 * defined after this table 158 */ IndexAVL(HsqlName name, long id, TableBase table, int[] columns, boolean[] descending, boolean[] nullsLast, Type[] colTypes, boolean pk, boolean unique, boolean constraint, boolean forward)159 public IndexAVL(HsqlName name, long id, TableBase table, int[] columns, 160 boolean[] descending, boolean[] nullsLast, 161 Type[] colTypes, boolean pk, boolean unique, 162 boolean constraint, boolean forward) { 163 164 this.persistenceId = id; 165 this.name = name; 166 this.colIndex = columns; 167 this.colTypes = colTypes; 168 this.colDesc = descending == null ? new boolean[columns.length] 169 : descending; 170 this.nullsLast = nullsLast == null ? new boolean[columns.length] 171 : nullsLast; 172 this.isPK = pk; 173 this.isUnique = unique; 174 this.isConstraint = constraint; 175 this.isForward = forward; 176 this.table = table; 177 this.colCheck = table.getNewColumnCheckList(); 178 this.asArray = new IndexUse[]{ new IndexUse(this, colIndex.length) }; 179 180 ArrayUtil.intIndexesToBooleanArray(colIndex, colCheck); 181 182 this.defaultColMap = new int[columns.length]; 183 184 ArrayUtil.fillSequence(defaultColMap); 185 186 boolean simpleOrder = colIndex.length > 0; 187 188 for (int i = 0; i < colDesc.length; i++) { 189 if (this.colDesc[i] || this.nullsLast[i]) { 190 simpleOrder = false; 191 } 192 } 193 194 isSimpleOrder = simpleOrder; 195 isSimple = isSimpleOrder && colIndex.length == 1; 196 nullData = new Object[colIndex.length]; 197 } 198 199 // SchemaObject implementation getType()200 public int getType() { 201 return SchemaObject.INDEX; 202 } 203 getName()204 public HsqlName getName() { 205 return name; 206 } 207 getCatalogName()208 public HsqlName getCatalogName() { 209 return name.schema.schema; 210 } 211 getSchemaName()212 public HsqlName getSchemaName() { 213 return name.schema; 214 } 215 getOwner()216 public Grantee getOwner() { 217 return name.schema.owner; 218 } 219 getReferences()220 public OrderedHashSet getReferences() { 221 return new OrderedHashSet(); 222 } 223 getComponents()224 public OrderedHashSet getComponents() { 225 return null; 226 } 227 compile(Session session, SchemaObject parentObject)228 public void compile(Session session, SchemaObject parentObject) {} 229 getSQL()230 public String getSQL() { 231 232 StringBuffer sb = new StringBuffer(128); 233 234 sb.append(Tokens.T_CREATE).append(' '); 235 236 if (isUnique()) { 237 sb.append(Tokens.T_UNIQUE).append(' '); 238 } 239 240 sb.append(Tokens.T_INDEX).append(' '); 241 sb.append(getName().statementName); 242 sb.append(' ').append(Tokens.T_ON).append(' '); 243 sb.append(((Table) table).getName().getSchemaQualifiedStatementName()); 244 sb.append(((Table) table).getColumnListSQL(colIndex, colIndex.length)); 245 246 return sb.toString(); 247 } 248 getChangeTimestamp()249 public long getChangeTimestamp() { 250 return 0; 251 } 252 253 // IndexInterface asArray()254 public IndexUse[] asArray() { 255 return asArray; 256 } 257 emptyIterator()258 public RowIterator emptyIterator() { 259 return emptyIterator; 260 } 261 getPosition()262 public int getPosition() { 263 return position; 264 } 265 setPosition(int position)266 public void setPosition(int position) { 267 this.position = position; 268 } 269 getPersistenceId()270 public long getPersistenceId() { 271 return persistenceId; 272 } 273 274 /** 275 * Returns the count of visible columns used 276 */ getColumnCount()277 public int getColumnCount() { 278 return colIndex.length; 279 } 280 281 /** 282 * Is this a UNIQUE index? 283 */ isUnique()284 public boolean isUnique() { 285 return isUnique; 286 } 287 288 /** 289 * Does this index belong to a constraint? 290 */ isConstraint()291 public boolean isConstraint() { 292 return isConstraint; 293 } 294 295 /** 296 * Returns the array containing column indexes for index 297 */ getColumns()298 public int[] getColumns() { 299 return colIndex; 300 } 301 302 /** 303 * Returns the array containing column indexes for index 304 */ getColumnTypes()305 public Type[] getColumnTypes() { 306 return colTypes; 307 } 308 getColumnDesc()309 public boolean[] getColumnDesc() { 310 return colDesc; 311 } 312 getDefaultColumnMap()313 public int[] getDefaultColumnMap() { 314 return this.defaultColMap; 315 } 316 317 /** 318 * Returns a value indicating the order of different types of index in 319 * the list of indexes for a table. The position of the groups of Indexes 320 * in the list in ascending order is as follows: 321 * 322 * primary key index 323 * unique constraint indexes 324 * autogenerated foreign key indexes for FK's that reference this table or 325 * tables created before this table 326 * user created indexes (CREATE INDEX) 327 * autogenerated foreign key indexes for FK's that reference tables created 328 * after this table 329 * 330 * Among a group of indexes, the order is based on the order of creation 331 * of the index. 332 * 333 * @return ordinal value 334 */ getIndexOrderValue()335 public int getIndexOrderValue() { 336 337 if (isPK) { 338 return 0; 339 } 340 341 if (isConstraint) { 342 return isForward ? 4 343 : isUnique ? 0 344 : 1; 345 } else { 346 return 2; 347 } 348 } 349 isForward()350 public boolean isForward() { 351 return isForward; 352 } 353 setTable(TableBase table)354 public void setTable(TableBase table) { 355 this.table = table; 356 } 357 getTable()358 public TableBase getTable() { 359 return table; 360 } 361 setClustered(boolean clustered)362 public void setClustered(boolean clustered) { 363 isClustered = clustered; 364 } 365 isClustered()366 public boolean isClustered() { 367 return isClustered; 368 } 369 370 /** 371 * Returns the node count. 372 */ size(Session session, PersistentStore store)373 public long size(Session session, PersistentStore store) { 374 return store.elementCount(session); 375 } 376 sizeUnique(PersistentStore store)377 public long sizeUnique(PersistentStore store) { 378 return store.elementCountUnique(this); 379 } 380 searchCost(Session session, PersistentStore store)381 public double[] searchCost(Session session, PersistentStore store) { 382 383 boolean probeDeeper = false; 384 int counter = 1; 385 double[] changes = new double[colIndex.length]; 386 int depth = 0; 387 int[] depths = new int[1]; 388 389 store.readLock(); 390 391 try { 392 NodeAVL node = getAccessor(store); 393 NodeAVL temp = node; 394 395 if (node == null) { 396 return changes; 397 } 398 399 while (true) { 400 node = temp; 401 temp = node.getLeft(store); 402 403 if (temp == null) { 404 break; 405 } 406 407 if (depth == Index.probeDepth) { 408 probeDeeper = true; 409 410 break; 411 } 412 413 depth++; 414 } 415 416 while (true) { 417 temp = next(store, node, depth, probeDepth, depths); 418 depth = depths[0]; 419 420 if (temp == null) { 421 break; 422 } 423 424 compareRowForChange(session, node.getData(store), 425 temp.getData(store), changes); 426 427 node = temp; 428 429 counter++; 430 } 431 432 if (probeDeeper) { 433 double[] factors = new double[colIndex.length]; 434 int extras = probeFactor(session, store, factors, true) 435 + probeFactor(session, store, factors, false); 436 437 for (int i = 0; i < colIndex.length; i++) { 438 factors[i] /= 2.0; 439 440 for (int j = 0; j < factors[i]; j++) { 441 changes[i] *= 2; 442 } 443 } 444 } 445 446 long rowCount = store.elementCount(); 447 448 for (int i = 0; i < colIndex.length; i++) { 449 if (changes[i] == 0) { 450 changes[i] = 1; 451 } 452 453 changes[i] = rowCount / changes[i]; 454 455 if (changes[i] < 2) { 456 changes[i] = 2; 457 } 458 } 459 460 /* 461 StringBuffer s = new StringBuffer(); 462 463 s.append("count " + rowCount + " columns " + colIndex.length 464 + " selectivity " + changes[0]); 465 System.out.println(s); 466 */ 467 return changes; 468 } finally { 469 store.readUnlock(); 470 } 471 } 472 probeFactor(Session session, PersistentStore store, double[] changes, boolean left)473 int probeFactor(Session session, PersistentStore store, double[] changes, 474 boolean left) { 475 476 int depth = 0; 477 NodeAVL x = getAccessor(store); 478 NodeAVL n = x; 479 480 if (x == null) { 481 return 0; 482 } 483 484 while (n != null) { 485 x = n; 486 n = left ? x.getLeft(store) 487 : x.getRight(store); 488 489 depth++; 490 491 if (depth > probeDepth && n != null) { 492 compareRowForChange(session, x.getData(store), 493 n.getData(store), changes); 494 } 495 } 496 497 return depth - probeDepth; 498 } 499 getNodeCount(Session session, PersistentStore store)500 public long getNodeCount(Session session, PersistentStore store) { 501 502 long count = 0; 503 RowIterator it = firstRow(session, store, 0, null); 504 505 while (it.hasNext()) { 506 it.getNextRow(); 507 508 count++; 509 } 510 511 return count; 512 } 513 isEmpty(PersistentStore store)514 public boolean isEmpty(PersistentStore store) { 515 516 store.readLock(); 517 518 try { 519 return getAccessor(store) == null; 520 } finally { 521 store.readUnlock(); 522 } 523 } 524 525 /** 526 * Removes all links between memory nodes 527 */ unlinkNodes(NodeAVL primaryRoot)528 public void unlinkNodes(NodeAVL primaryRoot) { 529 530 NodeAVL x = primaryRoot; 531 NodeAVL l = x; 532 533 while (l != null) { 534 x = l; 535 l = x.getLeft(null); 536 } 537 538 while (x != null) { 539 NodeAVL n = nextUnlink(x); 540 541 x = n; 542 } 543 } 544 nextUnlink(NodeAVL x)545 private NodeAVL nextUnlink(NodeAVL x) { 546 547 NodeAVL temp = x.getRight(null); 548 549 if (temp != null) { 550 x = temp; 551 temp = x.getLeft(null); 552 553 while (temp != null) { 554 x = temp; 555 temp = x.getLeft(null); 556 } 557 558 return x; 559 } 560 561 temp = x; 562 x = x.getParent(null); 563 564 while (x != null && x.isRight(temp)) { 565 x.nRight = null; 566 567 temp.getRow(null).destroy(); 568 temp.delete(); 569 570 // 571 temp = x; 572 x = x.getParent(null); 573 } 574 575 if (x != null) { 576 x.nLeft = null; 577 } 578 579 temp.getRow(null).destroy(); 580 temp.delete(); 581 582 return x; 583 } 584 checkIndex(PersistentStore store)585 public void checkIndex(PersistentStore store) { 586 587 store.readLock(); 588 589 try { 590 NodeAVL p = getAccessor(store); 591 NodeAVL f = null; 592 593 while (p != null) { 594 f = p; 595 596 checkNodes(store, p); 597 598 p = p.getLeft(store); 599 } 600 601 p = f; 602 603 while (f != null) { 604 checkNodes(store, f); 605 606 f = next(store, f); 607 } 608 } finally { 609 store.readUnlock(); 610 } 611 } 612 checkNodes(PersistentStore store, NodeAVL p)613 void checkNodes(PersistentStore store, NodeAVL p) { 614 615 NodeAVL l = p.getLeft(store); 616 NodeAVL r = p.getRight(store); 617 618 if (l != null && l.getBalance(store) == -2) { 619 System.out.print("broken index - deleted"); 620 } 621 622 if (r != null && r.getBalance(store) == -2) { 623 System.out.print("broken index -deleted"); 624 } 625 626 if (l != null && !p.equals(l.getParent(store))) { 627 System.out.print("broken index - no parent"); 628 } 629 630 if (r != null && !p.equals(r.getParent(store))) { 631 System.out.print("broken index - no parent"); 632 } 633 } 634 635 /** 636 * Compares two table rows based on the columns of this index. The rowColMap 637 * parameter specifies which columns of the other table are to be compared 638 * with the colIndex columns of this index. The rowColMap can cover all or 639 * only some columns of this index. 640 * 641 * @param session Session 642 * @param a row from another table 643 * @param rowColMap column indexes in the other table 644 * @param b a full row in this table 645 * @return comparison result, -1,0,+1 646 */ compareRowNonUnique(Session session, Object[] a, Object[] b, int[] rowColMap)647 public int compareRowNonUnique(Session session, Object[] a, Object[] b, 648 int[] rowColMap) { 649 650 int fieldcount = rowColMap.length; 651 652 for (int j = 0; j < fieldcount; j++) { 653 int i = colTypes[j].compare(session, a[colIndex[j]], 654 b[rowColMap[j]]); 655 656 if (i != 0) { 657 return i; 658 } 659 } 660 661 return 0; 662 } 663 compareRowNonUnique(Session session, Object[] a, Object[] b, int[] rowColMap, int fieldCount)664 public int compareRowNonUnique(Session session, Object[] a, Object[] b, 665 int[] rowColMap, int fieldCount) { 666 667 for (int j = 0; j < fieldCount; j++) { 668 int i = colTypes[j].compare(session, a[colIndex[j]], 669 b[rowColMap[j]]); 670 671 if (i != 0) { 672 return i; 673 } 674 } 675 676 return 0; 677 } 678 679 /** 680 * As above but use the index column data 681 */ compareRowNonUnique(Session session, Object[] a, Object[] b, int fieldCount)682 public int compareRowNonUnique(Session session, Object[] a, Object[] b, 683 int fieldCount) { 684 685 for (int j = 0; j < fieldCount; j++) { 686 int i = colTypes[j].compare(session, a[colIndex[j]], 687 b[colIndex[j]]); 688 689 if (i != 0) { 690 return i; 691 } 692 } 693 694 return 0; 695 } 696 compareRowForChange(Session session, Object[] a, Object[] b, double[] changes)697 public void compareRowForChange(Session session, Object[] a, Object[] b, 698 double[] changes) { 699 700 for (int j = 0; j < colIndex.length; j++) { 701 int i = colTypes[j].compare(session, a[colIndex[j]], 702 b[colIndex[j]]); 703 704 if (i != 0) { 705 for (; j < colIndex.length; j++) { 706 changes[j]++; 707 } 708 } 709 } 710 } 711 compareRow(Session session, Object[] a, Object[] b)712 public int compareRow(Session session, Object[] a, Object[] b) { 713 714 for (int j = 0; j < colIndex.length; j++) { 715 int i = colTypes[j].compare(session, a[colIndex[j]], 716 b[colIndex[j]]); 717 718 if (i != 0) { 719 if (isSimpleOrder) { 720 return i; 721 } 722 723 boolean nulls = a[colIndex[j]] == null 724 || b[colIndex[j]] == null; 725 726 if (colDesc[j] && !nulls) { 727 i = -i; 728 } 729 730 if (nullsLast[j] && nulls) { 731 i = -i; 732 } 733 734 return i; 735 } 736 } 737 738 return 0; 739 } 740 741 /** 742 * Compare two rows of the table for inserting rows into unique indexes 743 * Supports descending columns. 744 * 745 * @param session Session 746 * @param newRow data 747 * @param existingRow data 748 * @param useRowId boolean 749 * @param start int 750 * @return comparison result, -1,0,+1 751 */ compareRowForInsertOrDelete(Session session, Row newRow, Row existingRow, boolean useRowId, int start)752 int compareRowForInsertOrDelete(Session session, Row newRow, 753 Row existingRow, boolean useRowId, 754 int start) { 755 756 Object[] a = newRow.getData(); 757 Object[] b = existingRow.getData(); 758 759 for (int j = start; j < colIndex.length; j++) { 760 int i = colTypes[j].compare(session, a[colIndex[j]], 761 b[colIndex[j]]); 762 763 if (i != 0) { 764 if (isSimpleOrder) { 765 return i; 766 } 767 768 boolean nulls = a[colIndex[j]] == null 769 || b[colIndex[j]] == null; 770 771 if (colDesc[j] && !nulls) { 772 i = -i; 773 } 774 775 if (nullsLast[j] && nulls) { 776 i = -i; 777 } 778 779 return i; 780 } 781 } 782 783 if (useRowId) { 784 long diff = newRow.getPos() - existingRow.getPos(); 785 786 return diff == 0L ? 0 787 : diff > 0L ? 1 788 : -1; 789 } 790 791 return 0; 792 } 793 compareObject(Session session, Object[] a, Object[] b, int[] rowColMap, int position, int opType)794 int compareObject(Session session, Object[] a, Object[] b, 795 int[] rowColMap, int position, int opType) { 796 return colTypes[position].compare(session, a[colIndex[position]], 797 b[rowColMap[position]], opType); 798 } 799 hasNulls(Session session, Object[] rowData)800 boolean hasNulls(Session session, Object[] rowData) { 801 802 boolean uniqueNulls = session == null 803 || session.database.sqlUniqueNulls; 804 boolean compareId = false; 805 806 for (int j = 0; j < colIndex.length; j++) { 807 if (rowData[colIndex[j]] == null) { 808 compareId = true; 809 810 if (uniqueNulls) { 811 break; 812 } 813 } else if (!uniqueNulls) { 814 compareId = false; 815 816 break; 817 } 818 } 819 820 return compareId; 821 } 822 823 /** 824 * Insert a node into the index 825 */ insert(Session session, PersistentStore store, Row row)826 public void insert(Session session, PersistentStore store, Row row) { 827 828 NodeAVL n; 829 NodeAVL x; 830 boolean isleft = true; 831 int compare = -1; 832 boolean compareRowId = !isUnique || hasNulls(session, row.getData()); 833 834 n = getAccessor(store); 835 x = n; 836 837 if (n == null) { 838 store.setAccessor(this, ((RowAVL) row).getNode(position)); 839 840 return; 841 } 842 843 while (true) { 844 Row currentRow = n.getRow(store); 845 846 compare = compareRowForInsertOrDelete(session, row, currentRow, 847 compareRowId, 0); 848 849 // after the first match and check, all compares are with row id 850 if (compare == 0 && session != null && !compareRowId 851 && session.database.txManager.isMVRows()) { 852 if (!isEqualReadable(session, store, n)) { 853 compareRowId = true; 854 compare = compareRowForInsertOrDelete(session, row, 855 currentRow, 856 compareRowId, 857 colIndex.length); 858 } 859 } 860 861 if (compare == 0) { 862 Constraint c = null; 863 864 if (isConstraint) { 865 c = ((Table) table).getUniqueConstraintForIndex(this); 866 } 867 868 if (c == null) { 869 throw Error.error(ErrorCode.X_23505, name.statementName); 870 } else { 871 throw c.getException(row.getData()); 872 } 873 } 874 875 isleft = compare < 0; 876 x = n; 877 n = x.child(store, isleft); 878 879 if (n == null) { 880 break; 881 } 882 } 883 884 x = x.set(store, isleft, ((RowAVL) row).getNode(position)); 885 886 balance(store, x, isleft); 887 } 888 889 public void delete(Session session, PersistentStore store, Row row) { 890 891 row = (Row) store.get(row, false); 892 893 NodeAVL x = ((RowAVL) row).getNode(position); 894 895 if (x == null) { 896 return; 897 } 898 899 NodeAVL n; 900 901 if (x.getLeft(store) == null) { 902 n = x.getRight(store); 903 } else if (x.getRight(store) == null) { 904 n = x.getLeft(store); 905 } else { 906 NodeAVL d = x; 907 908 x = x.getLeft(store); 909 910 while (true) { 911 NodeAVL temp = x.getRight(store); 912 913 if (temp == null) { 914 break; 915 } 916 917 x = temp; 918 } 919 920 // x will be replaced with n later 921 n = x.getLeft(store); 922 923 // swap d and x 924 int b = x.getBalance(store); 925 926 x = x.setBalance(store, d.getBalance(store)); 927 d = d.setBalance(store, b); 928 929 // set x.parent 930 NodeAVL xp = x.getParent(store); 931 NodeAVL dp = d.getParent(store); 932 933 if (d.isRoot(store)) { 934 store.setAccessor(this, x); 935 } 936 937 x = x.setParent(store, dp); 938 939 if (dp != null) { 940 if (dp.isRight(d)) { 941 dp = dp.setRight(store, x); 942 } else { 943 dp = dp.setLeft(store, x); 944 } 945 } 946 947 // relink d.parent, x.left, x.right 948 if (d.equals(xp)) { 949 d = d.setParent(store, x); 950 951 if (d.isLeft(x)) { 952 x = x.setLeft(store, d); 953 954 NodeAVL dr = d.getRight(store); 955 956 x = x.setRight(store, dr); 957 } else { 958 x = x.setRight(store, d); 959 960 NodeAVL dl = d.getLeft(store); 961 962 x = x.setLeft(store, dl); 963 } 964 } else { 965 d = d.setParent(store, xp); 966 xp = xp.setRight(store, d); 967 968 NodeAVL dl = d.getLeft(store); 969 NodeAVL dr = d.getRight(store); 970 971 x = x.setLeft(store, dl); 972 x = x.setRight(store, dr); 973 } 974 975 x.getRight(store).setParent(store, x); 976 x.getLeft(store).setParent(store, x); 977 978 // set d.left, d.right 979 d = d.setLeft(store, n); 980 981 if (n != null) { 982 n = n.setParent(store, d); 983 } 984 985 d = d.setRight(store, null); 986 x = d; 987 } 988 989 boolean isleft = x.isFromLeft(store); 990 991 x.replace(store, this, n); 992 993 n = x.getParent(store); 994 995 x.delete(); 996 997 while (n != null) { 998 x = n; 999 1000 int sign = isleft ? 1 1001 : -1; 1002 1003 switch (x.getBalance(store) * sign) { 1004 1005 case -1 : 1006 x = x.setBalance(store, 0); 1007 break; 1008 1009 case 0 : 1010 x = x.setBalance(store, sign); 1011 1012 return; 1013 1014 case 1 : 1015 NodeAVL r = x.child(store, !isleft); 1016 int b = r.getBalance(store); 1017 1018 if (b * sign >= 0) { 1019 x.replace(store, this, r); 1020 1021 NodeAVL child = r.child(store, isleft); 1022 1023 x = x.set(store, !isleft, child); 1024 r = r.set(store, isleft, x); 1025 1026 if (b == 0) { 1027 x = x.setBalance(store, sign); 1028 r = r.setBalance(store, -sign); 1029 1030 return; 1031 } 1032 1033 x = x.setBalance(store, 0); 1034 r = r.setBalance(store, 0); 1035 x = r; 1036 } else { 1037 NodeAVL l = r.child(store, isleft); 1038 1039 x.replace(store, this, l); 1040 1041 b = l.getBalance(store); 1042 r = r.set(store, isleft, l.child(store, !isleft)); 1043 l = l.set(store, !isleft, r); 1044 x = x.set(store, !isleft, l.child(store, isleft)); 1045 l = l.set(store, isleft, x); 1046 x = x.setBalance(store, (b == sign) ? -sign 1047 : 0); 1048 r = r.setBalance(store, (b == -sign) ? sign 1049 : 0); 1050 l = l.setBalance(store, 0); 1051 x = l; 1052 } 1053 } 1054 1055 isleft = x.isFromLeft(store); 1056 n = x.getParent(store); 1057 } 1058 } 1059 existsParent(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap)1060 public boolean existsParent(Session session, PersistentStore store, 1061 Object[] rowdata, int[] rowColMap) { 1062 1063 NodeAVL node = findNode(session, store, rowdata, rowColMap, 1064 rowColMap.length, OpTypes.EQUAL, 1065 TransactionManager.ACTION_REF, false); 1066 1067 return node != null; 1068 } 1069 1070 /** 1071 * Return the first node equal to the indexdata object. The rowdata has the 1072 * same column mapping as this index. 1073 * 1074 * @param session session object 1075 * @param store store object 1076 * @param rowdata array containing index column data 1077 * @param matchCount count of columns to match 1078 * @param compareType int 1079 * @param reversed boolean 1080 * @param map boolean[] 1081 * @return iterator 1082 */ findFirstRow(Session session, PersistentStore store, Object[] rowdata, int matchCount, int distinctCount, int compareType, boolean reversed, boolean[] map)1083 public RowIterator findFirstRow(Session session, PersistentStore store, 1084 Object[] rowdata, int matchCount, 1085 int distinctCount, int compareType, 1086 boolean reversed, boolean[] map) { 1087 1088 NodeAVL node = findNode(session, store, rowdata, defaultColMap, 1089 matchCount, compareType, 1090 TransactionManager.ACTION_READ, reversed); 1091 1092 if (node == null) { 1093 return emptyIterator; 1094 } 1095 1096 return new IndexRowIterator(session, store, this, node, distinctCount, 1097 false, reversed); 1098 } 1099 1100 /** 1101 * Return the first node equal to the rowdata object. 1102 * The rowdata has the same column mapping as this table. 1103 * 1104 * @param session session object 1105 * @param store store object 1106 * @param rowdata array containing table row data 1107 * @return iterator 1108 */ findFirstRow(Session session, PersistentStore store, Object[] rowdata)1109 public RowIterator findFirstRow(Session session, PersistentStore store, 1110 Object[] rowdata) { 1111 1112 NodeAVL node = findNode(session, store, rowdata, colIndex, 1113 colIndex.length, OpTypes.EQUAL, 1114 TransactionManager.ACTION_READ, false); 1115 1116 if (node == null) { 1117 return emptyIterator; 1118 } 1119 1120 return new IndexRowIterator(session, store, this, node, 0, false, 1121 false); 1122 } 1123 1124 /** 1125 * Return the first node equal to the rowdata object. The rowdata has the 1126 * column mapping provided in rowColMap. 1127 * 1128 * @param session session object 1129 * @param store store object 1130 * @param rowdata array containing table row data 1131 * @param rowColMap int[] 1132 * @return iterator 1133 */ findFirstRow(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap)1134 public RowIterator findFirstRow(Session session, PersistentStore store, 1135 Object[] rowdata, int[] rowColMap) { 1136 1137 NodeAVL node = findNode(session, store, rowdata, rowColMap, 1138 rowColMap.length, OpTypes.EQUAL, 1139 TransactionManager.ACTION_READ, false); 1140 1141 if (node == null) { 1142 return emptyIterator; 1143 } 1144 1145 return new IndexRowIterator(session, store, this, node, 0, false, 1146 false); 1147 } 1148 1149 /** 1150 * Finds the first node where the data is not null. 1151 * 1152 * @return iterator 1153 */ findFirstRowNotNull(Session session, PersistentStore store)1154 public RowIterator findFirstRowNotNull(Session session, 1155 PersistentStore store) { 1156 1157 NodeAVL node = findNode(session, store, nullData, this.defaultColMap, 1158 1, OpTypes.NOT, 1159 TransactionManager.ACTION_READ, false); 1160 1161 if (node == null) { 1162 return emptyIterator; 1163 } 1164 1165 return new IndexRowIterator(session, store, this, node, 0, false, 1166 false); 1167 } 1168 1169 /** 1170 * Returns the row for the first node of the index 1171 * 1172 * @return Iterator for first row 1173 */ firstRow(Session session, PersistentStore store, int distinctCount, boolean[] map)1174 public RowIterator firstRow(Session session, PersistentStore store, 1175 int distinctCount, boolean[] map) { 1176 1177 store.readLock(); 1178 1179 try { 1180 NodeAVL x = getAccessor(store); 1181 NodeAVL l = x; 1182 1183 while (l != null) { 1184 x = l; 1185 l = x.getLeft(store); 1186 } 1187 1188 while (session != null && x != null) { 1189 Row row = x.getRow(store); 1190 1191 if (session.database.txManager.canRead( 1192 session, store, row, TransactionManager.ACTION_READ, 1193 null)) { 1194 break; 1195 } 1196 1197 x = next(store, x); 1198 } 1199 1200 if (x == null) { 1201 return emptyIterator; 1202 } 1203 1204 return new IndexRowIterator(session, store, this, x, 1205 distinctCount, false, false); 1206 } finally { 1207 store.readUnlock(); 1208 } 1209 } 1210 firstRow(PersistentStore store)1211 public RowIterator firstRow(PersistentStore store) { 1212 1213 store.readLock(); 1214 1215 try { 1216 NodeAVL x = getAccessor(store); 1217 NodeAVL l = x; 1218 1219 while (l != null) { 1220 x = l; 1221 l = x.getLeft(store); 1222 } 1223 1224 if (x == null) { 1225 return emptyIterator; 1226 } 1227 1228 return new IndexRowIterator(null, store, this, x, 0, false, false); 1229 } finally { 1230 store.readUnlock(); 1231 } 1232 } 1233 1234 /** 1235 * Returns the row for the last node of the index 1236 * 1237 * @return last row 1238 */ lastRow(Session session, PersistentStore store, int distinctCount, boolean[] map)1239 public RowIterator lastRow(Session session, PersistentStore store, 1240 int distinctCount, boolean[] map) { 1241 1242 store.readLock(); 1243 1244 try { 1245 NodeAVL x = getAccessor(store); 1246 NodeAVL l = x; 1247 1248 while (l != null) { 1249 x = l; 1250 l = x.getRight(store); 1251 } 1252 1253 while (session != null && x != null) { 1254 Row row = x.getRow(store); 1255 1256 if (session.database.txManager.canRead( 1257 session, store, row, TransactionManager.ACTION_READ, 1258 null)) { 1259 break; 1260 } 1261 1262 x = last(store, x); 1263 } 1264 1265 if (x == null) { 1266 return emptyIterator; 1267 } 1268 1269 return new IndexRowIterator(session, store, this, x, 1270 distinctCount, false, true); 1271 } finally { 1272 store.readUnlock(); 1273 } 1274 } 1275 1276 /** 1277 * Returns the node after the given one 1278 */ next(Session session, PersistentStore store, NodeAVL x, int distinctCount)1279 NodeAVL next(Session session, PersistentStore store, NodeAVL x, 1280 int distinctCount) { 1281 1282 if (x == null) { 1283 return null; 1284 } 1285 1286 if (distinctCount != 0) { 1287 return findDistinctNode(session, store, x, distinctCount, false); 1288 } 1289 1290 while (true) { 1291 x = next(store, x); 1292 1293 if (x == null) { 1294 return x; 1295 } 1296 1297 if (session == null) { 1298 return x; 1299 } 1300 1301 Row row = x.getRow(store); 1302 1303 if (session.database.txManager.canRead( 1304 session, store, row, TransactionManager.ACTION_READ, 1305 null)) { 1306 return x; 1307 } 1308 } 1309 } 1310 last(Session session, PersistentStore store, NodeAVL x, int distinctCount)1311 NodeAVL last(Session session, PersistentStore store, NodeAVL x, 1312 int distinctCount) { 1313 1314 if (x == null) { 1315 return null; 1316 } 1317 1318 if (distinctCount != 0) { 1319 return findDistinctNode(session, store, x, distinctCount, true); 1320 } 1321 1322 while (true) { 1323 x = last(store, x); 1324 1325 if (x == null) { 1326 return x; 1327 } 1328 1329 if (session == null) { 1330 return x; 1331 } 1332 1333 Row row = x.getRow(store); 1334 1335 if (session.database.txManager.canRead( 1336 session, store, row, TransactionManager.ACTION_READ, 1337 null)) { 1338 return x; 1339 } 1340 } 1341 } 1342 next(PersistentStore store, NodeAVL x)1343 NodeAVL next(PersistentStore store, NodeAVL x) { 1344 1345 if (x == null) { 1346 return null; 1347 } 1348 1349 RowAVL row = x.getRow(store); 1350 1351 x = row.getNode(position); 1352 1353 NodeAVL temp = x.getRight(store); 1354 1355 if (temp != null) { 1356 x = temp; 1357 temp = x.getLeft(store); 1358 1359 while (temp != null) { 1360 x = temp; 1361 temp = x.getLeft(store); 1362 } 1363 1364 return x; 1365 } 1366 1367 temp = x; 1368 x = x.getParent(store); 1369 1370 while (x != null && x.isRight(temp)) { 1371 temp = x; 1372 x = x.getParent(store); 1373 } 1374 1375 return x; 1376 } 1377 next(PersistentStore store, NodeAVL x, int depth, int maxDepth, int[] depths)1378 NodeAVL next(PersistentStore store, NodeAVL x, int depth, int maxDepth, 1379 int[] depths) { 1380 1381 NodeAVL temp = depth == maxDepth ? null 1382 : x.getRight(store); 1383 1384 if (temp != null) { 1385 depth++; 1386 1387 x = temp; 1388 temp = depth == maxDepth ? null 1389 : x.getLeft(store); 1390 1391 while (temp != null) { 1392 depth++; 1393 1394 x = temp; 1395 1396 if (depth == maxDepth) { 1397 temp = null; 1398 } else { 1399 temp = x.getLeft(store); 1400 } 1401 } 1402 1403 depths[0] = depth; 1404 1405 return x; 1406 } 1407 1408 temp = x; 1409 x = x.getParent(store); 1410 1411 depth--; 1412 1413 while (x != null && x.isRight(temp)) { 1414 temp = x; 1415 x = x.getParent(store); 1416 1417 depth--; 1418 } 1419 1420 depths[0] = depth; 1421 1422 return x; 1423 } 1424 last(PersistentStore store, NodeAVL x)1425 NodeAVL last(PersistentStore store, NodeAVL x) { 1426 1427 if (x == null) { 1428 return null; 1429 } 1430 1431 RowAVL row = x.getRow(store); 1432 1433 x = row.getNode(position); 1434 1435 NodeAVL temp = x.getLeft(store); 1436 1437 if (temp != null) { 1438 x = temp; 1439 temp = x.getRight(store); 1440 1441 while (temp != null) { 1442 x = temp; 1443 temp = x.getRight(store); 1444 } 1445 1446 return x; 1447 } 1448 1449 temp = x; 1450 x = x.getParent(store); 1451 1452 while (x != null && x.isLeft(temp)) { 1453 temp = x; 1454 x = x.getParent(store); 1455 } 1456 1457 return x; 1458 } 1459 isEqualReadable(Session session, PersistentStore store, NodeAVL node)1460 boolean isEqualReadable(Session session, PersistentStore store, 1461 NodeAVL node) { 1462 1463 NodeAVL c = node; 1464 Object[] data; 1465 Object[] nodeData; 1466 Row row; 1467 1468 row = node.getRow(store); 1469 1470 session.database.txManager.setTransactionInfo(store, row); 1471 1472 if (session.database.txManager.canRead(session, store, row, 1473 TransactionManager.ACTION_DUP, 1474 null)) { 1475 return true; 1476 } 1477 1478 data = node.getData(store); 1479 1480 while (true) { 1481 c = last(store, c); 1482 1483 if (c == null) { 1484 break; 1485 } 1486 1487 nodeData = c.getData(store); 1488 1489 if (compareRow(session, data, nodeData) == 0) { 1490 row = c.getRow(store); 1491 1492 session.database.txManager.setTransactionInfo(store, row); 1493 1494 if (session.database.txManager.canRead( 1495 session, store, row, TransactionManager.ACTION_DUP, 1496 null)) { 1497 return true; 1498 } 1499 1500 continue; 1501 } 1502 1503 break; 1504 } 1505 1506 while (true) { 1507 c = next(session, store, node, 0); 1508 1509 if (c == null) { 1510 break; 1511 } 1512 1513 nodeData = c.getData(store); 1514 1515 if (compareRow(session, data, nodeData) == 0) { 1516 row = c.getRow(store); 1517 1518 session.database.txManager.setTransactionInfo(store, row); 1519 1520 if (session.database.txManager.canRead( 1521 session, store, row, TransactionManager.ACTION_DUP, 1522 null)) { 1523 return true; 1524 } 1525 1526 continue; 1527 } 1528 1529 break; 1530 } 1531 1532 return false; 1533 } 1534 1535 /** 1536 * Finds a match with a row from a different table 1537 * 1538 * @param session Session 1539 * @param store PersistentStore 1540 * @param rowdata array containing data for the index columns 1541 * @param rowColMap map of the data to columns 1542 * @param fieldCount int 1543 * @param compareType int 1544 * @param readMode int 1545 * @param reversed 1546 * @return matching node or null 1547 */ findNode(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap, int fieldCount, int compareType, int readMode, boolean reversed)1548 NodeAVL findNode(Session session, PersistentStore store, Object[] rowdata, 1549 int[] rowColMap, int fieldCount, int compareType, 1550 int readMode, boolean reversed) { 1551 1552 store.readLock(); 1553 1554 try { 1555 NodeAVL x = getAccessor(store); 1556 NodeAVL n = null; 1557 NodeAVL result = null; 1558 Row currentRow = null; 1559 1560 if (compareType != OpTypes.EQUAL 1561 && compareType != OpTypes.IS_NULL) { 1562 fieldCount--; 1563 1564 if (compareType == OpTypes.SMALLER 1565 || compareType == OpTypes.SMALLER_EQUAL 1566 || compareType == OpTypes.MAX) { 1567 reversed = true; 1568 } 1569 } 1570 1571 while (x != null) { 1572 currentRow = x.getRow(store); 1573 1574 int i = 0; 1575 1576 if (fieldCount > 0) { 1577 i = compareRowNonUnique(session, currentRow.getData(), 1578 rowdata, rowColMap, fieldCount); 1579 } 1580 1581 if (i == 0) { 1582 switch (compareType) { 1583 1584 case OpTypes.MAX : 1585 case OpTypes.IS_NULL : 1586 case OpTypes.EQUAL : { 1587 result = x; 1588 1589 if (reversed) { 1590 n = x.getRight(store); 1591 } else { 1592 n = x.getLeft(store); 1593 } 1594 1595 break; 1596 } 1597 case OpTypes.NOT : 1598 case OpTypes.GREATER : { 1599 i = compareObject(session, currentRow.getData(), 1600 rowdata, rowColMap, fieldCount, 1601 compareType); 1602 1603 if (i <= 0) { 1604 n = x.getRight(store); 1605 } else { 1606 result = x; 1607 n = x.getLeft(store); 1608 } 1609 1610 break; 1611 } 1612 case OpTypes.GREATER_EQUAL_PRE : 1613 case OpTypes.GREATER_EQUAL : { 1614 i = compareObject(session, currentRow.getData(), 1615 rowdata, rowColMap, fieldCount, 1616 compareType); 1617 1618 if (i < 0) { 1619 n = x.getRight(store); 1620 } else { 1621 result = x; 1622 n = x.getLeft(store); 1623 } 1624 1625 break; 1626 } 1627 case OpTypes.SMALLER : { 1628 i = compareObject(session, currentRow.getData(), 1629 rowdata, rowColMap, fieldCount, 1630 compareType); 1631 1632 if (i < 0) { 1633 result = x; 1634 n = x.getRight(store); 1635 } else { 1636 n = x.getLeft(store); 1637 } 1638 1639 break; 1640 } 1641 case OpTypes.SMALLER_EQUAL : { 1642 i = compareObject(session, currentRow.getData(), 1643 rowdata, rowColMap, fieldCount, 1644 compareType); 1645 1646 if (i <= 0) { 1647 result = x; 1648 n = x.getRight(store); 1649 } else { 1650 n = x.getLeft(store); 1651 } 1652 1653 break; 1654 } 1655 default : 1656 throw Error.runtimeError(ErrorCode.U_S0500, 1657 "Index"); 1658 } 1659 } else if (i < 0) { 1660 n = x.getRight(store); 1661 } else if (i > 0) { 1662 n = x.getLeft(store); 1663 } 1664 1665 if (n == null) { 1666 break; 1667 } 1668 1669 x = n; 1670 } 1671 1672 // MVCC 190 1673 if (session == null) { 1674 return result; 1675 } 1676 1677 while (result != null) { 1678 currentRow = result.getRow(store); 1679 1680 if (session.database.txManager.canRead(session, store, 1681 currentRow, readMode, 1682 colIndex)) { 1683 break; 1684 } 1685 1686 result = reversed ? last(store, result) 1687 : next(store, result); 1688 1689 if (result == null) { 1690 break; 1691 } 1692 1693 currentRow = result.getRow(store); 1694 1695 if (fieldCount > 0 1696 && compareRowNonUnique( 1697 session, currentRow.getData(), rowdata, rowColMap, 1698 fieldCount) != 0) { 1699 result = null; 1700 1701 break; 1702 } 1703 } 1704 1705 return result; 1706 } finally { 1707 store.readUnlock(); 1708 } 1709 } 1710 findDistinctNode(Session session, PersistentStore store, NodeAVL node, int fieldCount, boolean reversed)1711 NodeAVL findDistinctNode(Session session, PersistentStore store, 1712 NodeAVL node, int fieldCount, boolean reversed) { 1713 1714 store.readLock(); 1715 1716 try { 1717 NodeAVL x = getAccessor(store); 1718 NodeAVL n = null; 1719 NodeAVL result = null; 1720 Row currentRow = null; 1721 Object[] rowData = node.getData(store); 1722 1723 while (x != null) { 1724 currentRow = x.getRow(store); 1725 1726 int i = 0; 1727 1728 i = compareRowNonUnique(session, currentRow.getData(), 1729 rowData, colIndex, fieldCount); 1730 1731 if (reversed) { 1732 if (i < 0) { 1733 result = x; 1734 n = x.getRight(store); 1735 } else { 1736 n = x.getLeft(store); 1737 } 1738 } else { 1739 if (i <= 0) { 1740 n = x.getRight(store); 1741 } else { 1742 result = x; 1743 n = x.getLeft(store); 1744 } 1745 } 1746 1747 if (n == null) { 1748 break; 1749 } 1750 1751 x = n; 1752 } 1753 1754 // MVCC 190 1755 if (session == null) { 1756 return result; 1757 } 1758 1759 while (result != null) { 1760 currentRow = result.getRow(store); 1761 1762 if (session.database.txManager.canRead( 1763 session, store, currentRow, 1764 TransactionManager.ACTION_READ, colIndex)) { 1765 break; 1766 } 1767 1768 result = reversed ? last(store, result) 1769 : next(store, result); 1770 } 1771 1772 return result; 1773 } finally { 1774 store.readUnlock(); 1775 } 1776 } 1777 1778 /** 1779 * Balances part of the tree after an alteration to the index. 1780 */ balance(PersistentStore store, NodeAVL x, boolean isleft)1781 void balance(PersistentStore store, NodeAVL x, boolean isleft) { 1782 1783 while (true) { 1784 int sign = isleft ? 1 1785 : -1; 1786 1787 switch (x.getBalance(store) * sign) { 1788 1789 case 1 : 1790 x = x.setBalance(store, 0); 1791 1792 return; 1793 1794 case 0 : 1795 x = x.setBalance(store, -sign); 1796 break; 1797 1798 case -1 : 1799 NodeAVL l = x.child(store, isleft); 1800 1801 if (l.getBalance(store) == -sign) { 1802 x.replace(store, this, l); 1803 1804 x = x.set(store, isleft, l.child(store, !isleft)); 1805 l = l.set(store, !isleft, x); 1806 x = x.setBalance(store, 0); 1807 l = l.setBalance(store, 0); 1808 } else { 1809 NodeAVL r = l.child(store, !isleft); 1810 1811 x.replace(store, this, r); 1812 1813 l = l.set(store, !isleft, r.child(store, isleft)); 1814 r = r.set(store, isleft, l); 1815 x = x.set(store, isleft, r.child(store, !isleft)); 1816 r = r.set(store, !isleft, x); 1817 1818 int rb = r.getBalance(store); 1819 1820 x = x.setBalance(store, (rb == -sign) ? sign 1821 : 0); 1822 l = l.setBalance(store, (rb == sign) ? -sign 1823 : 0); 1824 r = r.setBalance(store, 0); 1825 } 1826 1827 return; 1828 } 1829 1830 if (x.isRoot(store)) { 1831 return; 1832 } 1833 1834 isleft = x.isFromLeft(store); 1835 x = x.getParent(store); 1836 } 1837 } 1838 getAccessor(PersistentStore store)1839 NodeAVL getAccessor(PersistentStore store) { 1840 1841 NodeAVL node = (NodeAVL) store.getAccessor(this); 1842 1843 return node; 1844 } 1845 getIterator(Session session, PersistentStore store, NodeAVL x, boolean single, boolean reversed)1846 IndexRowIterator getIterator(Session session, PersistentStore store, 1847 NodeAVL x, boolean single, boolean reversed) { 1848 1849 if (x == null) { 1850 return emptyIterator; 1851 } else { 1852 IndexRowIterator it = new IndexRowIterator(session, store, this, 1853 x, 0, single, reversed); 1854 1855 return it; 1856 } 1857 } 1858 1859 public static final class IndexRowIterator implements RowIterator { 1860 1861 final Session session; 1862 final PersistentStore store; 1863 final IndexAVL index; 1864 NodeAVL nextnode; 1865 Row lastrow; 1866 int distinctCount; 1867 boolean single; 1868 boolean reversed; 1869 1870 /** 1871 * When session == null, rows from all sessions are returned 1872 */ IndexRowIterator(Session session, PersistentStore store, IndexAVL index, NodeAVL node, int distinctCount, boolean single, boolean reversed)1873 public IndexRowIterator(Session session, PersistentStore store, 1874 IndexAVL index, NodeAVL node, 1875 int distinctCount, boolean single, 1876 boolean reversed) { 1877 1878 this.session = session; 1879 this.store = store; 1880 this.index = index; 1881 this.distinctCount = distinctCount; 1882 this.single = single; 1883 this.reversed = reversed; 1884 1885 if (index == null) { 1886 return; 1887 } 1888 1889 nextnode = node; 1890 } 1891 hasNext()1892 public boolean hasNext() { 1893 return nextnode != null; 1894 } 1895 getNextRow()1896 public Row getNextRow() { 1897 1898 if (nextnode == null) { 1899 release(); 1900 1901 return null; 1902 } 1903 1904 NodeAVL lastnode = nextnode; 1905 1906 if (single) { 1907 nextnode = null; 1908 } else { 1909 store.readLock(); 1910 1911 try { 1912 while (true) { 1913 if (reversed) { 1914 nextnode = index.last(session, store, nextnode, 1915 distinctCount); 1916 } else { 1917 nextnode = index.next(session, store, nextnode, 1918 distinctCount); 1919 } 1920 1921 if (nextnode == null) { 1922 break; 1923 } 1924 1925 Row row = nextnode.getRow(store); 1926 1927 if (session == null 1928 || store.canRead( 1929 session, row, 1930 TransactionManager.ACTION_READ, null)) { 1931 break; 1932 } 1933 } 1934 } finally { 1935 store.readUnlock(); 1936 } 1937 } 1938 1939 lastrow = lastnode.getRow(store); 1940 1941 return lastrow; 1942 } 1943 getNext()1944 public Object[] getNext() { 1945 1946 Row row = getNextRow(); 1947 1948 return row == null ? null 1949 : row.getData(); 1950 } 1951 removeCurrent()1952 public void removeCurrent() { 1953 store.delete(session, lastrow); 1954 store.remove(lastrow); 1955 } 1956 release()1957 public void release() {} 1958 getRowId()1959 public long getRowId() { 1960 return nextnode.getPos(); 1961 } 1962 getCurrentTable()1963 public TableBase getCurrentTable() { 1964 return index.table; 1965 } 1966 } 1967 } 1968