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 createTooLargeException(rowSize);
2301 }
2302
2303
2304 dataPage = findFreeRowSpace(rowSize, dataPage, pageNumber);
2305 pageNumber = _addRowBufferH.getPageNumber();
2306
2307
2308 int rowNum = getRowsOnDataPage(dataPage, getFormat());
2309
2310 RowIdImplss/impl/RowIdImpl.html#RowIdImpl">RowIdImpl rowId = new RowIdImpl(pageNumber, rowNum);
2311
2312
2313
2314 if(!_indexDatas.isEmpty()) {
2315
2316 IndexData.PendingChange idxChange = null;
2317 try {
2318
2319
2320 _fkEnforcer.addRow(row);
2321
2322
2323 for(IndexData indexData : _indexDatas) {
2324 idxChange = indexData.prepareAddRow(row, rowId, idxChange);
2325 }
2326
2327
2328 IndexData.commitAll(idxChange);
2329
2330 } catch(ConstraintViolationException ce) {
2331 IndexData.rollbackAll(idxChange);
2332 throw ce;
2333 }
2334 }
2335
2336
2337 addDataPageRow(dataPage, rowSize, getFormat(), 0);
2338 dataPage.put(rowData);
2339
2340
2341 if((row.length > numCols) &&
2342 (row[numCols] == ColumnImpl.RETURN_ROW_ID)) {
2343 row[numCols] = rowId;
2344 }
2345
2346 ++updateCount;
2347 }
2348
2349 writeDataPage(dataPage, pageNumber);
2350
2351
2352 updateTableDefinition(rows.size());
2353
2354 } catch(Exception rowWriteFailure) {
2355
2356 boolean isWriteFailure = isWriteFailure(rowWriteFailure);
2357
2358 if(!isWriteFailure && (autoNumAssignCount > updateCount)) {
2359
2360
2361 restoreAutoNumbersFromAdd(rows.get(autoNumAssignCount - 1));
2362 }
2363
2364 if(!isBatchWrite) {
2365
2366 if(rowWriteFailure instanceof IOException) {
2367 throw (IOException)rowWriteFailure;
2368 }
2369 throw (RuntimeException)rowWriteFailure;
2370 }
2371
2372
2373 if(isWriteFailure) {
2374
2375
2376
2377 updateCount = 0;
2378
2379 } else if(updateCount > 0) {
2380
2381
2382 try {
2383
2384 writeDataPage(dataPage, pageNumber);
2385
2386
2387 updateTableDefinition(updateCount);
2388
2389 } catch(Exception flushFailure) {
2390
2391
2392
2393
2394 LOG.warn(withErrorContext(
2395 "Secondary row failure which preceded the write failure"),
2396 rowWriteFailure);
2397 updateCount = 0;
2398 rowWriteFailure = flushFailure;
2399 }
2400 }
2401
2402 throw new BatchUpdateException(
2403 updateCount, withErrorContext("Failed adding rows"),
2404 rowWriteFailure);
2405 }
2406
2407 } finally {
2408 getPageChannel().finishWrite();
2409 }
2410
2411 return rows;
2412 }
2413
2414 private static boolean isWriteFailure(Throwable t) {
2415 while(t != null) {
2416 if((t instanceof IOException) && !(t instanceof JackcessException)) {
2417 return true;
2418 }
2419 t = t.getCause();
2420 }
2421
2422 return false;
2423 }
2424
2425 @Override
2426 public Rowf="../../../../com/healthmarketscience/jackcess/Row.html#Row">Row updateRow(Row row) throws IOException {
2427 return updateRowFromMap(
2428 getDefaultCursor().getRowState(), (RowIdImpl)row.getId(), row);
2429 }
2430
2431
2432
2433
2434
2435
2436
2437
2438 public Object[] updateRow(RowId rowId, Object... row) throws IOException {
2439 return updateRow(
2440 getDefaultCursor().getRowState(), (RowIdImpl)rowId, row);
2441 }
2442
2443
2444
2445
2446
2447
2448
2449 public void updateValue(Column column, RowId rowId, Object value)
2450 throws IOException
2451 {
2452 Object[] row = new Object[_columns.size()];
2453 Arrays.fill(row, Column.KEEP_VALUE);
2454 column.setRowValue(row, value);
2455
2456 updateRow(rowId, row);
2457 }
2458
2459 public <M extends Map<String,Object>> M updateRowFromMap(
2460 RowState rowState, RowIdImpl rowId, M row)
2461 throws IOException
2462 {
2463 Object[] rowValues = updateRow(rowState, rowId, asUpdateRow(row));
2464 returnRowValues(row, rowValues, _columns);
2465 return row;
2466 }
2467
2468
2469
2470
2471
2472 public Object[] updateRow(RowState rowState, RowIdImpl rowId, Object... row)
2473 throws IOException
2474 {
2475 requireValidRowId(rowId);
2476
2477 getPageChannel().startWrite();
2478 try {
2479
2480
2481 ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
2482 int oldRowSize = rowBuffer.remaining();
2483
2484 requireNonDeletedRow(rowState, rowId);
2485
2486
2487
2488 if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
2489 row = dupeRow(row, _columns.size());
2490 }
2491
2492
2493
2494
2495 Map<ColumnImpl,byte[]> keepRawVarValues =
2496 (!_varColumns.isEmpty() ? new HashMap<ColumnImpl,byte[]>() : null);
2497
2498
2499 for(ColumnImpl column : _columns) {
2500
2501 if(column.isAutoNumber()) {
2502
2503 continue;
2504 }
2505
2506 Object rowValue = column.getRowValue(row);
2507 if(rowValue == Column.KEEP_VALUE) {
2508
2509
2510 rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
2511 keepRawVarValues);
2512
2513 } else {
2514
2515
2516 Object oldValue = Column.KEEP_VALUE;
2517 if(_indexColumns.contains(column)) {
2518
2519 oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
2520 null);
2521 } else {
2522 oldValue = rowState.getRowCacheValue(column.getColumnIndex());
2523 }
2524
2525
2526 if(oldValue != rowValue) {
2527
2528 rowValue = column.validate(rowValue);
2529 }
2530 }
2531
2532 column.setRowValue(row, rowValue);
2533 }
2534
2535
2536 handleAutoNumbersForUpdate(row, rowBuffer, rowState);
2537
2538
2539
2540 _calcColEval.calculate(row);
2541
2542
2543 if(_rowValidator != null) {
2544 _rowValidator.validate(row);
2545 }
2546
2547
2548 ByteBuffer newRowData = createRow(
2549 row, _writeRowBufferH.getPageBuffer(getPageChannel()), oldRowSize,
2550 keepRawVarValues);
2551
2552 if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
2553 throw createTooLargeException(newRowData.limit());
2554 }
2555
2556 if(!_indexDatas.isEmpty()) {
2557
2558 IndexData.PendingChange idxChange = null;
2559 try {
2560
2561 Object[] oldRowValues = rowState.getRowCacheValues();
2562
2563
2564 _fkEnforcer.updateRow(oldRowValues, row);
2565
2566
2567 for(IndexData indexData : _indexDatas) {
2568 idxChange = indexData.prepareUpdateRow(oldRowValues, rowId, row,
2569 idxChange);
2570 }
2571
2572
2573 IndexData.commitAll(idxChange);
2574
2575 } catch(ConstraintViolationException ce) {
2576 IndexData.rollbackAll(idxChange);
2577 throw ce;
2578 }
2579 }
2580
2581
2582 rowBuffer.reset();
2583 int rowSize = newRowData.remaining();
2584
2585 ByteBuffer dataPage = null;
2586 int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
2587
2588 if(oldRowSize >= rowSize) {
2589
2590
2591 rowBuffer.put(newRowData);
2592
2593
2594 dataPage = rowState.getFinalPage();
2595 pageNumber = rowState.getFinalRowId().getPageNumber();
2596
2597 } else {
2598
2599
2600 dataPage = findFreeRowSpace(rowSize, null,
2601 PageChannel.INVALID_PAGE_NUMBER);
2602 pageNumber = _addRowBufferH.getPageNumber();
2603
2604 RowIdImpl headerRowId = rowState.getHeaderRowId();
2605 ByteBuffer headerPage = rowState.getHeaderPage();
2606 if(pageNumber == headerRowId.getPageNumber()) {
2607
2608 dataPage = headerPage;
2609 }
2610
2611
2612
2613 int rowNum = addDataPageRow(dataPage, rowSize, getFormat(),
2614 DELETED_ROW_MASK);
2615 dataPage.put(newRowData);
2616
2617
2618
2619 rowBuffer = PageChannel.narrowBuffer(
2620 headerPage,
2621 findRowStart(headerPage, headerRowId.getRowNumber(), getFormat()),
2622 findRowEnd(headerPage, headerRowId.getRowNumber(), getFormat()));
2623 rowBuffer.put((byte)rowNum);
2624 ByteUtil.put3ByteInt(rowBuffer, pageNumber);
2625 ByteUtil.clearRemaining(rowBuffer);
2626
2627
2628 int headerRowIndex = getRowStartOffset(headerRowId.getRowNumber(),
2629 getFormat());
2630 headerPage.putShort(headerRowIndex,
2631 (short)(headerPage.getShort(headerRowIndex)
2632 | OVERFLOW_ROW_MASK));
2633 if(pageNumber != headerRowId.getPageNumber()) {
2634 writeDataPage(headerPage, headerRowId.getPageNumber());
2635 }
2636 }
2637
2638 writeDataPage(dataPage, pageNumber);
2639
2640 updateTableDefinition(0);
2641
2642 } finally {
2643 getPageChannel().finishWrite();
2644 }
2645
2646 return row;
2647 }
2648
2649 private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
2650 int pageNumber)
2651 throws IOException
2652 {
2653
2654 boolean modifiedPage = true;
2655
2656 if(dataPage == null) {
2657
2658
2659 dataPage = findFreeRowSpace(_ownedPages, _freeSpacePages,
2660 _addRowBufferH);
2661
2662 if(dataPage == null) {
2663
2664 return newDataPage();
2665 }
2666
2667
2668 pageNumber = _addRowBufferH.getPageNumber();
2669
2670 modifiedPage = false;
2671 }
2672
2673 if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
2674
2675
2676 if(modifiedPage) {
2677 writeDataPage(dataPage, pageNumber);
2678 }
2679 _freeSpacePages.removePageNumber(pageNumber);
2680
2681 dataPage = newDataPage();
2682 }
2683
2684 return dataPage;
2685 }
2686
2687 static ByteBuffer findFreeRowSpace(
2688 UsageMap./../../com/healthmarketscience/jackcess/impl/UsageMap.html#UsageMap">UsageMap ownedPages, UsageMap freeSpacePages,
2689 TempPageHolder rowBufferH)
2690 throws IOException
2691 {
2692
2693
2694 UsageMap.PageCursor revPageCursor = ownedPages.cursor();
2695 revPageCursor.afterLast();
2696 while(true) {
2697 int tmpPageNumber = revPageCursor.getPreviousPage();
2698 if(tmpPageNumber < 0) {
2699 break;
2700 }
2701
2702 if(!freeSpacePages.containsPageNumber(tmpPageNumber)) {
2703 continue;
2704 }
2705 ByteBuffer dataPage = rowBufferH.setPage(ownedPages.getPageChannel(),
2706 tmpPageNumber);
2707 if(dataPage.get() == PageTypes.DATA) {
2708
2709 return dataPage;
2710 }
2711 }
2712
2713 return null;
2714 }
2715
2716
2717
2718
2719 private void updateTableDefinition(int rowCountInc) throws IOException
2720 {
2721
2722 ByteBuffer tdefPage = _tableDefBufferH.setPage(getPageChannel(),
2723 _tableDefPageNumber);
2724
2725
2726 _rowCount += rowCountInc;
2727 tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
2728 tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastLongAutoNumber);
2729 int ctypeOff = getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER;
2730 if(ctypeOff >= 0) {
2731 tdefPage.putInt(ctypeOff, _lastComplexTypeAutoNumber);
2732 }
2733
2734
2735 for (IndexData indexData : _indexDatas) {
2736
2737
2738 tdefPage.putInt(indexData.getUniqueEntryCountOffset(),
2739 indexData.getUniqueEntryCount());
2740
2741 indexData.update();
2742 }
2743
2744
2745 getPageChannel().writePage(tdefPage, _tableDefPageNumber);
2746 }
2747
2748
2749
2750
2751
2752 private ByteBuffer newDataPage() throws IOException {
2753 ByteBuffer dataPage = _addRowBufferH.setNewPage(getPageChannel());
2754 dataPage.put(PageTypes.DATA);
2755 dataPage.put((byte) 1);
2756 dataPage.putShort((short)getFormat().DATA_PAGE_INITIAL_FREE_SPACE);
2757 dataPage.putInt(_tableDefPageNumber);
2758 dataPage.putInt(0);
2759 dataPage.putShort((short)0);
2760 int pageNumber = _addRowBufferH.getPageNumber();
2761 getPageChannel().writePage(dataPage, pageNumber);
2762 _ownedPages.addPageNumber(pageNumber);
2763 _freeSpacePages.addPageNumber(pageNumber);
2764 return dataPage;
2765 }
2766
2767
2768 protected ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
2769 throws IOException
2770 {
2771 return createRow(rowArray, buffer, 0,
2772 Collections.<ColumnImpl,byte[]>emptyMap());
2773 }
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785 private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
2786 int minRowSize,
2787 Map<ColumnImpl,byte[]> rawVarValues)
2788 throws IOException
2789 {
2790 buffer.putShort(_maxColumnCount);
2791 NullMask/impl/NullMask.html#NullMask">NullMask nullMask = new NullMask(_maxColumnCount);
2792
2793
2794 int fixedDataStart = buffer.position();
2795 int fixedDataEnd = fixedDataStart;
2796 for (ColumnImpl col : _columns) {
2797
2798 if(col.isVariableLength()) {
2799 continue;
2800 }
2801
2802 Object rowValue = col.getRowValue(rowArray);
2803
2804 if (col.storeInNullMask()) {
2805
2806 if(col.writeToNullMask(rowValue)) {
2807 nullMask.markNotNull(col);
2808 }
2809 rowValue = null;
2810 }
2811
2812 if(rowValue != null) {
2813
2814
2815 nullMask.markNotNull(col);
2816
2817
2818 buffer.position(fixedDataStart + col.getFixedDataOffset());
2819 buffer.put(col.write(rowValue, 0));
2820 }
2821
2822
2823
2824
2825 buffer.position(fixedDataStart + col.getFixedDataOffset() +
2826 col.getLength());
2827
2828
2829 if(buffer.position() > fixedDataEnd) {
2830 fixedDataEnd = buffer.position();
2831 }
2832
2833 }
2834
2835
2836 buffer.position(fixedDataEnd);
2837
2838
2839 if(_maxVarColumnCount > 0) {
2840
2841 int maxRowSize = getFormat().MAX_ROW_SIZE;
2842
2843
2844
2845 maxRowSize -= buffer.position();
2846
2847 int trailerSize = (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
2848 maxRowSize -= trailerSize;
2849
2850
2851
2852
2853 for (ColumnImpl varCol : _varColumns) {
2854 if((varCol.getType().isLongValue()) &&
2855 (varCol.getRowValue(rowArray) != null)) {
2856 maxRowSize -= getFormat().SIZE_LONG_VALUE_DEF;
2857 }
2858 }
2859
2860
2861 short[] varColumnOffsets = new short[_maxVarColumnCount];
2862 int varColumnOffsetsIndex = 0;
2863 for (ColumnImpl varCol : _varColumns) {
2864 short offset = (short) buffer.position();
2865 Object rowValue = varCol.getRowValue(rowArray);
2866 if (rowValue != null) {
2867
2868 nullMask.markNotNull(varCol);
2869
2870 byte[] rawValue = null;
2871 ByteBuffer varDataBuf = null;
2872 if(((rawValue = rawVarValues.get(varCol)) != null) &&
2873 (rawValue.length <= maxRowSize)) {
2874
2875 varDataBuf = ByteBuffer.wrap(rawValue);
2876 } else {
2877
2878 varDataBuf = varCol.write(rowValue, maxRowSize);
2879 }
2880
2881 maxRowSize -= varDataBuf.remaining();
2882 if(varCol.getType().isLongValue()) {
2883
2884
2885 maxRowSize += getFormat().SIZE_LONG_VALUE_DEF;
2886 }
2887 try {
2888 buffer.put(varDataBuf);
2889 } catch(BufferOverflowException e) {
2890
2891
2892 throw createTooLargeException(buffer.limit());
2893 }
2894 }
2895
2896
2897 while(varColumnOffsetsIndex <= varCol.getVarLenTableIndex()) {
2898 varColumnOffsets[varColumnOffsetsIndex++] = offset;
2899 }
2900 }
2901
2902
2903 while(varColumnOffsetsIndex < varColumnOffsets.length) {
2904 varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer.position();
2905 }
2906
2907
2908 int eod = buffer.position();
2909
2910 if(buffer.remaining() < trailerSize) {
2911
2912 throw createTooLargeException(eod + trailerSize);
2913 }
2914
2915
2916 padRowBuffer(buffer, minRowSize, trailerSize);
2917
2918 buffer.putShort((short) eod);
2919
2920
2921
2922 for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
2923 buffer.putShort(varColumnOffsets[i]);
2924 }
2925 buffer.putShort(_maxVarColumnCount);
2926
2927 } else {
2928
2929
2930 padRowBuffer(buffer, minRowSize, nullMask.byteSize());
2931 }
2932
2933 nullMask.write(buffer);
2934 buffer.flip();
2935 return buffer;
2936 }
2937
2938 private InvalidValueException createTooLargeException(int size) {
2939 return new InvalidValueException(
2940 withErrorContext(
2941 "Row size " + size + " is too large (max " +
2942 getFormat().MAX_ROW_SIZE + ")"));
2943 }
2944
2945
2946
2947
2948 private void handleAutoNumbersForAdd(Object[] row, WriteRowState writeRowState)
2949 throws IOException
2950 {
2951 if(_autoNumColumns.isEmpty()) {
2952 return;
2953 }
2954
2955 boolean enableInsert = isAllowAutoNumberInsert();
2956 writeRowState.resetAutoNumber();
2957 for(ColumnImpl col : _autoNumColumns) {
2958
2959
2960
2961 Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
2962
2963 ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator();
2964 Object rowValue = ((inRowValue == null) ?
2965 autoNumGen.getNext(writeRowState) :
2966 autoNumGen.handleInsert(writeRowState, inRowValue));
2967
2968 col.setRowValue(row, rowValue);
2969 }
2970 }
2971
2972
2973
2974
2975 private void handleAutoNumbersForUpdate(Object[] row, ByteBuffer rowBuffer,
2976 RowState rowState)
2977 throws IOException
2978 {
2979 if(_autoNumColumns.isEmpty()) {
2980 return;
2981 }
2982
2983 boolean enableInsert = isAllowAutoNumberInsert();
2984 rowState.resetAutoNumber();
2985 for(ColumnImpl col : _autoNumColumns) {
2986
2987
2988
2989 Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
2990
2991 Object rowValue =
2992 ((inRowValue == null) ?
2993 getRowColumn(getFormat(), rowBuffer, col, rowState, null) :
2994 col.getAutoNumberGenerator().handleInsert(rowState, inRowValue));
2995
2996 col.setRowValue(row, rowValue);
2997 }
2998 }
2999
3000
3001
3002
3003
3004 private static Object getInputAutoNumberRowValue(
3005 boolean enableInsert, ColumnImpl col, Object[] row)
3006 {
3007 if(!enableInsert) {
3008 return null;
3009 }
3010
3011 Object inRowValue = col.getRowValue(row);
3012 if((inRowValue == Column.KEEP_VALUE) || (inRowValue == Column.AUTO_NUMBER)) {
3013
3014 inRowValue = null;
3015 }
3016 return inRowValue;
3017 }
3018
3019
3020
3021
3022 private void restoreAutoNumbersFromAdd(Object[] row)
3023 {
3024 if(_autoNumColumns.isEmpty()) {
3025 return;
3026 }
3027
3028 for(ColumnImpl col : _autoNumColumns) {
3029
3030 col.getAutoNumberGenerator().restoreLast(col.getRowValue(row));
3031 }
3032 }
3033
3034 private static void padRowBuffer(ByteBuffer buffer, int minRowSize,
3035 int trailerSize)
3036 {
3037 int pos = buffer.position();
3038 if((pos + trailerSize) < minRowSize) {
3039
3040 int padSize = minRowSize - (pos + trailerSize);
3041 ByteUtil.clearRange(buffer, pos, pos + padSize);
3042 ByteUtil.forward(buffer, padSize);
3043 }
3044 }
3045
3046 @Override
3047 public int getRowCount() {
3048 return _rowCount;
3049 }
3050
3051 int getNextLongAutoNumber() {
3052
3053 return ++_lastLongAutoNumber;
3054 }
3055
3056 int getLastLongAutoNumber() {
3057
3058 return _lastLongAutoNumber;
3059 }
3060
3061 void adjustLongAutoNumber(int inLongAutoNumber) {
3062 if(inLongAutoNumber > _lastLongAutoNumber) {
3063 _lastLongAutoNumber = inLongAutoNumber;
3064 }
3065 }
3066
3067 void restoreLastLongAutoNumber(int lastLongAutoNumber) {
3068
3069 _lastLongAutoNumber = lastLongAutoNumber - 1;
3070 }
3071
3072 int getNextComplexTypeAutoNumber() {
3073
3074 return ++_lastComplexTypeAutoNumber;
3075 }
3076
3077 int getLastComplexTypeAutoNumber() {
3078
3079 return _lastComplexTypeAutoNumber;
3080 }
3081
3082 void adjustComplexTypeAutoNumber(int inComplexTypeAutoNumber) {
3083 if(inComplexTypeAutoNumber > _lastComplexTypeAutoNumber) {
3084 _lastComplexTypeAutoNumber = inComplexTypeAutoNumber;
3085 }
3086 }
3087
3088 void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) {
3089
3090 _lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1;
3091 }
3092
3093 @Override
3094 public String toString() {
3095 return CustomToStringStyle.builder(this)
3096 .append("type", (_tableType + (!isSystem() ? " (USER)" : " (SYSTEM)")))
3097 .append("name", _name)
3098 .append("rowCount", _rowCount)
3099 .append("columnCount", _columns.size())
3100 .append("indexCount(data)", _indexCount)
3101 .append("logicalIndexCount", _logicalIndexCount)
3102 .append("validator", CustomToStringStyle.ignoreNull(_rowValidator))
3103 .append("columns", _columns)
3104 .append("indexes", _indexes)
3105 .append("ownedPages", _ownedPages)
3106 .toString();
3107 }
3108
3109
3110
3111
3112
3113
3114 public String display() throws IOException {
3115 return display(Long.MAX_VALUE);
3116 }
3117
3118
3119
3120
3121
3122
3123
3124 public String display(long limit) throws IOException {
3125 reset();
3126 StringWriter rtn = new StringWriter();
3127 new ExportUtil.Builder(getDefaultCursor()).setDelimiter("\t").setHeader(true)
3128 .exportWriter(new BufferedWriter(rtn));
3129 return rtn.toString();
3130 }
3131
3132
3133
3134
3135
3136
3137
3138 public static int addDataPageRow(ByteBuffer dataPage,
3139 int rowSize,
3140 JetFormat format,
3141 int rowFlags)
3142 {
3143 int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
3144
3145
3146 short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
3147 dataPage.putShort(format.OFFSET_FREE_SPACE, (short) (freeSpaceInPage -
3148 rowSpaceUsage));
3149
3150
3151 short rowCount = dataPage.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
3152 dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
3153 (short) (rowCount + 1));
3154
3155
3156 short rowLocation = findRowEnd(dataPage, rowCount, format);
3157 rowLocation -= rowSize;
3158
3159
3160 dataPage.putShort(getRowStartOffset(rowCount, format),
3161 (short)(rowLocation | rowFlags));
3162
3163
3164 dataPage.position(rowLocation);
3165
3166 return rowCount;
3167 }
3168
3169
3170
3171
3172
3173 static int getRowsOnDataPage(ByteBuffer rowBuffer, JetFormat format)
3174 {
3175 int rowsOnPage = 0;
3176 if((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
3177 rowsOnPage = rowBuffer.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
3178 }
3179 return rowsOnPage;
3180 }
3181
3182
3183
3184
3185 private void requireValidRowId(RowIdImpl rowId) {
3186 if(!rowId.isValid()) {
3187 throw new IllegalArgumentException(withErrorContext(
3188 "Given rowId is invalid: " + rowId));
3189 }
3190 }
3191
3192
3193
3194
3195 private void requireNonDeletedRow(RowState rowState, RowIdImpl rowId)
3196 {
3197 if(!rowState.isValid()) {
3198 throw new IllegalArgumentException(withErrorContext(
3199 "Given rowId is invalid for this table: " + rowId));
3200 }
3201 if(rowState.isDeleted()) {
3202 throw new IllegalStateException(withErrorContext(
3203 "Row is deleted: " + rowId));
3204 }
3205 }
3206
3207
3208
3209
3210 public static boolean isDeletedRow(short rowStart) {
3211 return ((rowStart & DELETED_ROW_MASK) != 0);
3212 }
3213
3214
3215
3216
3217 public static boolean isOverflowRow(short rowStart) {
3218 return ((rowStart & OVERFLOW_ROW_MASK) != 0);
3219 }
3220
3221
3222
3223
3224 public static short cleanRowStart(short rowStart) {
3225 return (short)(rowStart & OFFSET_MASK);
3226 }
3227
3228
3229
3230
3231 public static short findRowStart(ByteBuffer buffer, int rowNum,
3232 JetFormat format)
3233 {
3234 return cleanRowStart(
3235 buffer.getShort(getRowStartOffset(rowNum, format)));
3236 }
3237
3238
3239
3240
3241 public static int getRowStartOffset(int rowNum, JetFormat format)
3242 {
3243 return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * rowNum);
3244 }
3245
3246
3247
3248
3249 public static short findRowEnd(ByteBuffer buffer, int rowNum,
3250 JetFormat format)
3251 {
3252 return (short)((rowNum == 0) ?
3253 format.PAGE_SIZE :
3254 cleanRowStart(
3255 buffer.getShort(getRowEndOffset(rowNum, format))));
3256 }
3257
3258
3259
3260
3261 public static int getRowEndOffset(int rowNum, JetFormat format)
3262 {
3263 return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * (rowNum - 1));
3264 }
3265
3266
3267
3268
3269 public static int getRowSpaceUsage(int rowSize, JetFormat format)
3270 {
3271 return rowSize + format.SIZE_ROW_LOCATION;
3272 }
3273
3274 private void initAutoNumberColumns() {
3275 for(ColumnImpl c : _columns) {
3276 if(c.isAutoNumber()) {
3277 _autoNumColumns.add(c);
3278 }
3279 }
3280 }
3281
3282 private void initCalculatedColumns() {
3283 for(ColumnImpl c : _columns) {
3284 if(c.isCalculated()) {
3285 _calcColEval.add(c);
3286 }
3287 }
3288 }
3289
3290 boolean isThisTable(Identifier identifier) {
3291 String collectionName = identifier.getCollectionName();
3292 return ((collectionName == null) ||
3293 collectionName.equalsIgnoreCase(getName()));
3294 }
3295
3296
3297
3298
3299
3300
3301 public static boolean rowFitsOnDataPage(
3302 int rowLength, ByteBuffer dataPage, JetFormat format)
3303 {
3304 int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
3305 short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
3306 int rowsOnPage = getRowsOnDataPage(dataPage, format);
3307 return ((rowSpaceUsage <= freeSpaceInPage) &&
3308 (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
3309 }
3310
3311
3312
3313
3314
3315 static Object[] dupeRow(Object[] row, int newRowLength) {
3316 Object[] copy = new Object[newRowLength];
3317 System.arraycopy(row, 0, copy, 0, Math.min(row.length, newRowLength));
3318 return copy;
3319 }
3320
3321 String withErrorContext(String msg) {
3322 return withErrorContext(msg, getDatabase(), getName());
3323 }
3324
3325 private static String withErrorContext(String msg, DatabaseImpl db,
3326 String tableName) {
3327 return msg + " (Db=" + db.getName() + ";Table=" + tableName + ")";
3328 }
3329
3330
3331 private enum RowStatus {
3332 INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
3333 }
3334
3335
3336 private enum RowStateStatus {
3337 INIT, AT_HEADER, AT_FINAL;
3338 }
3339
3340
3341
3342
3343 protected static class WriteRowState
3344 {
3345 private int _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
3346
3347 public int getComplexAutoNumber() {
3348 return _complexAutoNumber;
3349 }
3350
3351 public void setComplexAutoNumber(int complexAutoNumber) {
3352 _complexAutoNumber = complexAutoNumber;
3353 }
3354
3355 public void resetAutoNumber() {
3356 _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
3357 }
3358 }
3359
3360
3361
3362
3363
3364 public final class RowState extends WriteRowState
3365 implements ErrorHandler.Location
3366 {
3367
3368 private final TempPageHolder _headerRowBufferH;
3369
3370 private RowIdImpl _headerRowId = RowIdImpl.FIRST_ROW_ID;
3371
3372 private int _rowsOnHeaderPage;
3373
3374 private RowStateStatus _status = RowStateStatus.INIT;
3375
3376 private RowStatus _rowStatus = RowStatus.INIT;
3377
3378 private final TempPageHolder _overflowRowBufferH =
3379 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
3380
3381
3382 private ByteBuffer _finalRowBuffer;
3383
3384
3385 private RowIdImpl _finalRowId = null;
3386
3387 private boolean _haveRowValues;
3388
3389 private Object[] _rowValues;
3390
3391 private NullMask _nullMask;
3392
3393
3394
3395 private int _lastModCount;
3396
3397 private ErrorHandler _errorHandler;
3398
3399 private short[] _varColOffsets;
3400
3401 private RowState(TempBufferHolder.Type headerType) {
3402 _headerRowBufferH = TempPageHolder.newHolder(headerType);
3403 _rowValues = new Object[TableImpl.this.getColumnCount()];
3404 _lastModCount = TableImpl.this._modCount;
3405 }
3406
3407 @Override
3408 public TableImpl getTable() {
3409 return TableImpl.this;
3410 }
3411
3412 public ErrorHandler getErrorHandler() {
3413 return((_errorHandler != null) ? _errorHandler :
3414 getTable().getErrorHandler());
3415 }
3416
3417 public void setErrorHandler(ErrorHandler newErrorHandler) {
3418 _errorHandler = newErrorHandler;
3419 }
3420
3421 public void reset() {
3422 resetAutoNumber();
3423 _finalRowId = null;
3424 _finalRowBuffer = null;
3425 _rowsOnHeaderPage = 0;
3426 _status = RowStateStatus.INIT;
3427 _rowStatus = RowStatus.INIT;
3428 _varColOffsets = null;
3429 _nullMask = null;
3430 if(_haveRowValues) {
3431 Arrays.fill(_rowValues, null);
3432 _haveRowValues = false;
3433 }
3434 }
3435
3436 public boolean isUpToDate() {
3437 return(TableImpl.this._modCount == _lastModCount);
3438 }
3439
3440 private void checkForModification() {
3441 if(!isUpToDate()) {
3442 reset();
3443 _headerRowBufferH.invalidate();
3444 _overflowRowBufferH.invalidate();
3445 int colCount = TableImpl.this.getColumnCount();
3446 if(colCount != _rowValues.length) {
3447
3448 _rowValues = new Object[colCount];
3449 }
3450 _lastModCount = TableImpl.this._modCount;
3451 }
3452 }
3453
3454 private ByteBuffer getFinalPage()
3455 throws IOException
3456 {
3457 if(_finalRowBuffer == null) {
3458
3459 _finalRowBuffer = getHeaderPage();
3460 }
3461 return _finalRowBuffer;
3462 }
3463
3464 public RowIdImpl getFinalRowId() {
3465 if(_finalRowId == null) {
3466 _finalRowId = getHeaderRowId();
3467 }
3468 return _finalRowId;
3469 }
3470
3471 private void setRowStatus(RowStatus rowStatus) {
3472 _rowStatus = rowStatus;
3473 }
3474
3475 public boolean isValid() {
3476 return(_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
3477 }
3478
3479 public boolean isDeleted() {
3480 return(_rowStatus == RowStatus.DELETED);
3481 }
3482
3483 public boolean isOverflow() {
3484 return(_rowStatus == RowStatus.OVERFLOW);
3485 }
3486
3487 public boolean isHeaderPageNumberValid() {
3488 return(_rowStatus.ordinal() > RowStatus.INVALID_PAGE.ordinal());
3489 }
3490
3491 public boolean isHeaderRowNumberValid() {
3492 return(_rowStatus.ordinal() > RowStatus.INVALID_ROW.ordinal());
3493 }
3494
3495 private void setStatus(RowStateStatus status) {
3496 _status = status;
3497 }
3498
3499 public boolean isAtHeaderRow() {
3500 return(_status.ordinal() >= RowStateStatus.AT_HEADER.ordinal());
3501 }
3502
3503 public boolean isAtFinalRow() {
3504 return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
3505 }
3506
3507 private Object setRowCacheValue(int idx, Object value) {
3508 _haveRowValues = true;
3509 _rowValues[idx] = value;
3510 return value;
3511 }
3512
3513 private Object getRowCacheValue(int idx) {
3514 Object value = _rowValues[idx];
3515
3516
3517 return(ColumnImpl.isImmutableValue(value) ? value : null);
3518 }
3519
3520 public Object[] getRowCacheValues() {
3521 return dupeRow(_rowValues, _rowValues.length);
3522 }
3523
3524 public NullMask getNullMask(ByteBuffer rowBuffer) {
3525 if(_nullMask == null) {
3526 _nullMask = getRowNullMask(rowBuffer);
3527 }
3528 return _nullMask;
3529 }
3530
3531 private short[] getVarColOffsets() {
3532 return _varColOffsets;
3533 }
3534
3535 private void setVarColOffsets(short[] varColOffsets) {
3536 _varColOffsets = varColOffsets;
3537 }
3538
3539 public RowIdImpl getHeaderRowId() {
3540 return _headerRowId;
3541 }
3542
3543 public int getRowsOnHeaderPage() {
3544 return _rowsOnHeaderPage;
3545 }
3546
3547 private ByteBuffer getHeaderPage()
3548 throws IOException
3549 {
3550 checkForModification();
3551 return _headerRowBufferH.getPage(getPageChannel());
3552 }
3553
3554 private ByteBuffer setHeaderRow(RowIdImpl rowId)
3555 throws IOException
3556 {
3557 checkForModification();
3558
3559
3560 if(isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
3561 return(isValid() ? getHeaderPage() : null);
3562 }
3563
3564
3565 reset();
3566 _headerRowId = rowId;
3567 _finalRowId = rowId;
3568
3569 int pageNumber = rowId.getPageNumber();
3570 int rowNumber = rowId.getRowNumber();
3571 if((pageNumber < 0) || !_ownedPages.containsPageNumber(pageNumber)) {
3572 setRowStatus(RowStatus.INVALID_PAGE);
3573 return null;
3574 }
3575
3576 _finalRowBuffer = _headerRowBufferH.setPage(getPageChannel(),
3577 pageNumber);
3578 _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer, getFormat());
3579
3580 if((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
3581 setRowStatus(RowStatus.INVALID_ROW);
3582 return null;
3583 }
3584
3585 setRowStatus(RowStatus.VALID);
3586 return _finalRowBuffer;
3587 }
3588
3589 private ByteBuffer setOverflowRow(RowIdImpl rowId)
3590 throws IOException
3591 {
3592
3593
3594 if(!isUpToDate()) {
3595 throw new IllegalStateException(getTable().withErrorContext(
3596 "Table modified while searching?"));
3597 }
3598 if(_rowStatus != RowStatus.OVERFLOW) {
3599 throw new IllegalStateException(getTable().withErrorContext(
3600 "Row is not an overflow row?"));
3601 }
3602 _finalRowId = rowId;
3603 _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(),
3604 rowId.getPageNumber());
3605 return _finalRowBuffer;
3606 }
3607
3608 private Object handleRowError(ColumnImpl column, byte[] columnData,
3609 Exception error)
3610 throws IOException
3611 {
3612 return getErrorHandler().handleRowError(column, columnData,
3613 this, error);
3614 }
3615
3616 @Override
3617 public String toString() {
3618 return CustomToStringStyle.valueBuilder(this)
3619 .append("headerRowId", _headerRowId)
3620 .append("finalRowId", _finalRowId)
3621 .toString();
3622 }
3623 }
3624
3625
3626
3627
3628
3629 private class CalcColEvaluator
3630 {
3631
3632
3633 private final List<ColumnImpl> _calcColumns = new ArrayList<ColumnImpl>(1);
3634 private boolean _sorted;
3635
3636 public void add(ColumnImpl col) {
3637 if(!getDatabase().isEvaluateExpressions()) {
3638 return;
3639 }
3640 _calcColumns.add(col);
3641
3642 _sorted = false;
3643 }
3644
3645 public void reSort() {
3646
3647 _sorted = false;
3648 }
3649
3650 public void calculate(Object[] row) throws IOException {
3651 if(!_sorted) {
3652 sortColumnsByDeps();
3653 _sorted = true;
3654 }
3655
3656 for(ColumnImpl col : _calcColumns) {
3657 Object rowValue = col.getCalculationContext().eval(row);
3658 col.setRowValue(row, rowValue);
3659 }
3660 }
3661
3662 private void sortColumnsByDeps() {
3663
3664
3665
3666
3667
3668
3669
3670
3671 (new TopoSorter<ColumnImpl>(_calcColumns, TopoSorter.REVERSE) {
3672 @Override
3673 protected void getDescendents(ColumnImpl from,
3674 List<ColumnImpl> descendents) {
3675
3676 Set<Identifier> identifiers = new LinkedHashSet<Identifier>();
3677 from.getCalculationContext().collectIdentifiers(identifiers);
3678
3679 for(Identifier identifier : identifiers) {
3680 if(isThisTable(identifier)) {
3681 String colName = identifier.getObjectName();
3682 for(ColumnImpl calcCol : _calcColumns) {
3683
3684 if(calcCol.getName().equalsIgnoreCase(colName)) {
3685 descendents.add(calcCol);
3686 }
3687 }
3688 }
3689 }
3690 }
3691 }).sort();
3692 }
3693 }
3694 }