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