1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl;
18
19 import java.io.BufferedWriter;
20 import java.io.IOException;
21 import java.io.StringWriter;
22 import java.nio.BufferOverflowException;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.Charset;
25 import java.time.LocalDateTime;
26 import java.util.AbstractMap;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeSet;
39
40 import com.healthmarketscience.jackcess.BatchUpdateException;
41 import com.healthmarketscience.jackcess.Column;
42 import com.healthmarketscience.jackcess.ColumnBuilder;
43 import com.healthmarketscience.jackcess.ConstraintViolationException;
44 import com.healthmarketscience.jackcess.CursorBuilder;
45 import com.healthmarketscience.jackcess.Index;
46 import com.healthmarketscience.jackcess.IndexBuilder;
47 import com.healthmarketscience.jackcess.InvalidValueException;
48 import com.healthmarketscience.jackcess.JackcessException;
49 import com.healthmarketscience.jackcess.PropertyMap;
50 import com.healthmarketscience.jackcess.Row;
51 import com.healthmarketscience.jackcess.RowId;
52 import com.healthmarketscience.jackcess.Table;
53 import com.healthmarketscience.jackcess.expr.Identifier;
54 import com.healthmarketscience.jackcess.util.ErrorHandler;
55 import com.healthmarketscience.jackcess.util.ExportUtil;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58
59
60
61
62
63
64
65
66
67 public class TableImpl implements Table, PropertyMaps.Owner
68 {
69 private static final Log LOG = LogFactory.getLog(TableImpl.class);
70
71 private static final short OFFSET_MASK = (short)0x1FFF;
72
73 private static final short DELETED_ROW_MASK = (short)0x8000;
74
75 private static final short OVERFLOW_ROW_MASK = (short)0x4000;
76
77 static final int MAGIC_TABLE_NUMBER = 1625;
78
79 private static final int MAX_BYTE = 256;
80
81
82
83
84
85 public static final byte TYPE_SYSTEM = 0x53;
86
87
88
89
90 public static final byte TYPE_USER = 0x4e;
91
92 public enum IndexFeature {
93 EXACT_MATCH, EXACT_UNIQUE_ONLY, ANY_MATCH;
94 }
95
96
97
98 private static final Comparator<ColumnImpl> VAR_LEN_COLUMN_COMPARATOR =
99 new Comparator<ColumnImpl>() {
100 @Override
101 public int compare(ColumnImpl="../../../../com/healthmarketscience/jackcess/impl/ColumnImpl.html#ColumnImpl">ColumnImpl c1, ColumnImpl c2) {
102 return ((c1.getVarLenTableIndex() < c2.getVarLenTableIndex()) ? -1 :
103 ((c1.getVarLenTableIndex() > c2.getVarLenTableIndex()) ? 1 :
104 0));
105 }
106 };
107
108
109 private static final Comparator<ColumnImpl> DISPLAY_ORDER_COMPARATOR =
110 new Comparator<ColumnImpl>() {
111 @Override
112 public int compare(ColumnImpl="../../../../com/healthmarketscience/jackcess/impl/ColumnImpl.html#ColumnImpl">ColumnImpl c1, ColumnImpl c2) {
113 return ((c1.getDisplayIndex() < c2.getDisplayIndex()) ? -1 :
114 ((c1.getDisplayIndex() > c2.getDisplayIndex()) ? 1 :
115 0));
116 }
117 };
118
119
120 private final DatabaseImpl _database;
121
122 private final int _flags;
123
124 private final byte _tableType;
125
126 private int _indexCount;
127
128 private int _logicalIndexCount;
129
130 private final int _tableDefPageNumber;
131
132 private short _maxColumnCount;
133
134 private short _maxVarColumnCount;
135
136 private final List<ColumnImpl> _columns = new ArrayList<ColumnImpl>();
137
138 private final List<ColumnImpl> _varColumns = new ArrayList<ColumnImpl>();
139
140 private final List<ColumnImpl> _autoNumColumns = new ArrayList<ColumnImpl>(1);
141
142 private final CalcColEvaluator _calcColEval = new CalcColEvaluator();
143
144
145 private final List<IndexImpl> _indexes = new ArrayList<IndexImpl>();
146
147
148 private final List<IndexData> _indexDatas = new ArrayList<IndexData>();
149
150 private final Set<ColumnImpl> _indexColumns = new LinkedHashSet<ColumnImpl>();
151
152 private final String _name;
153
154 private final UsageMap _ownedPages;
155
156 private final UsageMap _freeSpacePages;
157
158 private int _rowCount;
159
160 private int _lastLongAutoNumber;
161
162 private int _lastComplexTypeAutoNumber;
163
164 private int _modCount;
165
166 private final TempPageHolder _addRowBufferH =
167 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
168
169 private final TempPageHolder _tableDefBufferH =
170 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
171
172 private final TempBufferHolder _writeRowBufferH =
173 TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
174
175 private final TempPageHolder _longValueBufferH =
176 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
177
178 private ErrorHandler _tableErrorHandler;
179
180 private PropertyMap _props;
181
182 private PropertyMaps _propertyMaps;
183
184
185 private Boolean _allowAutoNumInsert;
186
187 private final FKEnforcer _fkEnforcer;
188
189 private RowValidatorEvalContext _rowValidator;
190
191
192
193 private CursorImpl _defaultCursor;
194
195
196
197
198
199 protected TableImpl(boolean testing, List<ColumnImpl> columns)
200 {
201 if(!testing) {
202 throw new IllegalArgumentException();
203 }
204 _database = null;
205 _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
206 _name = null;
207
208 _columns.addAll(columns);
209 for(ColumnImpl col : _columns) {
210 if(col.getType().isVariableLength()) {
211 _varColumns.add(col);
212 }
213 }
214 _maxColumnCount = (short)_columns.size();
215 _maxVarColumnCount = (short)_varColumns.size();
216 initAutoNumberColumns();
217
218 _fkEnforcer = null;
219 _flags = 0;
220 _tableType = TYPE_USER;
221 _indexCount = 0;
222 _logicalIndexCount = 0;
223 _ownedPages = null;
224 _freeSpacePages = null;
225 }
226
227
228
229
230
231
232
233 protected TableImpl(DatabaseImpl database, ByteBuffer tableBuffer,
234 int pageNumber, String name, int flags)
235 throws IOException
236 {
237 _database = database;
238 _tableDefPageNumber = pageNumber;
239 _name = name;
240 _flags = flags;
241
242
243 tableBuffer = loadCompleteTableDefinitionBuffer(tableBuffer, null);
244
245 _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
246 _lastLongAutoNumber = tableBuffer.getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
247 if(getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER >= 0) {
248 _lastComplexTypeAutoNumber = tableBuffer.getInt(
249 getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER);
250 }
251 _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
252 _maxColumnCount = tableBuffer.getShort(getFormat().OFFSET_MAX_COLS);
253 _maxVarColumnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_VAR_COLS);
254 short columnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_COLS);
255 _logicalIndexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
256 _indexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEXES);
257
258 tableBuffer.position(getFormat().OFFSET_OWNED_PAGES);
259 _ownedPages = UsageMap.read(getDatabase(), tableBuffer);
260 tableBuffer.position(getFormat().OFFSET_FREE_SPACE_PAGES);
261 _freeSpacePages = UsageMap.read(getDatabase(), tableBuffer);
262
263 for (int i = 0; i < _indexCount; i++) {
264 _indexDatas.add(IndexData.create(this, tableBuffer, i, getFormat()));
265 }
266
267 readColumnDefinitions(tableBuffer, columnCount);
268
269 readIndexDefinitions(tableBuffer);
270
271
272 while((tableBuffer.remaining() >= 2) &&
273 readColumnUsageMaps(tableBuffer)) {
274
275 }
276
277
278 if(getDatabase().getColumnOrder() != ColumnOrder.DATA) {
279 Collections.sort(_columns, DISPLAY_ORDER_COMPARATOR);
280 }
281
282 for(ColumnImpl col : _columns) {
283
284
285 col.postTableLoadInit();
286 }
287
288 _fkEnforcer = new FKEnforcer(this);
289
290 if(!isSystem()) {
291
292
293 for(ColumnImpl col : _columns) {
294 col.initColumnValidator();
295 }
296
297 reloadRowValidator();
298 }
299 }
300
301 private void reloadRowValidator() throws IOException {
302
303
304 _rowValidator = null;
305
306 if(!getDatabase().isEvaluateExpressions()) {
307 return;
308 }
309
310 PropertyMap props = getProperties();
311
312 String exprStr = PropertyMaps.getTrimmedStringProperty(
313 props, PropertyMap.VALIDATION_RULE_PROP);
314
315 if(exprStr != null) {
316 String helpStr = PropertyMaps.getTrimmedStringProperty(
317 props, PropertyMap.VALIDATION_TEXT_PROP);
318
319 _rowValidator = new RowValidatorEvalContext(this)
320 .setExpr(exprStr, helpStr);
321 }
322 }
323
324 @Override
325 public String getName() {
326 return _name;
327 }
328
329 @Override
330 public boolean isHidden() {
331 return((_flags & DatabaseImpl.HIDDEN_OBJECT_FLAG) != 0);
332 }
333
334 @Override
335 public boolean isSystem() {
336 return(_tableType != TYPE_USER);
337 }
338
339
340
341
342 public int getMaxColumnCount() {
343 return _maxColumnCount;
344 }
345
346 @Override
347 public int getColumnCount() {
348 return _columns.size();
349 }
350
351 @Override
352 public DatabaseImpl getDatabase() {
353 return _database;
354 }
355
356
357
358
359 public JetFormat getFormat() {
360 return getDatabase().getFormat();
361 }
362
363
364
365
366 public PageChannel getPageChannel() {
367 return getDatabase().getPageChannel();
368 }
369
370 @Override
371 public ErrorHandler getErrorHandler() {
372 return((_tableErrorHandler != null) ? _tableErrorHandler :
373 getDatabase().getErrorHandler());
374 }
375
376 @Override
377 public void setErrorHandler(ErrorHandler newErrorHandler) {
378 _tableErrorHandler = newErrorHandler;
379 }
380
381 public int getTableDefPageNumber() {
382 return _tableDefPageNumber;
383 }
384
385 @Override
386 public boolean isAllowAutoNumberInsert() {
387 return ((_allowAutoNumInsert != null) ? (boolean)_allowAutoNumInsert :
388 getDatabase().isAllowAutoNumberInsert());
389 }
390
391 @Override
392 public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert) {
393 _allowAutoNumInsert = allowAutoNumInsert;
394 }
395
396
397
398
399 public RowState createRowState() {
400 return new RowState(TempBufferHolder.Type.HARD);
401 }
402
403
404
405
406 public UsageMap.PageCursor getOwnedPagesCursor() {
407 return _ownedPages.cursor();
408 }
409
410
411
412
413
414
415
416
417
418
419
420
421
422 public int getApproximateOwnedPageCount() {
423
424
425
426 int count = _ownedPages.getPageCount() + 1;
427
428 for(ColumnImpl col : _columns) {
429 count += col.getOwnedPageCount();
430 }
431
432
433
434 for(IndexData indexData : _indexDatas) {
435 count += indexData.getOwnedPageCount();
436 }
437
438 return count;
439 }
440
441 protected TempPageHolder getLongValueBuffer() {
442 return _longValueBufferH;
443 }
444
445 @Override
446 public List<ColumnImpl> getColumns() {
447 return Collections.unmodifiableList(_columns);
448 }
449
450 @Override
451 public ColumnImpl getColumn(String name) {
452 for(ColumnImpl column : _columns) {
453 if(column.getName().equalsIgnoreCase(name)) {
454 return column;
455 }
456 }
457 throw new IllegalArgumentException(withErrorContext(
458 "Column with name " + name + " does not exist in this table"));
459 }
460
461 public boolean hasColumn(String name) {
462 for(ColumnImpl column : _columns) {
463 if(column.getName().equalsIgnoreCase(name)) {
464 return true;
465 }
466 }
467 return false;
468 }
469
470 @Override
471 public PropertyMap getProperties() throws IOException {
472 if(_props == null) {
473 _props = getPropertyMaps().getDefault();
474 }
475 return _props;
476 }
477
478 @Override
479 public LocalDateTime getCreatedDate() throws IOException {
480 return getDatabase().getCreateDateForObject(_tableDefPageNumber);
481 }
482
483 @Override
484 public LocalDateTime getUpdatedDate() throws IOException {
485 return getDatabase().getUpdateDateForObject(_tableDefPageNumber);
486 }
487
488
489
490
491
492 public PropertyMaps getPropertyMaps() throws IOException {
493 if(_propertyMaps == null) {
494 _propertyMaps = getDatabase().getPropertiesForObject(
495 _tableDefPageNumber, this);
496 }
497 return _propertyMaps;
498 }
499
500 @Override
501 public void propertiesUpdated() throws IOException {
502
503 for(ColumnImpl col : _columns) {
504 col.propertiesUpdated();
505 }
506
507 reloadRowValidator();
508
509
510
511 _calcColEval.reSort();
512 }
513
514 @Override
515 public List<IndexImpl> getIndexes() {
516 return Collections.unmodifiableList(_indexes);
517 }
518
519 @Override
520 public IndexImpl getIndex(String name) {
521 for(IndexImpl index : _indexes) {
522 if(index.getName().equalsIgnoreCase(name)) {
523 return index;
524 }
525 }
526 throw new IllegalArgumentException(withErrorContext(
527 "Index with name " + name + " does not exist on this table"));
528 }
529
530 @Override
531 public IndexImpl getPrimaryKeyIndex() {
532 for(IndexImpl index : _indexes) {
533 if(index.isPrimaryKey()) {
534 return index;
535 }
536 }
537 throw new IllegalArgumentException(withErrorContext(
538 "No primary key index found"));
539 }
540
541 @Override
542 public IndexImpl getForeignKeyIndex(Table otherTable) {
543 for(IndexImpl index : _indexes) {
544 if(index.isForeignKey() && (index.getReference() != null) &&
545 (index.getReference().getOtherTablePageNumber() ==
546 ((TableImpl)otherTable).getTableDefPageNumber())) {
547 return index;
548 }
549 }
550 throw new IllegalArgumentException(withErrorContext(
551 "No foreign key reference to " +
552 otherTable.getName() + " found"));
553 }
554
555
556
557
558
559 public List<IndexData> getIndexDatas() {
560 return Collections.unmodifiableList(_indexDatas);
561 }
562
563
564
565
566
567 public int getLogicalIndexCount() {
568 return _logicalIndexCount;
569 }
570
571 int getIndexCount() {
572 return _indexCount;
573 }
574
575 public IndexImpl findIndexForColumns(Collection<String> searchColumns,
576 IndexFeature feature) {
577
578 IndexImpl partialIndex = null;
579 for(IndexImpl index : _indexes) {
580
581 Collection<? extends Index.Column> indexColumns = index.getColumns();
582 if(indexColumns.size() < searchColumns.size()) {
583 continue;
584 }
585 boolean exactMatch = (indexColumns.size() == searchColumns.size());
586
587 Iterator<String> sIter = searchColumns.iterator();
588 Iterator<? extends Index.Column> iIter = indexColumns.iterator();
589 boolean searchMatches = true;
590 while(sIter.hasNext()) {
591 String sColName = sIter.next();
592 String iColName = iIter.next().getName();
593 if((sColName != iColName) &&
594 ((sColName == null) || !sColName.equalsIgnoreCase(iColName))) {
595 searchMatches = false;
596 break;
597 }
598 }
599
600 if(searchMatches) {
601
602 if(exactMatch && ((feature != IndexFeature.EXACT_UNIQUE_ONLY) ||
603 index.isUnique())) {
604 return index;
605 }
606
607 if(!exactMatch && (feature == IndexFeature.ANY_MATCH) &&
608 ((partialIndex == null) ||
609 (indexColumns.size() < partialIndex.getColumnCount()))) {
610
611 partialIndex = index;
612 }
613 }
614 }
615
616 return partialIndex;
617 }
618
619 List<ColumnImpl> getAutoNumberColumns() {
620 return _autoNumColumns;
621 }
622
623 @Override
624 public CursorImpl getDefaultCursor() {
625 if(_defaultCursor == null) {
626 _defaultCursor = CursorImpl.createCursor(this);
627 }
628 return _defaultCursor;
629 }
630
631 @Override
632 public CursorBuilder newCursor() {
633 return new CursorBuilder(this);
634 }
635
636 @Override
637 public void reset() {
638 getDefaultCursor().reset();
639 }
640
641 @Override
642 public Rowf="../../../../com/healthmarketscience/jackcess/Row.html#Row">Row deleteRow(Row row) throws IOException {
643 deleteRow(row.getId());
644 return row;
645 }
646
647
648
649
650
651
652
653
654 public RowId"../../../../com/healthmarketscience/jackcess/RowId.html#RowId">RowId deleteRow(RowId rowId) throws IOException {
655 deleteRow(getDefaultCursor().getRowState(), (RowIdImpl)rowId);
656 return rowId;
657 }
658
659
660
661
662
663 public void deleteRow(RowState rowState, RowIdImpl rowId)
664 throws IOException
665 {
666 requireValidRowId(rowId);
667
668 getPageChannel().startWrite();
669 try {
670
671
672 ByteBuffer rowBuffer = positionAtRowHeader(rowState, rowId);
673
674 if(rowState.isDeleted()) {
675
676 return;
677 }
678 requireNonDeletedRow(rowState, rowId);
679
680
681
682 int pageNumber = rowState.getHeaderRowId().getPageNumber();
683 int rowNumber = rowState.getHeaderRowId().getRowNumber();
684
685
686 Object[] rowValues = null;
687 if(!_indexDatas.isEmpty()) {
688
689
690 rowBuffer = positionAtRowData(rowState, rowId);
691
692 for(ColumnImpl idxCol : _indexColumns) {
693 getRowColumn(getFormat(), rowBuffer, idxCol, rowState, null);
694 }
695
696
697 rowValues = rowState.getRowCacheValues();
698
699
700 _fkEnforcer.deleteRow(rowValues);
701
702
703 rowBuffer = positionAtRowHeader(rowState, rowId);
704 }
705
706
707 int rowIndex = getRowStartOffset(rowNumber, getFormat());
708 rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
709 | DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
710 writeDataPage(rowBuffer, pageNumber);
711
712
713 for(IndexData indexData : _indexDatas) {
714 indexData.deleteRow(rowValues, rowId);
715 }
716
717
718 updateTableDefinition(-1);
719
720 } finally {
721 getPageChannel().finishWrite();
722 }
723 }
724
725 @Override
726 public Row getNextRow() throws IOException {
727 return getDefaultCursor().getNextRow();
728 }
729
730
731
732
733
734 public Object getRowValue(RowState rowState, RowIdImpl rowId,
735 ColumnImpl column)
736 throws IOException
737 {
738 if(this != column.getTable()) {
739 throw new IllegalArgumentException(withErrorContext(
740 "Given column " + column + " is not from this table"));
741 }
742 requireValidRowId(rowId);
743
744
745 ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
746 requireNonDeletedRow(rowState, rowId);
747
748 return getRowColumn(getFormat(), rowBuffer, column, rowState, null);
749 }
750
751
752
753
754
755
756 public RowImpl getRow(
757 RowState rowState, RowIdImpl rowId, Collection<String> columnNames)
758 throws IOException
759 {
760 requireValidRowId(rowId);
761
762
763 ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
764 requireNonDeletedRow(rowState, rowId);
765
766 return getRow(getFormat(), rowState, rowBuffer, _columns, columnNames);
767 }
768
769
770
771
772
773 private static RowImpl getRow(
774 JetFormat format,
775 RowState rowState,
776 ByteBuffer rowBuffer,
777 Collection<ColumnImpl> columns,
778 Collection<String> columnNames)
779 throws IOException
780 {
781 RowImplckcess/impl/RowImpl.html#RowImpl">RowImpl rtn = new RowImpl(rowState.getHeaderRowId(), columns.size());
782 for(ColumnImpl column : columns) {
783
784 if((columnNames == null) || (columnNames.contains(column.getName()))) {
785
786 column.setRowValue(
787 rtn, getRowColumn(format, rowBuffer, column, rowState, null));
788 }
789 }
790 return rtn;
791 }
792
793
794
795
796
797 private static Object getRowColumn(JetFormat format,
798 ByteBuffer rowBuffer,
799 ColumnImpl column,
800 RowState rowState,
801 Map<ColumnImpl,byte[]> rawVarValues)
802 throws IOException
803 {
804 byte[] columnData = null;
805 try {
806
807 NullMask nullMask = rowState.getNullMask(rowBuffer);
808 boolean isNull = nullMask.isNull(column);
809 if(column.storeInNullMask()) {
810
811
812 return rowState.setRowCacheValue(column.getColumnIndex(),
813 column.readFromNullMask(isNull));
814 } else if(isNull) {
815
816 return null;
817 }
818
819 Object cachedValue = rowState.getRowCacheValue(column.getColumnIndex());
820 if(cachedValue != null) {
821
822 return cachedValue;
823 }
824
825
826 rowBuffer.reset();
827
828
829 int rowStart = rowBuffer.position();
830 int colDataPos = 0;
831 int colDataLen = 0;
832 if(!column.isVariableLength()) {
833
834
835 int dataStart = rowStart + format.OFFSET_COLUMN_FIXED_DATA_ROW_OFFSET;
836 colDataPos = dataStart + column.getFixedDataOffset();
837 colDataLen = column.getFixedDataSize();
838
839 } else {
840 int varDataStart;
841 int varDataEnd;
842
843 if(format.SIZE_ROW_VAR_COL_OFFSET == 2) {
844
845
846 int varColumnOffsetPos =
847 (rowBuffer.limit() - nullMask.byteSize() - 4) -
848 (column.getVarLenTableIndex() * 2);
849
850 varDataStart = rowBuffer.getShort(varColumnOffsetPos);
851 varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
852
853 } else {
854
855
856 short[] varColumnOffsets = readJumpTableVarColOffsets(
857 rowState, rowBuffer, rowStart, nullMask);
858
859 varDataStart = varColumnOffsets[column.getVarLenTableIndex()];
860 varDataEnd = varColumnOffsets[column.getVarLenTableIndex() + 1];
861 }
862
863 colDataPos = rowStart + varDataStart;
864 colDataLen = varDataEnd - varDataStart;
865 }
866
867
868 rowBuffer.position(colDataPos);
869 columnData = ByteUtil.getBytes(rowBuffer, colDataLen);
870
871 if((rawVarValues != null) && column.isVariableLength()) {
872
873 rawVarValues.put(column, columnData);
874 }
875
876
877
878
879
880 return rowState.setRowCacheValue(column.getColumnIndex(),
881 column.read(columnData));
882
883 } catch(Exception e) {
884
885
886 rowState.setRowCacheValue(column.getColumnIndex(),
887 ColumnImpl.rawDataWrapper(columnData));
888
889 return rowState.handleRowError(column, columnData, e);
890 }
891 }
892
893 private static short[] readJumpTableVarColOffsets(
894 RowState rowState, ByteBuffer rowBuffer, int rowStart,
895 NullMask nullMask)
896 {
897 short[] varColOffsets = rowState.getVarColOffsets();
898 if(varColOffsets != null) {
899 return varColOffsets;
900 }
901
902
903 int nullMaskSize = nullMask.byteSize();
904 int rowEnd = rowStart + rowBuffer.remaining() - 1;
905 int numVarCols = ByteUtil.getUnsignedByte(rowBuffer,
906 rowEnd - nullMaskSize);
907 varColOffsets = new short[numVarCols + 1];
908
909 int rowLen = rowEnd - rowStart + 1;
910 int numJumps = (rowLen - 1) / MAX_BYTE;
911 int colOffset = rowEnd - nullMaskSize - numJumps - 1;
912
913
914 if(((colOffset - rowStart - numVarCols) / MAX_BYTE) < numJumps) {
915 numJumps--;
916 }
917
918 int jumpsUsed = 0;
919 for(int i = 0; i < numVarCols + 1; i++) {
920
921 while((jumpsUsed < numJumps) &&
922 (i == ByteUtil.getUnsignedByte(
923 rowBuffer, rowEnd - nullMaskSize-jumpsUsed - 1))) {
924 jumpsUsed++;
925 }
926
927 varColOffsets[i] = (short)
928 (ByteUtil.getUnsignedByte(rowBuffer, colOffset - i)
929 + (jumpsUsed * MAX_BYTE));
930 }
931
932 rowState.setVarColOffsets(varColOffsets);
933 return varColOffsets;
934 }
935
936
937
938
939 private NullMask getRowNullMask(ByteBuffer rowBuffer)
940 {
941
942 rowBuffer.reset();
943
944
945 int columnCount = ByteUtil.getUnsignedVarInt(
946 rowBuffer, getFormat().SIZE_ROW_COLUMN_COUNT);
947
948
949 NullMask/impl/NullMask.html#NullMask">NullMask nullMask = new NullMask(columnCount);
950 rowBuffer.position(rowBuffer.limit() - nullMask.byteSize());
951 nullMask.read(rowBuffer);
952
953 return nullMask;
954 }
955
956
957
958
959
960
961
962
963
964 public static ByteBuffer positionAtRowHeader(RowState rowState,
965 RowIdImpl rowId)
966 throws IOException
967 {
968 ByteBuffer rowBuffer = rowState.setHeaderRow(rowId);
969
970 if(rowState.isAtHeaderRow()) {
971
972 return rowBuffer;
973 }
974
975 if(!rowState.isValid()) {
976
977 rowState.setStatus(RowStateStatus.AT_HEADER);
978 return null;
979 }
980
981
982 short rowStart = rowBuffer.getShort(
983 getRowStartOffset(rowId.getRowNumber(),
984 rowState.getTable().getFormat()));
985
986
987
988 RowStatus rowStatus = RowStatus.NORMAL;
989 if(isDeletedRow(rowStart)) {
990 rowStatus = RowStatus.DELETED;
991 } else if(isOverflowRow(rowStart)) {
992 rowStatus = RowStatus.OVERFLOW;
993 }
994
995 rowState.setRowStatus(rowStatus);
996 rowState.setStatus(RowStateStatus.AT_HEADER);
997 return rowBuffer;
998 }
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009 public static ByteBuffer positionAtRowData(RowState rowState,
1010 RowIdImpl rowId)
1011 throws IOException
1012 {
1013 positionAtRowHeader(rowState, rowId);
1014 if(!rowState.isValid() || rowState.isDeleted()) {
1015
1016 rowState.setStatus(RowStateStatus.AT_FINAL);
1017 return null;
1018 }
1019
1020 ByteBuffer rowBuffer = rowState.getFinalPage();
1021 int rowNum = rowState.getFinalRowId().getRowNumber();
1022 JetFormat format = rowState.getTable().getFormat();
1023
1024 if(rowState.isAtFinalRow()) {
1025
1026 return PageChannel.narrowBuffer(
1027 rowBuffer,
1028 findRowStart(rowBuffer, rowNum, format),
1029 findRowEnd(rowBuffer, rowNum, format));
1030 }
1031
1032 while(true) {
1033
1034
1035 short rowStart = rowBuffer.getShort(getRowStartOffset(rowNum, format));
1036 short rowEnd = findRowEnd(rowBuffer, rowNum, format);
1037
1038
1039
1040
1041 boolean overflowRow = isOverflowRow(rowStart);
1042
1043
1044 rowStart = (short)(rowStart & OFFSET_MASK);
1045
1046 if (overflowRow) {
1047
1048 if((rowEnd - rowStart) < 4) {
1049 throw new IOException(rowState.getTable().withErrorContext(
1050 "invalid overflow row info"));
1051 }
1052
1053
1054
1055 int overflowRowNum = ByteUtil.getUnsignedByte(rowBuffer, rowStart);
1056 int overflowPageNum = ByteUtil.get3ByteInt(rowBuffer, rowStart + 1);
1057 rowBuffer = rowState.setOverflowRow(
1058 new RowIdImpl(overflowPageNum, overflowRowNum));
1059 rowNum = overflowRowNum;
1060
1061 } else {
1062
1063 rowState.setStatus(RowStateStatus.AT_FINAL);
1064 return PageChannel.narrowBuffer(rowBuffer, rowStart, rowEnd);
1065 }
1066 }
1067 }
1068
1069 @Override
1070 public Iterator<Row> iterator() {
1071 return getDefaultCursor().iterator();
1072 }
1073
1074
1075
1076
1077
1078 protected static void writeTableDefinition(TableCreator creator)
1079 throws IOException
1080 {
1081
1082 createUsageMapDefinitionBuffer(creator);
1083
1084
1085
1086 JetFormat format = creator.getFormat();
1087 int idxDataLen = (creator.getIndexCount() *
1088 (format.SIZE_INDEX_DEFINITION +
1089 format.SIZE_INDEX_COLUMN_BLOCK)) +
1090 (creator.getLogicalIndexCount() * format.SIZE_INDEX_INFO_BLOCK);
1091 int colUmapLen = creator.getLongValueColumns().size() * 10;
1092 int totalTableDefSize = format.SIZE_TDEF_HEADER +
1093 (format.SIZE_COLUMN_DEF_BLOCK * creator.getColumns().size()) +
1094 idxDataLen + colUmapLen + format.SIZE_TDEF_TRAILER;
1095
1096
1097
1098 for(ColumnBuilder col : creator.getColumns()) {
1099 totalTableDefSize += DBMutator.calculateNameLength(col.getName());
1100 }
1101
1102 for(IndexBuilder idx : creator.getIndexes()) {
1103 totalTableDefSize += DBMutator.calculateNameLength(idx.getName());
1104 }
1105
1106
1107
1108 ByteBuffer buffer = PageChannel.createBuffer(Math.max(totalTableDefSize,
1109 format.PAGE_SIZE));
1110 writeTableDefinitionHeader(creator, buffer, totalTableDefSize);
1111
1112 if(creator.hasIndexes()) {
1113
1114 IndexData.writeRowCountDefinitions(creator, buffer);
1115 }
1116
1117
1118 ColumnImpl.writeDefinitions(creator, buffer);
1119
1120 if(creator.hasIndexes()) {
1121
1122 IndexData.writeDefinitions(creator, buffer);
1123 IndexImpl.writeDefinitions(creator, buffer);
1124 }
1125
1126
1127 ColumnImpl.writeColUsageMapDefinitions(creator, buffer);
1128
1129
1130 buffer.put((byte) 0xff);
1131 buffer.put((byte) 0xff);
1132 buffer.flip();
1133
1134
1135 writeTableDefinitionBuffer(buffer, creator.getTdefPageNumber(), creator,
1136 Collections.<Integer>emptyList());
1137 }
1138
1139 private static void writeTableDefinitionBuffer(
1140 ByteBuffer buffer, int tdefPageNumber,
1141 TableMutator mutator, List<Integer> reservedPages)
1142 throws IOException
1143 {
1144 buffer.rewind();
1145 int totalTableDefSize = buffer.remaining();
1146 JetFormat format = mutator.getFormat();
1147 PageChannel pageChannel = mutator.getPageChannel();
1148
1149
1150 if(totalTableDefSize <= format.PAGE_SIZE) {
1151
1152
1153
1154
1155 buffer.putShort(format.OFFSET_FREE_SPACE,
1156 (short)(Math.max(
1157 format.PAGE_SIZE - totalTableDefSize - 8, 0)));
1158
1159 buffer.clear();
1160 pageChannel.writePage(buffer, tdefPageNumber);
1161
1162 } else {
1163
1164
1165
1166 ByteBuffer partialTdef = pageChannel.createPageBuffer();
1167 buffer.rewind();
1168 int nextTdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
1169 while(buffer.hasRemaining()) {
1170
1171
1172 partialTdef.clear();
1173
1174 if(nextTdefPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
1175
1176
1177
1178 nextTdefPageNumber = tdefPageNumber;
1179
1180 } else {
1181
1182
1183 writeTablePageHeader(partialTdef);
1184 }
1185
1186
1187 int curTdefPageNumber = nextTdefPageNumber;
1188 int writeLen = Math.min(partialTdef.remaining(), buffer.remaining());
1189 partialTdef.put(buffer.array(), buffer.position(), writeLen);
1190 ByteUtil.forward(buffer, writeLen);
1191
1192 if(buffer.hasRemaining()) {
1193
1194 if(reservedPages.isEmpty()) {
1195 nextTdefPageNumber = pageChannel.allocateNewPage();
1196 } else {
1197 nextTdefPageNumber = reservedPages.remove(0);
1198 }
1199 partialTdef.putInt(format.OFFSET_NEXT_TABLE_DEF_PAGE,
1200 nextTdefPageNumber);
1201 }
1202
1203
1204 partialTdef.putShort(format.OFFSET_FREE_SPACE,
1205 (short)(Math.max(
1206 partialTdef.remaining() - 8, 0)));
1207
1208
1209 pageChannel.writePage(partialTdef, curTdefPageNumber);
1210 }
1211
1212 }
1213
1214 }
1215
1216
1217
1218
1219
1220 protected ColumnImpl mutateAddColumn(TableUpdater mutator) throws IOException
1221 {
1222 ColumnBuilder column = mutator.getColumn();
1223 JetFormat format = mutator.getFormat();
1224 boolean isVarCol = column.isVariableLength();
1225 boolean isLongVal = column.getType().isLongValue();
1226
1227
1228
1229 if(isLongVal) {
1230 mutator.addTdefLen(10);
1231 }
1232
1233 mutator.addTdefLen(format.SIZE_COLUMN_DEF_BLOCK);
1234
1235 int nameByteLen = DBMutator.calculateNameLength(column.getName());
1236 mutator.addTdefLen(nameByteLen);
1237
1238
1239
1240 ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
1241 mutator);
1242
1243 ColumnImpl newCol = null;
1244 int umapPos = -1;
1245 boolean success = false;
1246 try {
1247
1248
1249
1250 ByteUtil.forward(tableBuffer, 29);
1251 tableBuffer.putShort((short)(_maxColumnCount + 1));
1252 short varColCount = (short)(_varColumns.size() + (isVarCol ? 1 : 0));
1253 tableBuffer.putShort(varColCount);
1254 tableBuffer.putShort((short)(_columns.size() + 1));
1255
1256
1257 tableBuffer.position(format.SIZE_TDEF_HEADER +
1258 (_indexCount * format.SIZE_INDEX_DEFINITION) +
1259 (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
1260
1261
1262 int varOffset = 0;
1263
1264 for(ColumnImpl col : _varColumns) {
1265 if(col.isVariableLength() && (col.getVarLenTableIndex() >= varOffset)) {
1266 varOffset = col.getVarLenTableIndex() + 1;
1267 }
1268 }
1269
1270 int fixedOffset = 0;
1271 if(!column.isVariableLength() && !column.storeInNullMask()) {
1272
1273 for(ColumnImpl col : _columns) {
1274 if(!col.isVariableLength() &&
1275 (col.getFixedDataOffset() >= fixedOffset)) {
1276 fixedOffset = col.getFixedDataOffset() + col.getFixedDataSize();
1277 }
1278 }
1279 }
1280
1281 mutator.setColumnOffsets(fixedOffset, varOffset, varOffset);
1282
1283
1284 int colDefPos = tableBuffer.position();
1285 ByteUtil.insertEmptyData(tableBuffer, format.SIZE_COLUMN_DEF_BLOCK);
1286 ColumnImpl.writeDefinition(mutator, column, tableBuffer);
1287
1288
1289 skipNames(tableBuffer, _columns.size());
1290 ByteUtil.insertEmptyData(tableBuffer, nameByteLen);
1291 writeName(tableBuffer, column.getName(), mutator.getCharset());
1292
1293 if(isLongVal) {
1294
1295
1296 Map.Entry<Integer,Integer> umapInfo = addUsageMaps(2, null);
1297 TableMutator.ColumnState colState = mutator.getColumnState(column);
1298 colState.setUmapPageNumber(umapInfo.getKey());
1299 byte rowNum = umapInfo.getValue().byteValue();
1300 colState.setUmapOwnedRowNumber(rowNum);
1301 colState.setUmapFreeRowNumber((byte)(rowNum + 1));
1302
1303
1304 ByteUtil.forward(tableBuffer, (_indexCount *
1305 format.SIZE_INDEX_COLUMN_BLOCK));
1306 ByteUtil.forward(tableBuffer,
1307 (_logicalIndexCount * format.SIZE_INDEX_INFO_BLOCK));
1308 skipNames(tableBuffer, _logicalIndexCount);
1309
1310
1311 while(tableBuffer.remaining() >= 2) {
1312 if(tableBuffer.getShort() == IndexData.COLUMN_UNUSED) {
1313
1314 ByteUtil.forward(tableBuffer, -2);
1315 break;
1316 }
1317
1318 ByteUtil.forward(tableBuffer, 8);
1319
1320
1321 }
1322
1323
1324 umapPos = tableBuffer.position();
1325 ByteUtil.insertEmptyData(tableBuffer, 10);
1326 ColumnImpl.writeColUsageMapDefinition(
1327 mutator, column, tableBuffer);
1328 }
1329
1330
1331 validateTableDefUpdate(mutator, tableBuffer);
1332
1333
1334 newCol = ColumnImpl.create(this, tableBuffer, colDefPos,
1335 column.getName(), _columns.size());
1336 newCol.setColumnIndex(_columns.size());
1337
1338
1339
1340 writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
1341 mutator.getNextPages());
1342 success = true;
1343
1344 } finally {
1345 if(!success) {
1346
1347 _tableDefBufferH.invalidate();
1348 }
1349 }
1350
1351
1352
1353
1354 _columns.add(newCol);
1355 ++_maxColumnCount;
1356 if(newCol.isVariableLength()) {
1357 _varColumns.add(newCol);
1358 ++_maxVarColumnCount;
1359 }
1360 if(newCol.isAutoNumber()) {
1361 _autoNumColumns.add(newCol);
1362 }
1363 if(newCol.isCalculated()) {
1364 _calcColEval.add(newCol);
1365 }
1366
1367 if(umapPos >= 0) {
1368
1369 tableBuffer.position(umapPos);
1370 readColumnUsageMaps(tableBuffer);
1371 }
1372
1373 newCol.postTableLoadInit();
1374
1375 if(!isSystem()) {
1376
1377
1378 newCol.initColumnValidator();
1379 }
1380
1381
1382 Map<String,PropertyMap.Property> colProps = column.getProperties();
1383 if(colProps != null) {
1384 newCol.getProperties().putAll(colProps.values());
1385 getProperties().save();
1386 }
1387
1388 completeTableMutation(tableBuffer);
1389
1390 return newCol;
1391 }
1392
1393
1394
1395
1396
1397 protected IndexData mutateAddIndexData(TableUpdater mutator) throws IOException
1398 {
1399 IndexBuilder index = mutator.getIndex();
1400 JetFormat format = mutator.getFormat();
1401
1402
1403
1404 mutator.addTdefLen(format.SIZE_INDEX_DEFINITION +
1405 format.SIZE_INDEX_COLUMN_BLOCK);
1406
1407
1408
1409 ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
1410 mutator);
1411
1412 IndexData newIdxData = null;
1413 boolean success = false;
1414 try {
1415
1416
1417
1418 ByteUtil.forward(tableBuffer, 39);
1419 tableBuffer.putInt(_indexCount + 1);
1420
1421
1422 tableBuffer.position(format.SIZE_TDEF_HEADER +
1423 (_indexCount * format.SIZE_INDEX_DEFINITION));
1424
1425
1426 ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_DEFINITION);
1427 IndexData.writeRowCountDefinitions(mutator, tableBuffer, 1);
1428
1429
1430 ByteUtil.forward(tableBuffer,
1431 (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
1432 skipNames(tableBuffer, _columns.size());
1433
1434
1435 ByteUtil.forward(tableBuffer, (_indexCount *
1436 format.SIZE_INDEX_COLUMN_BLOCK));
1437
1438
1439 TableMutator.IndexDataState idxDataState = mutator.getIndexDataState(index);
1440 int rootPageNumber = getPageChannel().allocateNewPage();
1441 Map.Entry<Integer,Integer> umapInfo = addUsageMaps(1, rootPageNumber);
1442 idxDataState.setRootPageNumber(rootPageNumber);
1443 idxDataState.setUmapPageNumber(umapInfo.getKey());
1444 idxDataState.setUmapRowNumber(umapInfo.getValue().byteValue());
1445
1446
1447 int idxDataDefPos = tableBuffer.position();
1448 ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_COLUMN_BLOCK);
1449 IndexData.writeDefinition(mutator, tableBuffer, idxDataState, null);
1450
1451
1452 validateTableDefUpdate(mutator, tableBuffer);
1453
1454
1455 tableBuffer.position(0);
1456 newIdxData = IndexData.create(
1457 this, tableBuffer, idxDataState.getIndexDataNumber(), format);
1458 tableBuffer.position(idxDataDefPos);
1459 newIdxData.read(tableBuffer, _columns);
1460
1461
1462
1463 writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
1464 mutator.getNextPages());
1465 success = true;
1466
1467 } finally {
1468 if(!success) {
1469
1470 _tableDefBufferH.invalidate();
1471 }
1472 }
1473
1474
1475
1476
1477 for(IndexData.ColumnDescriptor iCol : newIdxData.getColumns()) {
1478 _indexColumns.add(iCol.getColumn());
1479 }
1480
1481 ++_indexCount;
1482 _indexDatas.add(newIdxData);
1483
1484 completeTableMutation(tableBuffer);
1485
1486
1487 populateIndexData(newIdxData);
1488
1489 return newIdxData;
1490 }
1491
1492 private void populateIndexData(IndexData idxData)
1493 throws IOException
1494 {
1495
1496 List<ColumnImpl> idxCols = new ArrayList<ColumnImpl>();
1497 for(IndexData.ColumnDescriptor col : idxData.getColumns()) {
1498 idxCols.add(col.getColumn());
1499 }
1500
1501
1502 Object[] rowVals = new Object[_columns.size()];
1503 for(Row row : getDefaultCursor().newIterable().addColumns(idxCols)) {
1504 for(Column col : idxCols) {
1505 col.setRowValue(rowVals, col.getRowValue(row));
1506 }
1507
1508 IndexData.commitAll(
1509 idxData.prepareAddRow(rowVals, (RowIdImpl)row.getId(), null));
1510 }
1511
1512 updateTableDefinition(0);
1513 }
1514
1515
1516
1517
1518
1519 protected IndexImpl mutateAddIndex(TableUpdater mutator) throws IOException
1520 {
1521 IndexBuilder index = mutator.getIndex();
1522 JetFormat format = mutator.getFormat();
1523
1524
1525
1526 mutator.addTdefLen(format.SIZE_INDEX_INFO_BLOCK);
1527
1528 int nameByteLen = DBMutator.calculateNameLength(index.getName());
1529 mutator.addTdefLen(nameByteLen);
1530
1531
1532
1533 ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
1534 mutator);
1535
1536 IndexImpl newIdx = null;
1537 boolean success = false;
1538 try {
1539
1540
1541
1542 ByteUtil.forward(tableBuffer, 35);
1543 tableBuffer.putInt(_logicalIndexCount + 1);
1544
1545
1546 tableBuffer.position(format.SIZE_TDEF_HEADER +
1547 (_indexCount * format.SIZE_INDEX_DEFINITION));
1548
1549
1550 ByteUtil.forward(tableBuffer,
1551 (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
1552 skipNames(tableBuffer, _columns.size());
1553
1554
1555 ByteUtil.forward(tableBuffer, (_indexCount *
1556 format.SIZE_INDEX_COLUMN_BLOCK));
1557
1558 ByteUtil.forward(tableBuffer, (_logicalIndexCount *
1559 format.SIZE_INDEX_INFO_BLOCK));
1560
1561 int idxDefPos = tableBuffer.position();
1562 ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_INFO_BLOCK);
1563 IndexImpl.writeDefinition(mutator, index, tableBuffer);
1564
1565
1566 skipNames(tableBuffer, _logicalIndexCount);
1567 ByteUtil.insertEmptyData(tableBuffer, nameByteLen);
1568 writeName(tableBuffer, index.getName(), mutator.getCharset());
1569
1570
1571 validateTableDefUpdate(mutator, tableBuffer);
1572
1573
1574 tableBuffer.position(idxDefPos);
1575 newIdx = new IndexImpl(tableBuffer, _indexDatas, format);
1576 newIdx.setName(index.getName());
1577
1578
1579
1580 writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
1581 mutator.getNextPages());
1582 success = true;
1583
1584 } finally {
1585 if(!success) {
1586
1587 _tableDefBufferH.invalidate();
1588 }
1589 }
1590
1591
1592
1593
1594 ++_logicalIndexCount;
1595 _indexes.add(newIdx);
1596
1597 completeTableMutation(tableBuffer);
1598
1599 return newIdx;
1600 }
1601
1602 private void validateTableDefUpdate(TableUpdater mutator, ByteBuffer tableBuffer)
1603 {
1604 if(!mutator.validateUpdatedTdef(tableBuffer)) {
1605 throw new IllegalStateException(
1606 withErrorContext("Failed updating table definition (unexpected length)"));
1607 }
1608 }
1609
1610 private void completeTableMutation(ByteBuffer tableBuffer)
1611 {
1612
1613 _tableDefBufferH.possiblyInvalidate(_tableDefPageNumber, tableBuffer);
1614
1615
1616 _fkEnforcer.reset();
1617
1618
1619
1620 ++_modCount;
1621 }
1622
1623
1624
1625
1626 private static void skipNames(ByteBuffer tableBuffer, int count) {
1627 for(int i = 0; i < count; ++i) {
1628 ByteUtil.forward(tableBuffer, tableBuffer.getShort());
1629 }
1630 }
1631
1632 private ByteBuffer loadCompleteTableDefinitionBufferForUpdate(
1633 TableUpdater mutator)
1634 throws IOException
1635 {
1636
1637 ByteBuffer tableBuffer = _tableDefBufferH.setPage(getPageChannel(),
1638 _tableDefPageNumber);
1639 tableBuffer = loadCompleteTableDefinitionBuffer(
1640 tableBuffer, mutator.getNextPages());
1641
1642
1643 int addedLen = mutator.getAddedTdefLen();
1644 int origTdefLen = tableBuffer.getInt(8);
1645 mutator.setOrigTdefLen(origTdefLen);
1646 int newTdefLen = origTdefLen + addedLen;
1647 while(newTdefLen > tableBuffer.capacity()) {
1648 tableBuffer = expandTableBuffer(tableBuffer);
1649 tableBuffer.flip();
1650 }
1651
1652 tableBuffer.limit(origTdefLen);
1653
1654
1655 tableBuffer.position(8);
1656 tableBuffer.putInt(newTdefLen);
1657
1658 return tableBuffer;
1659 }
1660
1661
1662
1663
1664
1665 private Map.Entry<Integer,Integer> addUsageMaps(
1666 int numMaps, Integer firstUsedPage)
1667 throws IOException
1668 {
1669 JetFormat format = getFormat();
1670 PageChannel pageChannel = getPageChannel();
1671 int umapRowLength = format.OFFSET_USAGE_MAP_START +
1672 format.USAGE_MAP_TABLE_BYTE_LENGTH;
1673 int totalUmapSpaceUsage = getRowSpaceUsage(umapRowLength, format) * numMaps;
1674 int umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
1675 int firstRowNum = -1;
1676 int freeSpace = 0;
1677
1678
1679
1680
1681
1682 Set<Integer> knownPages = new TreeSet<Integer>(Collections.reverseOrder());
1683 collectUsageMapPages(knownPages);
1684
1685 ByteBuffer umapBuf = pageChannel.createPageBuffer();
1686 for(Integer pageNum : knownPages) {
1687 pageChannel.readPage(umapBuf, pageNum);
1688 freeSpace = umapBuf.getShort(format.OFFSET_FREE_SPACE);
1689 if(freeSpace >= totalUmapSpaceUsage) {
1690
1691 umapPageNumber = pageNum;
1692 firstRowNum = getRowsOnDataPage(umapBuf, format);
1693 break;
1694 }
1695 }
1696
1697 if(umapPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
1698
1699
1700 umapPageNumber = pageChannel.allocateNewPage();
1701 freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE;
1702 firstRowNum = 0;
1703 umapBuf = createUsageMapDefPage(pageChannel, freeSpace);
1704 }
1705
1706
1707 int rowStart = findRowEnd(umapBuf, firstRowNum, format) - umapRowLength;
1708 int umapRowNum = firstRowNum;
1709 for(int i = 0; i < numMaps; ++i) {
1710 umapBuf.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
1711 umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
1712
1713 int dataOffset = rowStart + 1;
1714 if(firstUsedPage != null) {
1715
1716 umapBuf.putInt(dataOffset, firstUsedPage);
1717 dataOffset += 4;
1718 umapBuf.put(dataOffset, (byte)1);
1719 dataOffset++;
1720 }
1721
1722
1723 ByteUtil.clearRange(umapBuf, dataOffset, (rowStart + umapRowLength));
1724
1725 rowStart -= umapRowLength;
1726 ++umapRowNum;
1727 }
1728
1729
1730 freeSpace -= totalUmapSpaceUsage;
1731 umapBuf.putShort(format.OFFSET_FREE_SPACE, (short)freeSpace);
1732 umapBuf.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
1733 (short)umapRowNum);
1734 pageChannel.writePage(umapBuf, umapPageNumber);
1735
1736 return new AbstractMap.SimpleImmutableEntry<Integer,Integer>(
1737 umapPageNumber, firstRowNum);
1738 }
1739
1740 void collectUsageMapPages(Collection<Integer> pages) {
1741 pages.add(_ownedPages.getTablePageNumber());
1742 pages.add(_freeSpacePages.getTablePageNumber());
1743
1744 for(IndexData idx : _indexDatas) {
1745 idx.collectUsageMapPages(pages);
1746 }
1747
1748 for(ColumnImpl col : _columns) {
1749 col.collectUsageMapPages(pages);
1750 }
1751 }
1752
1753
1754
1755
1756 private static void writeTableDefinitionHeader(
1757 TableCreator creator, ByteBuffer buffer, int totalTableDefSize)
1758 {
1759 List<ColumnBuilder> columns = creator.getColumns();
1760
1761
1762 writeTablePageHeader(buffer);
1763 buffer.putInt(totalTableDefSize);
1764 buffer.putInt(MAGIC_TABLE_NUMBER);
1765 buffer.putInt(0);
1766 buffer.putInt(0);
1767 buffer.put((byte) 1);
1768 for (int i = 0; i < 15; i++) {
1769 buffer.put((byte) 0);
1770 }
1771 buffer.put(TYPE_USER);
1772 buffer.putShort((short) columns.size());
1773 buffer.putShort(ColumnImpl.countVariableLength(columns));
1774 buffer.putShort((short) columns.size());
1775 buffer.putInt(creator.getLogicalIndexCount());
1776 buffer.putInt(creator.getIndexCount());
1777 buffer.put((byte) 0);
1778 ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber());
1779 buffer.put((byte) 1);
1780 ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber());
1781 }
1782
1783
1784
1785
1786
1787 private static void writeTablePageHeader(ByteBuffer buffer)
1788 {
1789 buffer.put(PageTypes.TABLE_DEF);
1790 buffer.put((byte) 0x01);
1791 buffer.put((byte) 0);
1792 buffer.put((byte) 0);
1793 buffer.putInt(0);
1794 }
1795
1796
1797
1798
1799
1800 static void writeName(ByteBuffer buffer, String name, Charset charset)
1801 {
1802 ByteBuffer encName = ColumnImpl.encodeUncompressedText(name, charset);
1803 buffer.putShort((short) encName.remaining());
1804 buffer.put(encName);
1805 }
1806
1807
1808
1809
1810
1811
1812 private static void createUsageMapDefinitionBuffer(TableCreator creator)
1813 throws IOException
1814 {
1815 List<ColumnBuilder> lvalCols = creator.getLongValueColumns();
1816
1817
1818 int indexUmapEnd = 2 + creator.getIndexCount();
1819 int umapNum = indexUmapEnd + (lvalCols.size() * 2);
1820
1821 JetFormat format = creator.getFormat();
1822 int umapRowLength = format.OFFSET_USAGE_MAP_START +
1823 format.USAGE_MAP_TABLE_BYTE_LENGTH;
1824 int umapSpaceUsage = getRowSpaceUsage(umapRowLength, format);
1825 PageChannel pageChannel = creator.getPageChannel();
1826 int umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
1827 ByteBuffer umapBuf = null;
1828 int freeSpace = 0;
1829 int rowStart = 0;
1830 int umapRowNum = 0;
1831
1832 for(int i = 0; i < umapNum; ++i) {
1833
1834 if(umapBuf == null) {
1835
1836
1837 if(umapPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
1838
1839 umapPageNumber = creator.getUmapPageNumber();
1840 } else {
1841
1842 umapPageNumber = creator.reservePageNumber();
1843 }
1844
1845 freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE;
1846
1847 umapBuf = createUsageMapDefPage(pageChannel, freeSpace);
1848
1849 rowStart = findRowEnd(umapBuf, 0, format) - umapRowLength;
1850 umapRowNum = 0;
1851 }
1852
1853 umapBuf.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
1854
1855 if(i == 0) {
1856
1857
1858 umapBuf.put(rowStart, UsageMap.MAP_TYPE_REFERENCE);
1859
1860 } else if(i == 1) {
1861
1862
1863 umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
1864
1865 } else if(i < indexUmapEnd) {
1866
1867
1868 int indexIdx = i - 2;
1869 TableMutator.IndexDataState idxDataState =
1870 creator.getIndexDataStates().get(indexIdx);
1871
1872
1873 int rootPageNumber = pageChannel.allocateNewPage();
1874
1875
1876 idxDataState.setRootPageNumber(rootPageNumber);
1877 idxDataState.setUmapRowNumber((byte)umapRowNum);
1878 idxDataState.setUmapPageNumber(umapPageNumber);
1879
1880
1881 umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
1882 umapBuf.putInt(rowStart + 1, rootPageNumber);
1883 umapBuf.put(rowStart + 5, (byte)1);
1884
1885 } else {
1886
1887
1888 int lvalColIdx = i - indexUmapEnd;
1889 int umapType = lvalColIdx % 2;
1890 lvalColIdx /= 2;
1891
1892 ColumnBuilder lvalCol = lvalCols.get(lvalColIdx);
1893 TableMutator.ColumnState colState =
1894 creator.getColumnState(lvalCol);
1895
1896 umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
1897
1898 if((umapType == 1) &&
1899 (umapPageNumber != colState.getUmapPageNumber())) {
1900
1901
1902 --i;
1903 umapType = 0;
1904 }
1905
1906 if(umapType == 0) {
1907
1908 colState.setUmapOwnedRowNumber((byte)umapRowNum);
1909 colState.setUmapPageNumber(umapPageNumber);
1910 } else {
1911
1912 colState.setUmapFreeRowNumber((byte)umapRowNum);
1913 }
1914 }
1915
1916 rowStart -= umapRowLength;
1917 freeSpace -= umapSpaceUsage;
1918 ++umapRowNum;
1919
1920 if((freeSpace <= umapSpaceUsage) || (i == (umapNum - 1))) {
1921
1922 umapBuf.putShort(format.OFFSET_FREE_SPACE, (short)freeSpace);
1923 umapBuf.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
1924 (short)umapRowNum);
1925 pageChannel.writePage(umapBuf, umapPageNumber);
1926 umapBuf = null;
1927 }
1928 }
1929 }
1930
1931 private static ByteBuffer createUsageMapDefPage(
1932 PageChannel pageChannel, int freeSpace)
1933 {
1934 ByteBuffer umapBuf = pageChannel.createPageBuffer();
1935 umapBuf.put(PageTypes.DATA);
1936 umapBuf.put((byte) 0x1);
1937 umapBuf.putShort((short)freeSpace);
1938 umapBuf.putInt(0);
1939 umapBuf.putInt(0);
1940 umapBuf.putShort((short)0);
1941 return umapBuf;
1942 }
1943
1944
1945
1946
1947
1948 private ByteBuffer loadCompleteTableDefinitionBuffer(
1949 ByteBuffer tableBuffer, List<Integer> pages)
1950 throws IOException
1951 {
1952 int nextPage = tableBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
1953 ByteBuffer nextPageBuffer = null;
1954 while (nextPage != 0) {
1955 if(pages != null) {
1956 pages.add(nextPage);
1957 }
1958 if (nextPageBuffer == null) {
1959 nextPageBuffer = getPageChannel().createPageBuffer();
1960 }
1961 getPageChannel().readPage(nextPageBuffer, nextPage);
1962 nextPage = nextPageBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
1963 tableBuffer = expandTableBuffer(tableBuffer);
1964 tableBuffer.put(nextPageBuffer.array(), 8, getFormat().PAGE_SIZE - 8);
1965 tableBuffer.flip();
1966 }
1967 return tableBuffer;
1968 }
1969
1970 private ByteBuffer expandTableBuffer(ByteBuffer tableBuffer) {
1971 ByteBuffer newBuffer = PageChannel.createBuffer(
1972 tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
1973 newBuffer.put(tableBuffer);
1974 return newBuffer;
1975 }
1976
1977 private void readColumnDefinitions(ByteBuffer tableBuffer, short columnCount)
1978 throws IOException
1979 {
1980 int colOffset = getFormat().OFFSET_INDEX_DEF_BLOCK +
1981 _indexCount * getFormat().SIZE_INDEX_DEFINITION;
1982
1983 tableBuffer.position(colOffset +
1984 (columnCount * getFormat().SIZE_COLUMN_HEADER));
1985 List<String> colNames = new ArrayList<String>(columnCount);
1986 for (int i = 0; i < columnCount; i++) {
1987 colNames.add(readName(tableBuffer));
1988 }
1989
1990 int dispIndex = 0;
1991 for (int i = 0; i < columnCount; i++) {
1992 ColumnImpl column = ColumnImpl.create(this, tableBuffer,
1993 colOffset + (i * getFormat().SIZE_COLUMN_HEADER), colNames.get(i),
1994 dispIndex++);
1995 _columns.add(column);
1996 if(column.isVariableLength()) {
1997
1998
1999 _varColumns.add(column);
2000 }
2001 }
2002
2003 Collections.sort(_columns);
2004 initAutoNumberColumns();
2005 initCalculatedColumns();
2006
2007
2008 int colIdx = 0;
2009 for(ColumnImpl col : _columns) {
2010 col.setColumnIndex(colIdx++);
2011 }
2012
2013
2014
2015 Collections.sort(_varColumns, VAR_LEN_COLUMN_COMPARATOR);
2016 }
2017
2018 private void readIndexDefinitions(ByteBuffer tableBuffer) throws IOException
2019 {
2020
2021 for (int i = 0; i < _indexCount; i++) {
2022 IndexData idxData = _indexDatas.get(i);
2023 idxData.read(tableBuffer, _columns);
2024
2025 for(IndexData.ColumnDescriptor iCol : idxData.getColumns()) {
2026 _indexColumns.add(iCol.getColumn());
2027 }
2028 }
2029
2030
2031 for (int i = 0; i < _logicalIndexCount; i++) {
2032 _indexes.add(new IndexImpl(tableBuffer, _indexDatas, getFormat()));
2033 }
2034
2035
2036 for (int i = 0; i < _logicalIndexCount; i++) {
2037 _indexes.get(i).setName(readName(tableBuffer));
2038 }
2039
2040 Collections.sort(_indexes);
2041 }
2042
2043 private boolean readColumnUsageMaps(ByteBuffer tableBuffer)
2044 throws IOException
2045 {
2046 short umapColNum = tableBuffer.getShort();
2047 if(umapColNum == IndexData.COLUMN_UNUSED) {
2048 return false;
2049 }
2050
2051 int pos = tableBuffer.position();
2052 UsageMap colOwnedPages = null;
2053 UsageMap colFreeSpacePages = null;
2054 try {
2055 colOwnedPages = UsageMap.read(getDatabase(), tableBuffer);
2056 colFreeSpacePages = UsageMap.read(getDatabase(), tableBuffer);
2057 } catch(IllegalStateException e) {
2058
2059 colOwnedPages = null;
2060 colFreeSpacePages = null;
2061 tableBuffer.position(pos + 8);
2062 LOG.warn(withErrorContext("Invalid column " + umapColNum +
2063 " usage map definition: " + e));
2064 }
2065
2066 for(ColumnImpl col : _columns) {
2067 if(col.getColumnNumber() == umapColNum) {
2068 col.setUsageMaps(colOwnedPages, colFreeSpacePages);
2069 break;
2070 }
2071 }
2072
2073 return true;
2074 }
2075
2076
2077
2078
2079
2080 private void writeDataPage(ByteBuffer pageBuffer, int pageNumber)
2081 throws IOException
2082 {
2083
2084 getPageChannel().writePage(pageBuffer, pageNumber);
2085
2086
2087
2088 _addRowBufferH.possiblyInvalidate(pageNumber, pageBuffer);
2089
2090
2091
2092 ++_modCount;
2093 }
2094
2095
2096
2097
2098
2099
2100 private String readName(ByteBuffer buffer) {
2101 int nameLength = readNameLength(buffer);
2102 byte[] nameBytes = ByteUtil.getBytes(buffer, nameLength);
2103 return ColumnImpl.decodeUncompressedText(nameBytes,
2104 getDatabase().getCharset());
2105 }
2106
2107
2108
2109
2110 private int readNameLength(ByteBuffer buffer) {
2111 return ByteUtil.getUnsignedVarInt(buffer, getFormat().SIZE_NAME_LENGTH);
2112 }
2113
2114 @Override
2115 public Object[] asRow(Map<String,?> rowMap) {
2116 return asRow(rowMap, null, false);
2117 }
2118
2119
2120
2121
2122
2123
2124
2125
2126 public Object[] asRowWithRowId(Map<String,?> rowMap) {
2127 return asRow(rowMap, null, true);
2128 }
2129
2130 @Override
2131 public Object[] asUpdateRow(Map<String,?> rowMap) {
2132 return asRow(rowMap, Column.KEEP_VALUE, false);
2133 }
2134
2135
2136
2137
2138
2139
2140 public RowId getRowId(Object[] row) {
2141 return (RowId)row[_columns.size()];
2142 }
2143
2144
2145
2146
2147 private Object[] asRow(Map<String,?> rowMap, Object defaultValue,
2148 boolean returnRowId)
2149 {
2150 int len = _columns.size();
2151 if(returnRowId) {
2152 ++len;
2153 }
2154 Object[] row = new Object[len];
2155 if(defaultValue != null) {
2156 Arrays.fill(row, defaultValue);
2157 }
2158 if(returnRowId) {
2159 row[len - 1] = ColumnImpl.RETURN_ROW_ID;
2160 }
2161 if(rowMap == null) {
2162 return row;
2163 }
2164 for(ColumnImpl col : _columns) {
2165 if(rowMap.containsKey(col.getName())) {
2166 col.setRowValue(row, col.getRowValue(rowMap));
2167 }
2168 }
2169 return row;
2170 }
2171
2172 @Override
2173 public Object[] addRow(Object... row) throws IOException {
2174 return addRows(Collections.singletonList(row), false).get(0);
2175 }
2176
2177 @Override
2178 public <M extends Map<String,Object>> M addRowFromMap(M row)
2179 throws IOException
2180 {
2181 Object[] rowValues = asRow(row);
2182
2183 addRow(rowValues);
2184
2185 returnRowValues(row, rowValues, _columns);
2186 return row;
2187 }
2188
2189 @Override
2190 public List<? extends Object[]> addRows(List<? extends Object[]> rows)
2191 throws IOException
2192 {
2193 return addRows(rows, true);
2194 }
2195
2196 @Override
2197 public <M extends Map<String,Object>> List<M> addRowsFromMaps(List<M> rows)
2198 throws IOException
2199 {
2200 List<Object[]> rowValuesList = new ArrayList<Object[]>(rows.size());
2201 for(Map<String,Object> row : rows) {
2202 rowValuesList.add(asRow(row));
2203 }
2204
2205 addRows(rowValuesList);
2206
2207 for(int i = 0; i < rowValuesList.size(); ++i) {
2208 Map<String,Object> row = rows.get(i);
2209 Object[] rowValues = rowValuesList.get(i);
2210 returnRowValues(row, rowValues, _columns);
2211 }
2212 return rows;
2213 }
2214
2215 private static void returnRowValues(Map<String,Object> row, Object[] rowValues,
2216 List<ColumnImpl> cols)
2217 {
2218 for(ColumnImpl col : cols) {
2219 col.setRowValue(row, col.getRowValue(rowValues));
2220 }
2221 }
2222
2223
2224
2225
2226
2227
2228 protected List<? extends Object[]> addRows(List<? extends Object[]> rows,
2229 final boolean isBatchWrite)
2230 throws IOException
2231 {
2232 if(rows.isEmpty()) {
2233 return rows;
2234 }
2235
2236 getPageChannel().startWrite();
2237 try {
2238
2239 ByteBuffer dataPage = null;
2240 int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
2241 int updateCount = 0;
2242 int autoNumAssignCount = 0;
2243 WriteRowState writeRowState =
2244 (!_autoNumColumns.isEmpty() ? new WriteRowState() : null);
2245 try {
2246
2247 List<Object[]> dupeRows = null;
2248 final int numCols = _columns.size();
2249 for (int i = 0; i < rows.size(); i++) {
2250
2251
2252
2253
2254
2255
2256 Object[] row = rows.get(i);
2257 if((row.length < numCols) || (row.getClass() != Object[].class)) {
2258 row = dupeRow(row, numCols);
2259
2260
2261 if(dupeRows == null) {
2262 dupeRows = new ArrayList<Object[]>(rows);
2263 rows = dupeRows;
2264 }
2265
2266 dupeRows.set(i, row);
2267 }
2268
2269
2270 for(ColumnImpl column : _columns) {
2271 if(!column.isAutoNumber()) {
2272 Object val = column.getRowValue(row);
2273 if(val == null) {
2274 val = column.generateDefaultValue();
2275 }
2276
2277 column.setRowValue(row, column.validate(val));
2278 }
2279 }
2280
2281
2282 handleAutoNumbersForAdd(row, writeRowState);
2283 ++autoNumAssignCount;
2284
2285
2286
2287 _calcColEval.calculate(row);
2288
2289
2290 if(_rowValidator != null) {
2291 _rowValidator.validate(row);
2292 }
2293
2294
2295 ByteBuffer rowData = createRow(
2296 row, _writeRowBufferH.getPageBuffer(getPageChannel()));
2297
2298 int rowSize = rowData.remaining();
2299 if (rowSize > getFormat().MAX_ROW_SIZE) {
2300 throw new InvalidValueException(withErrorContext(
2301 "Row size " + rowSize + " is too large"));
2302 }
2303
2304
2305 dataPage = findFreeRowSpace(rowSize, dataPage, pageNumber);
2306 pageNumber = _addRowBufferH.getPageNumber();
2307
2308
2309 int rowNum = getRowsOnDataPage(dataPage, getFormat());
2310
2311 RowIdImplss/impl/RowIdImpl.html#RowIdImpl">RowIdImpl rowId = new RowIdImpl(pageNumber, rowNum);
2312
2313
2314
2315 if(!_indexDatas.isEmpty()) {
2316
2317 IndexData.PendingChange idxChange = null;
2318 try {
2319
2320
2321 _fkEnforcer.addRow(row);
2322
2323
2324 for(IndexData indexData : _indexDatas) {
2325 idxChange = indexData.prepareAddRow(row, rowId, idxChange);
2326 }
2327
2328
2329 IndexData.commitAll(idxChange);
2330
2331 } catch(ConstraintViolationException ce) {
2332 IndexData.rollbackAll(idxChange);
2333 throw ce;
2334 }
2335 }
2336
2337
2338 addDataPageRow(dataPage, rowSize, getFormat(), 0);
2339 dataPage.put(rowData);
2340
2341
2342 if((row.length > numCols) &&
2343 (row[numCols] == ColumnImpl.RETURN_ROW_ID)) {
2344 row[numCols] = rowId;
2345 }
2346
2347 ++updateCount;
2348 }
2349
2350 writeDataPage(dataPage, pageNumber);
2351
2352
2353 updateTableDefinition(rows.size());
2354
2355 } catch(Exception rowWriteFailure) {
2356
2357 boolean isWriteFailure = isWriteFailure(rowWriteFailure);
2358
2359 if(!isWriteFailure && (autoNumAssignCount > updateCount)) {
2360
2361
2362 restoreAutoNumbersFromAdd(rows.get(autoNumAssignCount - 1));
2363 }
2364
2365 if(!isBatchWrite) {
2366
2367 if(rowWriteFailure instanceof IOException) {
2368 throw (IOException)rowWriteFailure;
2369 }
2370 throw (RuntimeException)rowWriteFailure;
2371 }
2372
2373
2374 if(isWriteFailure) {
2375
2376
2377
2378 updateCount = 0;
2379
2380 } else if(updateCount > 0) {
2381
2382
2383 try {
2384
2385 writeDataPage(dataPage, pageNumber);
2386
2387
2388 updateTableDefinition(updateCount);
2389
2390 } catch(Exception flushFailure) {
2391
2392
2393
2394
2395 LOG.warn(withErrorContext(
2396 "Secondary row failure which preceded the write failure"),
2397 rowWriteFailure);
2398 updateCount = 0;
2399 rowWriteFailure = flushFailure;
2400 }
2401 }
2402
2403 throw new BatchUpdateException(
2404 updateCount, withErrorContext("Failed adding rows"),
2405 rowWriteFailure);
2406 }
2407
2408 } finally {
2409 getPageChannel().finishWrite();
2410 }
2411
2412 return rows;
2413 }
2414
2415 private static boolean isWriteFailure(Throwable t) {
2416 while(t != null) {
2417 if((t instanceof IOException) && !(t instanceof JackcessException)) {
2418 return true;
2419 }
2420 t = t.getCause();
2421 }
2422
2423 return false;
2424 }
2425
2426 @Override
2427 public Rowf="../../../../com/healthmarketscience/jackcess/Row.html#Row">Row updateRow(Row row) throws IOException {
2428 return updateRowFromMap(
2429 getDefaultCursor().getRowState(), (RowIdImpl)row.getId(), row);
2430 }
2431
2432
2433
2434
2435
2436
2437
2438
2439 public Object[] updateRow(RowId rowId, Object... row) throws IOException {
2440 return updateRow(
2441 getDefaultCursor().getRowState(), (RowIdImpl)rowId, row);
2442 }
2443
2444
2445
2446
2447
2448
2449
2450 public void updateValue(Column column, RowId rowId, Object value)
2451 throws IOException
2452 {
2453 Object[] row = new Object[_columns.size()];
2454 Arrays.fill(row, Column.KEEP_VALUE);
2455 column.setRowValue(row, value);
2456
2457 updateRow(rowId, row);
2458 }
2459
2460 public <M extends Map<String,Object>> M updateRowFromMap(
2461 RowState rowState, RowIdImpl rowId, M row)
2462 throws IOException
2463 {
2464 Object[] rowValues = updateRow(rowState, rowId, asUpdateRow(row));
2465 returnRowValues(row, rowValues, _columns);
2466 return row;
2467 }
2468
2469
2470
2471
2472
2473 public Object[] updateRow(RowState rowState, RowIdImpl rowId, Object... row)
2474 throws IOException
2475 {
2476 requireValidRowId(rowId);
2477
2478 getPageChannel().startWrite();
2479 try {
2480
2481
2482 ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
2483 int oldRowSize = rowBuffer.remaining();
2484
2485 requireNonDeletedRow(rowState, rowId);
2486
2487
2488
2489 if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
2490 row = dupeRow(row, _columns.size());
2491 }
2492
2493
2494
2495
2496 Map<ColumnImpl,byte[]> keepRawVarValues =
2497 (!_varColumns.isEmpty() ? new HashMap<ColumnImpl,byte[]>() : null);
2498
2499
2500 for(ColumnImpl column : _columns) {
2501
2502 if(column.isAutoNumber()) {
2503
2504 continue;
2505 }
2506
2507 Object rowValue = column.getRowValue(row);
2508 if(rowValue == Column.KEEP_VALUE) {
2509
2510
2511 rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
2512 keepRawVarValues);
2513
2514 } else {
2515
2516
2517 Object oldValue = Column.KEEP_VALUE;
2518 if(_indexColumns.contains(column)) {
2519
2520 oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
2521 null);
2522 } else {
2523 oldValue = rowState.getRowCacheValue(column.getColumnIndex());
2524 }
2525
2526
2527 if(oldValue != rowValue) {
2528
2529 rowValue = column.validate(rowValue);
2530 }
2531 }
2532
2533 column.setRowValue(row, rowValue);
2534 }
2535
2536
2537 handleAutoNumbersForUpdate(row, rowBuffer, rowState);
2538
2539
2540
2541 _calcColEval.calculate(row);
2542
2543
2544 if(_rowValidator != null) {
2545 _rowValidator.validate(row);
2546 }
2547
2548
2549 ByteBuffer newRowData = createRow(
2550 row, _writeRowBufferH.getPageBuffer(getPageChannel()), oldRowSize,
2551 keepRawVarValues);
2552
2553 if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
2554 throw new InvalidValueException(withErrorContext(
2555 "Row size " + newRowData.limit() + " is too large"));
2556 }
2557
2558 if(!_indexDatas.isEmpty()) {
2559
2560 IndexData.PendingChange idxChange = null;
2561 try {
2562
2563 Object[] oldRowValues = rowState.getRowCacheValues();
2564
2565
2566 _fkEnforcer.updateRow(oldRowValues, row);
2567
2568
2569 for(IndexData indexData : _indexDatas) {
2570 idxChange = indexData.prepareUpdateRow(oldRowValues, rowId, row,
2571 idxChange);
2572 }
2573
2574
2575 IndexData.commitAll(idxChange);
2576
2577 } catch(ConstraintViolationException ce) {
2578 IndexData.rollbackAll(idxChange);
2579 throw ce;
2580 }
2581 }
2582
2583
2584 rowBuffer.reset();
2585 int rowSize = newRowData.remaining();
2586
2587 ByteBuffer dataPage = null;
2588 int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
2589
2590 if(oldRowSize >= rowSize) {
2591
2592
2593 rowBuffer.put(newRowData);
2594
2595
2596 dataPage = rowState.getFinalPage();
2597 pageNumber = rowState.getFinalRowId().getPageNumber();
2598
2599 } else {
2600
2601
2602 dataPage = findFreeRowSpace(rowSize, null,
2603 PageChannel.INVALID_PAGE_NUMBER);
2604 pageNumber = _addRowBufferH.getPageNumber();
2605
2606 RowIdImpl headerRowId = rowState.getHeaderRowId();
2607 ByteBuffer headerPage = rowState.getHeaderPage();
2608 if(pageNumber == headerRowId.getPageNumber()) {
2609
2610 dataPage = headerPage;
2611 }
2612
2613
2614
2615 int rowNum = addDataPageRow(dataPage, rowSize, getFormat(),
2616 DELETED_ROW_MASK);
2617 dataPage.put(newRowData);
2618
2619
2620
2621 rowBuffer = PageChannel.narrowBuffer(
2622 headerPage,
2623 findRowStart(headerPage, headerRowId.getRowNumber(), getFormat()),
2624 findRowEnd(headerPage, headerRowId.getRowNumber(), getFormat()));
2625 rowBuffer.put((byte)rowNum);
2626 ByteUtil.put3ByteInt(rowBuffer, pageNumber);
2627 ByteUtil.clearRemaining(rowBuffer);
2628
2629
2630 int headerRowIndex = getRowStartOffset(headerRowId.getRowNumber(),
2631 getFormat());
2632 headerPage.putShort(headerRowIndex,
2633 (short)(headerPage.getShort(headerRowIndex)
2634 | OVERFLOW_ROW_MASK));
2635 if(pageNumber != headerRowId.getPageNumber()) {
2636 writeDataPage(headerPage, headerRowId.getPageNumber());
2637 }
2638 }
2639
2640 writeDataPage(dataPage, pageNumber);
2641
2642 updateTableDefinition(0);
2643
2644 } finally {
2645 getPageChannel().finishWrite();
2646 }
2647
2648 return row;
2649 }
2650
2651 private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
2652 int pageNumber)
2653 throws IOException
2654 {
2655
2656 boolean modifiedPage = true;
2657
2658 if(dataPage == null) {
2659
2660
2661 dataPage = findFreeRowSpace(_ownedPages, _freeSpacePages,
2662 _addRowBufferH);
2663
2664 if(dataPage == null) {
2665
2666 return newDataPage();
2667 }
2668
2669
2670 pageNumber = _addRowBufferH.getPageNumber();
2671
2672 modifiedPage = false;
2673 }
2674
2675 if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
2676
2677
2678 if(modifiedPage) {
2679 writeDataPage(dataPage, pageNumber);
2680 }
2681 _freeSpacePages.removePageNumber(pageNumber);
2682
2683 dataPage = newDataPage();
2684 }
2685
2686 return dataPage;
2687 }
2688
2689 static ByteBuffer findFreeRowSpace(
2690 UsageMap./../../com/healthmarketscience/jackcess/impl/UsageMap.html#UsageMap">UsageMap ownedPages, UsageMap freeSpacePages,
2691 TempPageHolder rowBufferH)
2692 throws IOException
2693 {
2694
2695
2696 UsageMap.PageCursor revPageCursor = ownedPages.cursor();
2697 revPageCursor.afterLast();
2698 while(true) {
2699 int tmpPageNumber = revPageCursor.getPreviousPage();
2700 if(tmpPageNumber < 0) {
2701 break;
2702 }
2703
2704 if(!freeSpacePages.containsPageNumber(tmpPageNumber)) {
2705 continue;
2706 }
2707 ByteBuffer dataPage = rowBufferH.setPage(ownedPages.getPageChannel(),
2708 tmpPageNumber);
2709 if(dataPage.get() == PageTypes.DATA) {
2710
2711 return dataPage;
2712 }
2713 }
2714
2715 return null;
2716 }
2717
2718
2719
2720
2721 private void updateTableDefinition(int rowCountInc) throws IOException
2722 {
2723
2724 ByteBuffer tdefPage = _tableDefBufferH.setPage(getPageChannel(),
2725 _tableDefPageNumber);
2726
2727
2728 _rowCount += rowCountInc;
2729 tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
2730 tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastLongAutoNumber);
2731 int ctypeOff = getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER;
2732 if(ctypeOff >= 0) {
2733 tdefPage.putInt(ctypeOff, _lastComplexTypeAutoNumber);
2734 }
2735
2736
2737 for (IndexData indexData : _indexDatas) {
2738
2739
2740 tdefPage.putInt(indexData.getUniqueEntryCountOffset(),
2741 indexData.getUniqueEntryCount());
2742
2743 indexData.update();
2744 }
2745
2746
2747 getPageChannel().writePage(tdefPage, _tableDefPageNumber);
2748 }
2749
2750
2751
2752
2753
2754 private ByteBuffer newDataPage() throws IOException {
2755 ByteBuffer dataPage = _addRowBufferH.setNewPage(getPageChannel());
2756 dataPage.put(PageTypes.DATA);
2757 dataPage.put((byte) 1);
2758 dataPage.putShort((short)getFormat().DATA_PAGE_INITIAL_FREE_SPACE);
2759 dataPage.putInt(_tableDefPageNumber);
2760 dataPage.putInt(0);
2761 dataPage.putShort((short)0);
2762 int pageNumber = _addRowBufferH.getPageNumber();
2763 getPageChannel().writePage(dataPage, pageNumber);
2764 _ownedPages.addPageNumber(pageNumber);
2765 _freeSpacePages.addPageNumber(pageNumber);
2766 return dataPage;
2767 }
2768
2769
2770 protected ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
2771 throws IOException
2772 {
2773 return createRow(rowArray, buffer, 0,
2774 Collections.<ColumnImpl,byte[]>emptyMap());
2775 }
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787 private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
2788 int minRowSize,
2789 Map<ColumnImpl,byte[]> rawVarValues)
2790 throws IOException
2791 {
2792 buffer.putShort(_maxColumnCount);
2793 NullMask/impl/NullMask.html#NullMask">NullMask nullMask = new NullMask(_maxColumnCount);
2794
2795
2796 int fixedDataStart = buffer.position();
2797 int fixedDataEnd = fixedDataStart;
2798 for (ColumnImpl col : _columns) {
2799
2800 if(col.isVariableLength()) {
2801 continue;
2802 }
2803
2804 Object rowValue = col.getRowValue(rowArray);
2805
2806 if (col.storeInNullMask()) {
2807
2808 if(col.writeToNullMask(rowValue)) {
2809 nullMask.markNotNull(col);
2810 }
2811 rowValue = null;
2812 }
2813
2814 if(rowValue != null) {
2815
2816
2817 nullMask.markNotNull(col);
2818
2819
2820 buffer.position(fixedDataStart + col.getFixedDataOffset());
2821 buffer.put(col.write(rowValue, 0));
2822 }
2823
2824
2825
2826
2827 buffer.position(fixedDataStart + col.getFixedDataOffset() +
2828 col.getLength());
2829
2830
2831 if(buffer.position() > fixedDataEnd) {
2832 fixedDataEnd = buffer.position();
2833 }
2834
2835 }
2836
2837
2838 buffer.position(fixedDataEnd);
2839
2840
2841 if(_maxVarColumnCount > 0) {
2842
2843 int maxRowSize = getFormat().MAX_ROW_SIZE;
2844
2845
2846
2847 maxRowSize -= buffer.position();
2848
2849 int trailerSize = (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
2850 maxRowSize -= trailerSize;
2851
2852
2853
2854
2855 for (ColumnImpl varCol : _varColumns) {
2856 if((varCol.getType().isLongValue()) &&
2857 (varCol.getRowValue(rowArray) != null)) {
2858 maxRowSize -= getFormat().SIZE_LONG_VALUE_DEF;
2859 }
2860 }
2861
2862
2863 short[] varColumnOffsets = new short[_maxVarColumnCount];
2864 int varColumnOffsetsIndex = 0;
2865 for (ColumnImpl varCol : _varColumns) {
2866 short offset = (short) buffer.position();
2867 Object rowValue = varCol.getRowValue(rowArray);
2868 if (rowValue != null) {
2869
2870 nullMask.markNotNull(varCol);
2871
2872 byte[] rawValue = null;
2873 ByteBuffer varDataBuf = null;
2874 if(((rawValue = rawVarValues.get(varCol)) != null) &&
2875 (rawValue.length <= maxRowSize)) {
2876
2877 varDataBuf = ByteBuffer.wrap(rawValue);
2878 } else {
2879
2880 varDataBuf = varCol.write(rowValue, maxRowSize);
2881 }
2882
2883 maxRowSize -= varDataBuf.remaining();
2884 if(varCol.getType().isLongValue()) {
2885
2886
2887 maxRowSize += getFormat().SIZE_LONG_VALUE_DEF;
2888 }
2889 try {
2890 buffer.put(varDataBuf);
2891 } catch(BufferOverflowException e) {
2892
2893
2894 throw new InvalidValueException(withErrorContext(
2895 "Row size " + buffer.limit() + " is too large"));
2896 }
2897 }
2898
2899
2900 while(varColumnOffsetsIndex <= varCol.getVarLenTableIndex()) {
2901 varColumnOffsets[varColumnOffsetsIndex++] = offset;
2902 }
2903 }
2904
2905
2906 while(varColumnOffsetsIndex < varColumnOffsets.length) {
2907 varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer.position();
2908 }
2909
2910
2911 int eod = buffer.position();
2912
2913
2914 padRowBuffer(buffer, minRowSize, trailerSize);
2915
2916 buffer.putShort((short) eod);
2917
2918
2919
2920 for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
2921 buffer.putShort(varColumnOffsets[i]);
2922 }
2923 buffer.putShort(_maxVarColumnCount);
2924
2925 } else {
2926
2927
2928 padRowBuffer(buffer, minRowSize, nullMask.byteSize());
2929 }
2930
2931 nullMask.write(buffer);
2932 buffer.flip();
2933 return buffer;
2934 }
2935
2936
2937
2938
2939 private void handleAutoNumbersForAdd(Object[] row, WriteRowState writeRowState)
2940 throws IOException
2941 {
2942 if(_autoNumColumns.isEmpty()) {
2943 return;
2944 }
2945
2946 boolean enableInsert = isAllowAutoNumberInsert();
2947 writeRowState.resetAutoNumber();
2948 for(ColumnImpl col : _autoNumColumns) {
2949
2950
2951
2952 Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
2953
2954 ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator();
2955 Object rowValue = ((inRowValue == null) ?
2956 autoNumGen.getNext(writeRowState) :
2957 autoNumGen.handleInsert(writeRowState, inRowValue));
2958
2959 col.setRowValue(row, rowValue);
2960 }
2961 }
2962
2963
2964
2965
2966 private void handleAutoNumbersForUpdate(Object[] row, ByteBuffer rowBuffer,
2967 RowState rowState)
2968 throws IOException
2969 {
2970 if(_autoNumColumns.isEmpty()) {
2971 return;
2972 }
2973
2974 boolean enableInsert = isAllowAutoNumberInsert();
2975 rowState.resetAutoNumber();
2976 for(ColumnImpl col : _autoNumColumns) {
2977
2978
2979
2980 Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
2981
2982 Object rowValue =
2983 ((inRowValue == null) ?
2984 getRowColumn(getFormat(), rowBuffer, col, rowState, null) :
2985 col.getAutoNumberGenerator().handleInsert(rowState, inRowValue));
2986
2987 col.setRowValue(row, rowValue);
2988 }
2989 }
2990
2991
2992
2993
2994
2995 private static Object getInputAutoNumberRowValue(
2996 boolean enableInsert, ColumnImpl col, Object[] row)
2997 {
2998 if(!enableInsert) {
2999 return null;
3000 }
3001
3002 Object inRowValue = col.getRowValue(row);
3003 if((inRowValue == Column.KEEP_VALUE) || (inRowValue == Column.AUTO_NUMBER)) {
3004
3005 inRowValue = null;
3006 }
3007 return inRowValue;
3008 }
3009
3010
3011
3012
3013 private void restoreAutoNumbersFromAdd(Object[] row)
3014 {
3015 if(_autoNumColumns.isEmpty()) {
3016 return;
3017 }
3018
3019 for(ColumnImpl col : _autoNumColumns) {
3020
3021 col.getAutoNumberGenerator().restoreLast(col.getRowValue(row));
3022 }
3023 }
3024
3025 private static void padRowBuffer(ByteBuffer buffer, int minRowSize,
3026 int trailerSize)
3027 {
3028 int pos = buffer.position();
3029 if((pos + trailerSize) < minRowSize) {
3030
3031 int padSize = minRowSize - (pos + trailerSize);
3032 ByteUtil.clearRange(buffer, pos, pos + padSize);
3033 ByteUtil.forward(buffer, padSize);
3034 }
3035 }
3036
3037 @Override
3038 public int getRowCount() {
3039 return _rowCount;
3040 }
3041
3042 int getNextLongAutoNumber() {
3043
3044 return ++_lastLongAutoNumber;
3045 }
3046
3047 int getLastLongAutoNumber() {
3048
3049 return _lastLongAutoNumber;
3050 }
3051
3052 void adjustLongAutoNumber(int inLongAutoNumber) {
3053 if(inLongAutoNumber > _lastLongAutoNumber) {
3054 _lastLongAutoNumber = inLongAutoNumber;
3055 }
3056 }
3057
3058 void restoreLastLongAutoNumber(int lastLongAutoNumber) {
3059
3060 _lastLongAutoNumber = lastLongAutoNumber - 1;
3061 }
3062
3063 int getNextComplexTypeAutoNumber() {
3064
3065 return ++_lastComplexTypeAutoNumber;
3066 }
3067
3068 int getLastComplexTypeAutoNumber() {
3069
3070 return _lastComplexTypeAutoNumber;
3071 }
3072
3073 void adjustComplexTypeAutoNumber(int inComplexTypeAutoNumber) {
3074 if(inComplexTypeAutoNumber > _lastComplexTypeAutoNumber) {
3075 _lastComplexTypeAutoNumber = inComplexTypeAutoNumber;
3076 }
3077 }
3078
3079 void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) {
3080
3081 _lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1;
3082 }
3083
3084 @Override
3085 public String toString() {
3086 return CustomToStringStyle.builder(this)
3087 .append("type", (_tableType + (!isSystem() ? " (USER)" : " (SYSTEM)")))
3088 .append("name", _name)
3089 .append("rowCount", _rowCount)
3090 .append("columnCount", _columns.size())
3091 .append("indexCount(data)", _indexCount)
3092 .append("logicalIndexCount", _logicalIndexCount)
3093 .append("validator", CustomToStringStyle.ignoreNull(_rowValidator))
3094 .append("columns", _columns)
3095 .append("indexes", _indexes)
3096 .append("ownedPages", _ownedPages)
3097 .toString();
3098 }
3099
3100
3101
3102
3103
3104
3105 public String display() throws IOException {
3106 return display(Long.MAX_VALUE);
3107 }
3108
3109
3110
3111
3112
3113
3114
3115 public String display(long limit) throws IOException {
3116 reset();
3117 StringWriter rtn = new StringWriter();
3118 new ExportUtil.Builder(getDefaultCursor()).setDelimiter("\t").setHeader(true)
3119 .exportWriter(new BufferedWriter(rtn));
3120 return rtn.toString();
3121 }
3122
3123
3124
3125
3126
3127
3128
3129 public static int addDataPageRow(ByteBuffer dataPage,
3130 int rowSize,
3131 JetFormat format,
3132 int rowFlags)
3133 {
3134 int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
3135
3136
3137 short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
3138 dataPage.putShort(format.OFFSET_FREE_SPACE, (short) (freeSpaceInPage -
3139 rowSpaceUsage));
3140
3141
3142 short rowCount = dataPage.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
3143 dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
3144 (short) (rowCount + 1));
3145
3146
3147 short rowLocation = findRowEnd(dataPage, rowCount, format);
3148 rowLocation -= rowSize;
3149
3150
3151 dataPage.putShort(getRowStartOffset(rowCount, format),
3152 (short)(rowLocation | rowFlags));
3153
3154
3155 dataPage.position(rowLocation);
3156
3157 return rowCount;
3158 }
3159
3160
3161
3162
3163
3164 static int getRowsOnDataPage(ByteBuffer rowBuffer, JetFormat format)
3165 {
3166 int rowsOnPage = 0;
3167 if((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
3168 rowsOnPage = rowBuffer.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
3169 }
3170 return rowsOnPage;
3171 }
3172
3173
3174
3175
3176 private void requireValidRowId(RowIdImpl rowId) {
3177 if(!rowId.isValid()) {
3178 throw new IllegalArgumentException(withErrorContext(
3179 "Given rowId is invalid: " + rowId));
3180 }
3181 }
3182
3183
3184
3185
3186 private void requireNonDeletedRow(RowState rowState, RowIdImpl rowId)
3187 {
3188 if(!rowState.isValid()) {
3189 throw new IllegalArgumentException(withErrorContext(
3190 "Given rowId is invalid for this table: " + rowId));
3191 }
3192 if(rowState.isDeleted()) {
3193 throw new IllegalStateException(withErrorContext(
3194 "Row is deleted: " + rowId));
3195 }
3196 }
3197
3198
3199
3200
3201 public static boolean isDeletedRow(short rowStart) {
3202 return ((rowStart & DELETED_ROW_MASK) != 0);
3203 }
3204
3205
3206
3207
3208 public static boolean isOverflowRow(short rowStart) {
3209 return ((rowStart & OVERFLOW_ROW_MASK) != 0);
3210 }
3211
3212
3213
3214
3215 public static short cleanRowStart(short rowStart) {
3216 return (short)(rowStart & OFFSET_MASK);
3217 }
3218
3219
3220
3221
3222 public static short findRowStart(ByteBuffer buffer, int rowNum,
3223 JetFormat format)
3224 {
3225 return cleanRowStart(
3226 buffer.getShort(getRowStartOffset(rowNum, format)));
3227 }
3228
3229
3230
3231
3232 public static int getRowStartOffset(int rowNum, JetFormat format)
3233 {
3234 return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * rowNum);
3235 }
3236
3237
3238
3239
3240 public static short findRowEnd(ByteBuffer buffer, int rowNum,
3241 JetFormat format)
3242 {
3243 return (short)((rowNum == 0) ?
3244 format.PAGE_SIZE :
3245 cleanRowStart(
3246 buffer.getShort(getRowEndOffset(rowNum, format))));
3247 }
3248
3249
3250
3251
3252 public static int getRowEndOffset(int rowNum, JetFormat format)
3253 {
3254 return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * (rowNum - 1));
3255 }
3256
3257
3258
3259
3260 public static int getRowSpaceUsage(int rowSize, JetFormat format)
3261 {
3262 return rowSize + format.SIZE_ROW_LOCATION;
3263 }
3264
3265 private void initAutoNumberColumns() {
3266 for(ColumnImpl c : _columns) {
3267 if(c.isAutoNumber()) {
3268 _autoNumColumns.add(c);
3269 }
3270 }
3271 }
3272
3273 private void initCalculatedColumns() {
3274 for(ColumnImpl c : _columns) {
3275 if(c.isCalculated()) {
3276 _calcColEval.add(c);
3277 }
3278 }
3279 }
3280
3281 boolean isThisTable(Identifier identifier) {
3282 String collectionName = identifier.getCollectionName();
3283 return ((collectionName == null) ||
3284 collectionName.equalsIgnoreCase(getName()));
3285 }
3286
3287
3288
3289
3290
3291
3292 public static boolean rowFitsOnDataPage(
3293 int rowLength, ByteBuffer dataPage, JetFormat format)
3294 {
3295 int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
3296 short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
3297 int rowsOnPage = getRowsOnDataPage(dataPage, format);
3298 return ((rowSpaceUsage <= freeSpaceInPage) &&
3299 (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
3300 }
3301
3302
3303
3304
3305
3306 static Object[] dupeRow(Object[] row, int newRowLength) {
3307 Object[] copy = new Object[newRowLength];
3308 System.arraycopy(row, 0, copy, 0, Math.min(row.length, newRowLength));
3309 return copy;
3310 }
3311
3312 String withErrorContext(String msg) {
3313 return withErrorContext(msg, getDatabase(), getName());
3314 }
3315
3316 private static String withErrorContext(String msg, DatabaseImpl db,
3317 String tableName) {
3318 return msg + " (Db=" + db.getName() + ";Table=" + tableName + ")";
3319 }
3320
3321
3322 private enum RowStatus {
3323 INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
3324 }
3325
3326
3327 private enum RowStateStatus {
3328 INIT, AT_HEADER, AT_FINAL;
3329 }
3330
3331
3332
3333
3334 protected static class WriteRowState
3335 {
3336 private int _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
3337
3338 public int getComplexAutoNumber() {
3339 return _complexAutoNumber;
3340 }
3341
3342 public void setComplexAutoNumber(int complexAutoNumber) {
3343 _complexAutoNumber = complexAutoNumber;
3344 }
3345
3346 public void resetAutoNumber() {
3347 _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
3348 }
3349 }
3350
3351
3352
3353
3354
3355 public final class RowState extends WriteRowState
3356 implements ErrorHandler.Location
3357 {
3358
3359 private final TempPageHolder _headerRowBufferH;
3360
3361 private RowIdImpl _headerRowId = RowIdImpl.FIRST_ROW_ID;
3362
3363 private int _rowsOnHeaderPage;
3364
3365 private RowStateStatus _status = RowStateStatus.INIT;
3366
3367 private RowStatus _rowStatus = RowStatus.INIT;
3368
3369 private final TempPageHolder _overflowRowBufferH =
3370 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
3371
3372
3373 private ByteBuffer _finalRowBuffer;
3374
3375
3376 private RowIdImpl _finalRowId = null;
3377
3378 private boolean _haveRowValues;
3379
3380 private Object[] _rowValues;
3381
3382 private NullMask _nullMask;
3383
3384
3385
3386 private int _lastModCount;
3387
3388 private ErrorHandler _errorHandler;
3389
3390 private short[] _varColOffsets;
3391
3392 private RowState(TempBufferHolder.Type headerType) {
3393 _headerRowBufferH = TempPageHolder.newHolder(headerType);
3394 _rowValues = new Object[TableImpl.this.getColumnCount()];
3395 _lastModCount = TableImpl.this._modCount;
3396 }
3397
3398 @Override
3399 public TableImpl getTable() {
3400 return TableImpl.this;
3401 }
3402
3403 public ErrorHandler getErrorHandler() {
3404 return((_errorHandler != null) ? _errorHandler :
3405 getTable().getErrorHandler());
3406 }
3407
3408 public void setErrorHandler(ErrorHandler newErrorHandler) {
3409 _errorHandler = newErrorHandler;
3410 }
3411
3412 public void reset() {
3413 resetAutoNumber();
3414 _finalRowId = null;
3415 _finalRowBuffer = null;
3416 _rowsOnHeaderPage = 0;
3417 _status = RowStateStatus.INIT;
3418 _rowStatus = RowStatus.INIT;
3419 _varColOffsets = null;
3420 _nullMask = null;
3421 if(_haveRowValues) {
3422 Arrays.fill(_rowValues, null);
3423 _haveRowValues = false;
3424 }
3425 }
3426
3427 public boolean isUpToDate() {
3428 return(TableImpl.this._modCount == _lastModCount);
3429 }
3430
3431 private void checkForModification() {
3432 if(!isUpToDate()) {
3433 reset();
3434 _headerRowBufferH.invalidate();
3435 _overflowRowBufferH.invalidate();
3436 int colCount = TableImpl.this.getColumnCount();
3437 if(colCount != _rowValues.length) {
3438
3439 _rowValues = new Object[colCount];
3440 }
3441 _lastModCount = TableImpl.this._modCount;
3442 }
3443 }
3444
3445 private ByteBuffer getFinalPage()
3446 throws IOException
3447 {
3448 if(_finalRowBuffer == null) {
3449
3450 _finalRowBuffer = getHeaderPage();
3451 }
3452 return _finalRowBuffer;
3453 }
3454
3455 public RowIdImpl getFinalRowId() {
3456 if(_finalRowId == null) {
3457 _finalRowId = getHeaderRowId();
3458 }
3459 return _finalRowId;
3460 }
3461
3462 private void setRowStatus(RowStatus rowStatus) {
3463 _rowStatus = rowStatus;
3464 }
3465
3466 public boolean isValid() {
3467 return(_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
3468 }
3469
3470 public boolean isDeleted() {
3471 return(_rowStatus == RowStatus.DELETED);
3472 }
3473
3474 public boolean isOverflow() {
3475 return(_rowStatus == RowStatus.OVERFLOW);
3476 }
3477
3478 public boolean isHeaderPageNumberValid() {
3479 return(_rowStatus.ordinal() > RowStatus.INVALID_PAGE.ordinal());
3480 }
3481
3482 public boolean isHeaderRowNumberValid() {
3483 return(_rowStatus.ordinal() > RowStatus.INVALID_ROW.ordinal());
3484 }
3485
3486 private void setStatus(RowStateStatus status) {
3487 _status = status;
3488 }
3489
3490 public boolean isAtHeaderRow() {
3491 return(_status.ordinal() >= RowStateStatus.AT_HEADER.ordinal());
3492 }
3493
3494 public boolean isAtFinalRow() {
3495 return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
3496 }
3497
3498 private Object setRowCacheValue(int idx, Object value) {
3499 _haveRowValues = true;
3500 _rowValues[idx] = value;
3501 return value;
3502 }
3503
3504 private Object getRowCacheValue(int idx) {
3505 Object value = _rowValues[idx];
3506
3507
3508 return(ColumnImpl.isImmutableValue(value) ? value : null);
3509 }
3510
3511 public Object[] getRowCacheValues() {
3512 return dupeRow(_rowValues, _rowValues.length);
3513 }
3514
3515 public NullMask getNullMask(ByteBuffer rowBuffer) {
3516 if(_nullMask == null) {
3517 _nullMask = getRowNullMask(rowBuffer);
3518 }
3519 return _nullMask;
3520 }
3521
3522 private short[] getVarColOffsets() {
3523 return _varColOffsets;
3524 }
3525
3526 private void setVarColOffsets(short[] varColOffsets) {
3527 _varColOffsets = varColOffsets;
3528 }
3529
3530 public RowIdImpl getHeaderRowId() {
3531 return _headerRowId;
3532 }
3533
3534 public int getRowsOnHeaderPage() {
3535 return _rowsOnHeaderPage;
3536 }
3537
3538 private ByteBuffer getHeaderPage()
3539 throws IOException
3540 {
3541 checkForModification();
3542 return _headerRowBufferH.getPage(getPageChannel());
3543 }
3544
3545 private ByteBuffer setHeaderRow(RowIdImpl rowId)
3546 throws IOException
3547 {
3548 checkForModification();
3549
3550
3551 if(isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
3552 return(isValid() ? getHeaderPage() : null);
3553 }
3554
3555
3556 reset();
3557 _headerRowId = rowId;
3558 _finalRowId = rowId;
3559
3560 int pageNumber = rowId.getPageNumber();
3561 int rowNumber = rowId.getRowNumber();
3562 if((pageNumber < 0) || !_ownedPages.containsPageNumber(pageNumber)) {
3563 setRowStatus(RowStatus.INVALID_PAGE);
3564 return null;
3565 }
3566
3567 _finalRowBuffer = _headerRowBufferH.setPage(getPageChannel(),
3568 pageNumber);
3569 _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer, getFormat());
3570
3571 if((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
3572 setRowStatus(RowStatus.INVALID_ROW);
3573 return null;
3574 }
3575
3576 setRowStatus(RowStatus.VALID);
3577 return _finalRowBuffer;
3578 }
3579
3580 private ByteBuffer setOverflowRow(RowIdImpl rowId)
3581 throws IOException
3582 {
3583
3584
3585 if(!isUpToDate()) {
3586 throw new IllegalStateException(getTable().withErrorContext(
3587 "Table modified while searching?"));
3588 }
3589 if(_rowStatus != RowStatus.OVERFLOW) {
3590 throw new IllegalStateException(getTable().withErrorContext(
3591 "Row is not an overflow row?"));
3592 }
3593 _finalRowId = rowId;
3594 _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(),
3595 rowId.getPageNumber());
3596 return _finalRowBuffer;
3597 }
3598
3599 private Object handleRowError(ColumnImpl column, byte[] columnData,
3600 Exception error)
3601 throws IOException
3602 {
3603 return getErrorHandler().handleRowError(column, columnData,
3604 this, error);
3605 }
3606
3607 @Override
3608 public String toString() {
3609 return CustomToStringStyle.valueBuilder(this)
3610 .append("headerRowId", _headerRowId)
3611 .append("finalRowId", _finalRowId)
3612 .toString();
3613 }
3614 }
3615
3616
3617
3618
3619
3620 private class CalcColEvaluator
3621 {
3622
3623
3624 private final List<ColumnImpl> _calcColumns = new ArrayList<ColumnImpl>(1);
3625 private boolean _sorted;
3626
3627 public void add(ColumnImpl col) {
3628 if(!getDatabase().isEvaluateExpressions()) {
3629 return;
3630 }
3631 _calcColumns.add(col);
3632
3633 _sorted = false;
3634 }
3635
3636 public void reSort() {
3637
3638 _sorted = false;
3639 }
3640
3641 public void calculate(Object[] row) throws IOException {
3642 if(!_sorted) {
3643 sortColumnsByDeps();
3644 _sorted = true;
3645 }
3646
3647 for(ColumnImpl col : _calcColumns) {
3648 Object rowValue = col.getCalculationContext().eval(row);
3649 col.setRowValue(row, rowValue);
3650 }
3651 }
3652
3653 private void sortColumnsByDeps() {
3654
3655
3656
3657
3658
3659
3660
3661
3662 (new TopoSorter<ColumnImpl>(_calcColumns, TopoSorter.REVERSE) {
3663 @Override
3664 protected void getDescendents(ColumnImpl from,
3665 List<ColumnImpl> descendents) {
3666
3667 Set<Identifier> identifiers = new LinkedHashSet<Identifier>();
3668 from.getCalculationContext().collectIdentifiers(identifiers);
3669
3670 for(Identifier identifier : identifiers) {
3671 if(isThisTable(identifier)) {
3672 String colName = identifier.getObjectName();
3673 for(ColumnImpl calcCol : _calcColumns) {
3674
3675 if(calcCol.getName().equalsIgnoreCase(colName)) {
3676 descendents.add(calcCol);
3677 }
3678 }
3679 }
3680 }
3681 }
3682 }).sort();
3683 }
3684 }
3685 }