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.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamException;
24 import java.io.Reader;
25 import java.io.Serializable;
26 import java.lang.System.Logger;
27 import java.math.BigDecimal;
28 import java.math.BigInteger;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 import java.nio.CharBuffer;
32 import java.nio.charset.Charset;
33 import java.time.DateTimeException;
34 import java.time.Duration;
35 import java.time.Instant;
36 import java.time.LocalDate;
37 import java.time.LocalDateTime;
38 import java.time.LocalTime;
39 import java.time.ZoneId;
40 import java.time.ZonedDateTime;
41 import java.time.temporal.ChronoUnit;
42 import java.time.temporal.TemporalAccessor;
43 import java.time.temporal.TemporalQueries;
44 import java.util.Calendar;
45 import java.util.Collection;
46 import java.util.Comparator;
47 import java.util.Date;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.TimeZone;
51 import java.util.UUID;
52 import java.util.regex.Matcher;
53 import java.util.regex.Pattern;
54
55 import com.healthmarketscience.jackcess.Column;
56 import com.healthmarketscience.jackcess.ColumnBuilder;
57 import com.healthmarketscience.jackcess.DataType;
58 import com.healthmarketscience.jackcess.DateTimeType;
59 import com.healthmarketscience.jackcess.InvalidValueException;
60 import com.healthmarketscience.jackcess.PropertyMap;
61 import com.healthmarketscience.jackcess.Table;
62 import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
63 import com.healthmarketscience.jackcess.complex.ComplexDataType;
64 import com.healthmarketscience.jackcess.complex.ComplexValue;
65 import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
66 import com.healthmarketscience.jackcess.expr.Identifier;
67 import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
68 import com.healthmarketscience.jackcess.impl.expr.LocaleUtil;
69 import com.healthmarketscience.jackcess.util.ColumnValidator;
70 import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
71
72
73
74
75
76
77 public class ColumnImpl implements Column, DateTimeContext
78 {
79
80 protected static final Logger LOG = System.getLogger(ColumnImpl.class.getName());
81
82
83
84
85
86
87
88
89 public static final Object RETURN_ROW_ID = "<RETURN_ROW_ID>";
90
91
92 static final Comparator<ColumnImpl> DEFAULT_ORDER_COMPARATOR =
93 Comparator.comparingInt(ColumnImpl::getColumnNumber);
94
95
96 static final Comparator<ColumnImpl> DISPLAY_ORDER_COMPARATOR =
97 Comparator.comparingInt(ColumnImpl::getDisplayIndex);
98
99
100
101
102 private static final long MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
103 private static final long SECONDS_PER_DAY = (24L * 60L * 60L);
104 private static final long NANOS_PER_SECOND = 1_000_000_000L;
105
106
107 private static final long[] TIME_ROUNDING_NANOS =
108 {1_000_000L, 100_000L, 10_000L, 1_000L, 100L, 10L, 1L};
109
110
111
112
113
114
115
116 static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
117 25569L * MILLISECONDS_PER_DAY;
118
119 public static final LocalDate BASE_LD = LocalDate.of(1899, 12, 30);
120 public static final LocalTime BASE_LT = LocalTime.of(0, 0);
121 public static final LocalDateTime BASE_LDT = LocalDateTime.of(BASE_LD, BASE_LT);
122
123 private static final LocalDate BASE_EXT_LD = LocalDate.of(1, 1, 1);
124 private static final LocalTime BASE_EXT_LT = LocalTime.of(0, 0);
125 private static final LocalDateTime BASE_EXT_LDT =
126 LocalDateTime.of(BASE_EXT_LD, BASE_EXT_LT);
127 private static final byte[] EXT_LDT_TRAILER = {':', '7', 0x00};
128
129 private static final DateTimeFactory DEF_DATE_TIME_FACTORY =
130 new DefaultDateTimeFactory();
131
132 static final DateTimeFactory LDT_DATE_TIME_FACTORY =
133 new LDTDateTimeFactory();
134
135
136
137
138
139 public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
140
141
142
143
144
145 public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
146
147
148
149
150
151 public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
152
153
154
155
156
157 public static final byte HYPERLINK_FLAG_MASK = (byte)0x80;
158
159
160
161
162
163 public static final byte UPDATABLE_FLAG_MASK = (byte)0x02;
164
165
166
167
168
169
170
171 public static final byte HIDDEN_FLAG_MASK = (byte)0x10;
172
173
174
175
176
177
178
179 public static final byte SECURITY_IDENTIFIER_FLAG_MASK = (byte)0x20;
180
181
182
183 protected static final byte COMPRESSED_UNICODE_EXT_FLAG_MASK = (byte)0x01;
184 private static final byte CALCULATED_EXT_FLAG_MASK = (byte)0xC0;
185
186
187
188
189
190
191
192 public static final byte COMPLEX_FK_EXT_FLAG_MASK = (byte)0x08;
193
194
195
196
197
198
199
200 public static final byte VERSION_HISTORY_EXT_FLAG_MASK = (byte)0x20;
201
202 static final byte NUMERIC_NEGATIVE_BYTE = (byte)0x80;
203
204
205 private static final short GENERAL_SORT_ORDER_VALUE = 1033;
206
207
208
209
210
211 public static final SortOrder GENERAL_97_SORT_ORDER =
212 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)-1);
213
214
215
216
217
218 public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
219 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)0);
220
221
222
223
224
225 public static final SortOrder GENERAL_SORT_ORDER =
226 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)1);
227
228
229
230 private static final Pattern GUID_PATTERN = Pattern.compile("\\s*[{]?([\\p{XDigit}]{8})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{12})[}]?\\s*");
231
232
233 private static final byte[] TEXT_COMPRESSION_HEADER =
234 { (byte)0xFF, (byte)0XFE };
235 private static final char MIN_COMPRESS_CHAR = 1;
236 private static final char MAX_COMPRESS_CHAR = 0xFF;
237
238
239 static final int INVALID_AUTO_NUMBER = 0;
240
241 static final int INVALID_LENGTH = -1;
242
243
244
245 private final TableImpl _table;
246
247 private final boolean _variableLength;
248
249 private final boolean _autoNumber;
250
251 private final boolean _calculated;
252
253
254
255
256 private final boolean _complexValueForeignKey;
257
258 private final boolean _hidden;
259
260 private final boolean _securityIdentifier;
261
262 private final DataType _type;
263
264 private final short _columnLength;
265
266 private final short _columnNumber;
267
268
269 private final short _columnId;
270
271 private int _columnIndex;
272
273 private final int _displayIndex;
274
275 private final String _name;
276
277 private final int _fixedDataOffset;
278
279 private final int _varLenTableIndex;
280
281 private final AutoNumberGenerator _autoNumberGenerator;
282
283 private PropertyMap _props;
284
285 private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
286
287 private ColDefaultValueEvalContext _defValue;
288
289 private int _lengthInUnits = INVALID_LENGTH;
290
291
292
293
294 protected ColumnImpl(TableImpl table, String name, DataType type,
295 int colNumber, int fixedOffset, int varLenIndex) {
296 _table = table;
297 _name = name;
298 _type = type;
299
300 if(!_type.isVariableLength()) {
301 _columnLength = (short)type.getFixedSize();
302 } else {
303 _columnLength = (short)type.getMaxSize();
304 }
305 _variableLength = type.isVariableLength();
306 _autoNumber = false;
307 _calculated = false;
308 _complexValueForeignKey = false;
309 _hidden = false;
310 _securityIdentifier = false;
311 _autoNumberGenerator = null;
312 _columnNumber = (short)colNumber;
313 _columnId = (short)colNumber;
314 _columnIndex = colNumber;
315 _displayIndex = colNumber;
316 _fixedDataOffset = fixedOffset;
317 _varLenTableIndex = varLenIndex;
318 }
319
320
321
322
323
324 ColumnImpl(InitArgs args)
325 {
326 _table = args.table;
327 _name = args.name;
328 _displayIndex = args.displayIndex;
329 _type = args.type;
330
331 _columnNumber = args.buffer.getShort(
332 args.offset + getFormat().OFFSET_COLUMN_NUMBER);
333 _columnId = args.buffer.getShort(
334 args.offset + getFormat().OFFSET_COLUMN_ID);
335 _columnLength = args.buffer.getShort(
336 args.offset + getFormat().OFFSET_COLUMN_LENGTH);
337
338 _variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
339 _autoNumber = ((args.flags &
340 (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK)) != 0);
341 _calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
342 _complexValueForeignKey =
343 ((args.extFlags & COMPLEX_FK_EXT_FLAG_MASK) != 0);
344 _hidden = ((args.flags & HIDDEN_FLAG_MASK) != 0);
345 _securityIdentifier =
346 ((args.flags & SECURITY_IDENTIFIER_FLAG_MASK) != 0);
347
348 _autoNumberGenerator = createAutoNumberGenerator();
349
350 _varLenTableIndex = args.buffer.getShort(
351 args.offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
352 _fixedDataOffset = args.buffer.getShort(
353 args.offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
354 }
355
356
357
358
359
360
361
362
363
364 public static ColumnImpl create(TableImpl table, ByteBuffer buffer,
365 int offset, String name, int displayIndex)
366 throws IOException
367 {
368 InitArgs args = new InitArgs(table, buffer, offset, name, displayIndex);
369
370 boolean calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
371 byte colType = args.colType;
372 if(calculated) {
373
374 PropertyMap colProps = table.getPropertyMaps().get(name);
375 Byte resultType = (Byte)colProps.getValue(PropertyMap.RESULT_TYPE_PROP);
376 if(resultType != null) {
377 colType = resultType;
378 }
379 }
380
381 try {
382 args.type = DataType.fromByte(colType);
383 } catch(IOException e) {
384 LOG.log(Logger.Level.WARNING, withErrorContext("Unsupported column type " + colType,
385 table.getDatabase(), table.getName(), name));
386 boolean variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
387 args.type = (variableLength ? DataType.UNSUPPORTED_VARLEN :
388 DataType.UNSUPPORTED_FIXEDLEN);
389 return new UnsupportedColumnImpl(args);
390 }
391
392 if(calculated) {
393 return CalculatedColumnUtil.create(args);
394 }
395
396 switch(args.type) {
397 case TEXT:
398 return new TextColumnImpl(args);
399 case MEMO:
400 return new MemoColumnImpl(args);
401 case COMPLEX_TYPE:
402 return new ComplexColumnImpl(args);
403 default:
404
405 }
406
407 if(args.type.getHasScalePrecision()) {
408 return new NumericColumnImpl(args);
409 }
410 if(args.type.isLongValue()) {
411 return new LongValueColumnImpl(args);
412 }
413
414 return new ColumnImpl(args);
415 }
416
417
418
419
420 void setUsageMaps(UsageMap./../../com/healthmarketscience/jackcess/impl/UsageMap.html#UsageMap">UsageMap ownedPages, UsageMap freeSpacePages) {
421
422 }
423
424 void collectUsageMapPages(Collection<Integer> pages) {
425
426 }
427
428
429
430
431 void postTableLoadInit() throws IOException {
432
433 }
434
435 @Override
436 public TableImpl getTable() {
437 return _table;
438 }
439
440 @Override
441 public DatabaseImpl getDatabase() {
442 return getTable().getDatabase();
443 }
444
445
446
447
448 public JetFormat getFormat() {
449 return getDatabase().getFormat();
450 }
451
452
453
454
455 public PageChannel getPageChannel() {
456 return getDatabase().getPageChannel();
457 }
458
459 @Override
460 public String getName() {
461 return _name;
462 }
463
464 @Override
465 public boolean isVariableLength() {
466 return _variableLength;
467 }
468
469 @Override
470 public boolean isAutoNumber() {
471 return _autoNumber;
472 }
473
474
475
476
477 public short getColumnNumber() {
478 return _columnNumber;
479 }
480
481 @Override
482 public short getColumnId() {
483 return _columnId;
484 }
485
486 @Override
487 public boolean isHidden() {
488 return _hidden;
489 }
490
491 @Override
492 public boolean isSecurityIdentifier() {
493 return _securityIdentifier;
494 }
495
496 @Override
497 public int getColumnIndex() {
498 return _columnIndex;
499 }
500
501
502
503
504 public void setColumnIndex(int newColumnIndex) {
505 _columnIndex = newColumnIndex;
506 }
507
508
509
510
511 public int getDisplayIndex() {
512 return _displayIndex;
513 }
514
515 @Override
516 public DataType getType() {
517 return _type;
518 }
519
520 @Override
521 public int getSQLType() throws IOException {
522 return _type.getSQLType();
523 }
524
525 @Override
526 public boolean isCompressedUnicode() {
527 return false;
528 }
529
530 @Override
531 public byte getPrecision() {
532 return (byte)getType().getDefaultPrecision();
533 }
534
535 @Override
536 public byte getScale() {
537 return (byte)getType().getDefaultScale();
538 }
539
540
541
542
543 public SortOrder getTextSortOrder() {
544 return null;
545 }
546
547
548
549
550 public short getTextCodePage() {
551 return 0;
552 }
553
554 @Override
555 public short getLength() {
556 return _columnLength;
557 }
558
559 @Override
560 public final short getLengthInUnits() {
561 if(_lengthInUnits == INVALID_LENGTH) {
562 _lengthInUnits = calcLengthInUnits();
563 }
564 return (short)_lengthInUnits;
565 }
566
567 protected int calcLengthInUnits() {
568 return getType().toUnitSize(getLength(), getFormat());
569 }
570
571 @Override
572 public boolean isCalculated() {
573 return _calculated;
574 }
575
576
577
578
579
580
581
582 public boolean isComplexValueForeignKey() {
583 return _complexValueForeignKey;
584 }
585
586
587
588
589 public int getVarLenTableIndex() {
590 return _varLenTableIndex;
591 }
592
593
594
595
596 public int getFixedDataOffset() {
597 return _fixedDataOffset;
598 }
599
600 protected int getFixedDataSize() {
601 return _type.getFixedSize(_columnLength);
602 }
603
604 protected Charset getCharset() {
605 return getDatabase().getCharset();
606 }
607
608 @Override
609 public TimeZone getTimeZone() {
610 return getDatabase().getTimeZone();
611 }
612
613 @Override
614 public ZoneId getZoneId() {
615 return getDatabase().getZoneId();
616 }
617
618 @Override
619 public DateTimeFactory getDateTimeFactory() {
620 return getDatabase().getDateTimeFactory();
621 }
622
623 @Override
624 public boolean isAppendOnly() {
625 return (getVersionHistoryColumn() != null);
626 }
627
628 @Override
629 public ColumnImpl getVersionHistoryColumn() {
630 return null;
631 }
632
633
634
635
636
637 public int getOwnedPageCount() {
638 return 0;
639 }
640
641
642
643
644 public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
645 throw new UnsupportedOperationException();
646 }
647
648 @Override
649 public boolean isHyperlink() {
650 return false;
651 }
652
653 @Override
654 public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
655 return null;
656 }
657
658 void initColumnValidator() throws IOException {
659
660 if(getDatabase().isReadOnly()) {
661
662 return;
663 }
664
665
666 setColumnValidator(null);
667
668
669 reloadPropertiesValidators();
670 }
671
672 void reloadPropertiesValidators() throws IOException {
673
674 if(isAutoNumber()) {
675
676 return;
677 }
678
679 if(isCalculated()) {
680
681 CalcColEvalContext calcCol = null;
682
683 if(getDatabase().isEvaluateExpressions()) {
684
685
686 PropertyMap props = getProperties();
687 String calcExpr = (String)props.getValue(PropertyMap.EXPRESSION_PROP);
688 calcCol = new CalcColEvalContext(this).setExpr(calcExpr);
689 }
690
691 setCalcColEvalContext(calcCol);
692
693
694 return;
695 }
696
697
698
699 _validator = getColumnValidator();
700 _defValue = null;
701
702 PropertyMap props = getProperties();
703
704
705 boolean required = (Boolean)props.getValue(PropertyMap.REQUIRED_PROP,
706 Boolean.FALSE);
707 if(required) {
708 _validator = new RequiredColValidator(_validator);
709 }
710
711
712
713 boolean allowZeroLen =
714 !getType().isTextual() ||
715 (Boolean)props.getValue(PropertyMap.ALLOW_ZERO_LEN_PROP,
716 Boolean.TRUE);
717 if(!allowZeroLen) {
718 _validator = new NoZeroLenColValidator(_validator);
719 }
720
721
722 if(!getDatabase().isEvaluateExpressions()) {
723 return;
724 }
725
726 String exprStr = PropertyMaps.getTrimmedStringProperty(
727 props, PropertyMap.VALIDATION_RULE_PROP);
728
729 if(exprStr != null) {
730 String helpStr = PropertyMaps.getTrimmedStringProperty(
731 props, PropertyMap.VALIDATION_TEXT_PROP);
732
733 _validator = new ColValidatorEvalContext(this)
734 .setExpr(exprStr, helpStr)
735 .toColumnValidator(_validator);
736 }
737
738 String defValueStr = PropertyMaps.getTrimmedStringProperty(
739 props, PropertyMap.DEFAULT_VALUE_PROP);
740 if(defValueStr != null) {
741 _defValue = new ColDefaultValueEvalContext(this)
742 .setExpr(defValueStr);
743 }
744 }
745
746 void propertiesUpdated() throws IOException {
747 reloadPropertiesValidators();
748 }
749
750 @Override
751 public ColumnValidator getColumnValidator() {
752
753 return ((_validator instanceof InternalColumnValidator) ?
754 ((InternalColumnValidator)_validator).getExternal() : _validator);
755 }
756
757 @Override
758 public void setColumnValidator(ColumnValidator newValidator) {
759
760 if(isAutoNumber()) {
761
762
763 if(newValidator != null) {
764 throw new IllegalArgumentException(withErrorContext(
765 "Cannot set ColumnValidator for autonumber columns"));
766 }
767
768 return;
769 }
770
771 if(newValidator == null) {
772 newValidator = getDatabase().getColumnValidatorFactory()
773 .createValidator(this);
774 if(newValidator == null) {
775 newValidator = SimpleColumnValidator.INSTANCE;
776 }
777 }
778
779
780 if(_validator instanceof InternalColumnValidator) {
781 ((InternalColumnValidator)_validator).setExternal(newValidator);
782 } else {
783 _validator = newValidator;
784 }
785 }
786
787 byte getOriginalDataType() {
788 return _type.getValue();
789 }
790
791 private AutoNumberGenerator createAutoNumberGenerator() {
792 if(!_autoNumber || (_type == null)) {
793 return null;
794 }
795
796 switch(_type) {
797 case LONG:
798 return new LongAutoNumberGenerator();
799 case GUID:
800 return new GuidAutoNumberGenerator();
801 case COMPLEX_TYPE:
802 return new ComplexTypeAutoNumberGenerator();
803 default:
804 LOG.log(Logger.Level.WARNING, withErrorContext("Unknown auto number column type " + _type));
805 return new UnsupportedAutoNumberGenerator(_type);
806 }
807 }
808
809
810
811
812
813
814 public AutoNumberGenerator getAutoNumberGenerator() {
815 return _autoNumberGenerator;
816 }
817
818 @Override
819 public PropertyMap getProperties() throws IOException {
820 if(_props == null) {
821 _props = getTable().getPropertyMaps().get(getName());
822 }
823 return _props;
824 }
825
826 @Override
827 public Object setRowValue(Object[] rowArray, Object value) {
828 rowArray[_columnIndex] = value;
829 return value;
830 }
831
832 @Override
833 public Object setRowValue(Map<String,Object> rowMap, Object value) {
834 rowMap.put(_name, value);
835 return value;
836 }
837
838 @Override
839 public Object getRowValue(Object[] rowArray) {
840 return rowArray[_columnIndex];
841 }
842
843 @Override
844 public Object getRowValue(Map<String,?> rowMap) {
845 return rowMap.get(_name);
846 }
847
848 public boolean storeInNullMask() {
849 return (getType() == DataType.BOOLEAN);
850 }
851
852 public boolean writeToNullMask(Object value) {
853 return toBooleanValue(value);
854 }
855
856 public Object readFromNullMask(boolean isNull) {
857 return Boolean.valueOf(!isNull);
858 }
859
860
861
862
863
864
865
866 public Object read(byte[] data) throws IOException {
867 return read(data, PageChannel.DEFAULT_BYTE_ORDER);
868 }
869
870
871
872
873
874
875
876
877 public Object read(byte[] data, ByteOrder order) throws IOException {
878 ByteBuffer buffer = ByteBuffer.wrap(data).order(order);
879
880 switch(getType()) {
881 case BOOLEAN:
882 throw new IOException(withErrorContext("Tried to read a boolean from data instead of null mask."));
883 case BYTE:
884 return Byte.valueOf(buffer.get());
885 case INT:
886 return Short.valueOf(buffer.getShort());
887 case LONG:
888 return Integer.valueOf(buffer.getInt());
889 case DOUBLE:
890 return Double.valueOf(buffer.getDouble());
891 case FLOAT:
892 return Float.valueOf(buffer.getFloat());
893 case SHORT_DATE_TIME:
894 return readDateValue(buffer);
895 case BINARY:
896 return data;
897 case TEXT:
898 return decodeTextValue(data);
899 case MONEY:
900 return readCurrencyValue(buffer);
901 case NUMERIC:
902 return readNumericValue(buffer);
903 case GUID:
904 return readGUIDValue(buffer, order);
905 case EXT_DATE_TIME:
906 return readExtendedDateValue(buffer);
907 case BIG_BINARY:
908
909 return data;
910 case COMPLEX_TYPE:
911 return new ComplexValueForeignKeyImpl(this, buffer.getInt());
912 case BIG_INT:
913 return Long.valueOf(buffer.getLong());
914 default:
915 throw new IOException(withErrorContext("Unrecognized data type: " + _type));
916 }
917 }
918
919
920
921
922
923
924
925
926 private BigDecimal readCurrencyValue(ByteBuffer buffer)
927 throws IOException
928 {
929 if(buffer.remaining() != 8) {
930 throw new IOException(withErrorContext("Invalid money value"));
931 }
932
933 return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
934 }
935
936
937
938
939 private void writeCurrencyValue(ByteBuffer buffer, Object value)
940 throws IOException
941 {
942 Object inValue = value;
943 try {
944 BigDecimal decVal = toBigDecimal(value);
945 inValue = decVal;
946
947
948
949 decVal = decVal.setScale(4);
950
951
952
953 buffer.putLong(decVal.movePointRight(4).longValueExact());
954 } catch(ArithmeticException e) {
955 throw new IOException(
956 withErrorContext("Currency value '" + inValue + "' out of range"), e);
957 }
958 }
959
960
961
962
963 private BigDecimal readNumericValue(ByteBuffer buffer)
964 {
965 boolean negate = (buffer.get() != 0);
966
967 byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
968
969 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
970 fixNumericByteOrder(tmpArr);
971 }
972
973 return toBigDecimal(tmpArr, negate, getScale());
974 }
975
976 static BigDecimal toBigDecimal(byte[] bytes, boolean negate, int scale)
977 {
978 if((bytes[0] & 0x80) != 0) {
979
980
981
982 bytes = ByteUtil.copyOf(bytes, 0, bytes.length + 1, 1);
983 }
984 BigInteger intVal = new BigInteger(bytes);
985 if(negate) {
986 intVal = intVal.negate();
987 }
988 return new BigDecimal(intVal, scale);
989 }
990
991
992
993
994 private void writeNumericValue(ByteBuffer buffer, Object value)
995 throws IOException
996 {
997 Object inValue = value;
998 try {
999 BigDecimal decVal = toBigDecimal(value);
1000 inValue = decVal;
1001
1002 int signum = decVal.signum();
1003 if(signum < 0) {
1004 decVal = decVal.negate();
1005 }
1006
1007
1008 buffer.put((signum < 0) ? NUMERIC_NEGATIVE_BYTE : 0);
1009
1010
1011
1012 decVal = decVal.setScale(getScale());
1013
1014
1015 if(decVal.precision() > getPrecision()) {
1016 throw new InvalidValueException(withErrorContext(
1017 "Numeric value is too big for specified precision "
1018 + getPrecision() + ": " + decVal));
1019 }
1020
1021
1022 byte[] intValBytes = toUnscaledByteArray(
1023 decVal, getType().getFixedSize() - 1);
1024 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
1025 fixNumericByteOrder(intValBytes);
1026 }
1027 buffer.put(intValBytes);
1028 } catch(ArithmeticException e) {
1029 throw new IOException(
1030 withErrorContext("Numeric value '" + inValue + "' out of range"), e);
1031 }
1032 }
1033
1034 byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen)
1035 throws IOException
1036 {
1037
1038 byte[] intValBytes = decVal.unscaledValue().toByteArray();
1039 if(intValBytes.length > maxByteLen) {
1040 if((intValBytes[0] == 0) && ((intValBytes.length - 1) == maxByteLen)) {
1041
1042
1043
1044 intValBytes = ByteUtil.copyOf(intValBytes, 1, maxByteLen);
1045 } else {
1046 throw new InvalidValueException(withErrorContext(
1047 "Too many bytes for valid BigInteger?"));
1048 }
1049 } else if(intValBytes.length < maxByteLen) {
1050 intValBytes = ByteUtil.copyOf(intValBytes, 0, maxByteLen,
1051 (maxByteLen - intValBytes.length));
1052 }
1053 return intValBytes;
1054 }
1055
1056
1057
1058
1059 private Object readDateValue(ByteBuffer buffer) {
1060 long dateBits = buffer.getLong();
1061 return getDateTimeFactory().fromDateBits(this, dateBits);
1062 }
1063
1064
1065
1066
1067 private static Object readExtendedDateValue(ByteBuffer buffer) {
1068
1069 long numDays = readExtDateLong(buffer, 19);
1070 buffer.get();
1071 long seconds = readExtDateLong(buffer, 12);
1072
1073 long nanos = readExtDateLong(buffer, 7) * 100L;
1074 ByteUtil.forward(buffer, EXT_LDT_TRAILER.length);
1075
1076 return BASE_EXT_LDT
1077 .plusDays(numDays)
1078 .plusSeconds(seconds)
1079 .plusNanos(nanos);
1080 }
1081
1082
1083
1084
1085 private static long readExtDateLong(ByteBuffer buffer, int numChars) {
1086 long val = 0L;
1087 for(int i = 0; i < numChars; ++i) {
1088 char digit = (char)buffer.get();
1089 long inc = digit - '0';
1090 val = (val * 10L) + inc;
1091 }
1092 return val;
1093 }
1094
1095
1096
1097
1098
1099 public long fromDateDouble(double value) {
1100 return fromDateDouble(value, getTimeZone());
1101 }
1102
1103 private static long fromDateDouble(double value, TimeZone tz) {
1104 long localTime = fromLocalDateDouble(value);
1105 return localTime - getFromLocalTimeZoneOffset(localTime, tz);
1106 }
1107
1108 static long fromLocalDateDouble(double value) {
1109 long datePart = ((long)value) * MILLISECONDS_PER_DAY;
1110
1111
1112
1113
1114
1115
1116 long timePart = Math.round((Math.abs(value) % 1.0d) *
1117 MILLISECONDS_PER_DAY);
1118
1119 long time = datePart + timePart;
1120 return time - MILLIS_BETWEEN_EPOCH_AND_1900;
1121 }
1122
1123 public static LocalDateTime ldtFromLocalDateDouble(double value) {
1124 Duration dateTimeOffset = durationFromLocalDateDouble(value);
1125 return BASE_LDT.plus(dateTimeOffset);
1126 }
1127
1128 private static Duration durationFromLocalDateDouble(double value) {
1129 long dateSeconds = ((long)value) * SECONDS_PER_DAY;
1130
1131
1132
1133
1134
1135
1136
1137 double secondsDouble = (Math.abs(value) % 1.0d) * SECONDS_PER_DAY;
1138 long timeSeconds = (long)secondsDouble;
1139 long timeNanos = Math.round((secondsDouble % 1.0d) * NANOS_PER_SECOND);
1140
1141
1142
1143
1144
1145
1146
1147
1148 Duration offset = null;
1149 for(long unit : TIME_ROUNDING_NANOS) {
1150
1151
1152 offset = Duration.ofSeconds(dateSeconds + timeSeconds,
1153 (Math.round((double)timeNanos / unit) * unit));
1154 if(Double.doubleToLongBits(toLocalDateDouble(offset)) ==
1155 Double.doubleToLongBits(value)) {
1156 break;
1157 }
1158 }
1159 return offset;
1160 }
1161
1162
1163
1164
1165 private void writeDateValue(ByteBuffer buffer, Object value)
1166 throws InvalidValueException
1167 {
1168 if(value == null) {
1169 buffer.putDouble(0d);
1170 } else if(value instanceof DateExt) {
1171
1172
1173 buffer.putLong(((DateExt)value).getDateBits());
1174 } else {
1175 buffer.putDouble(toDateDouble(value));
1176 }
1177 }
1178
1179
1180
1181
1182 private void writeExtendedDateValue(ByteBuffer buffer, Object value)
1183 {
1184 LocalDateTime ldt = BASE_EXT_LDT;
1185 if(value != null) {
1186 ldt = toLocalDateTime(value, this);
1187 }
1188
1189 LocalDate ld = ldt.toLocalDate();
1190 LocalTime lt = ldt.toLocalTime();
1191
1192 long numDays = BASE_EXT_LD.until(ld, ChronoUnit.DAYS);
1193 long numSeconds = BASE_EXT_LT.until(lt, ChronoUnit.SECONDS);
1194 long nanos = lt.getNano();
1195
1196
1197 writeExtDateLong(buffer, numDays, 19);
1198 buffer.put((byte)':');
1199 writeExtDateLong(buffer, numSeconds, 12);
1200
1201 writeExtDateLong(buffer, (nanos / 100L), 7);
1202
1203 buffer.put(EXT_LDT_TRAILER);
1204 }
1205
1206
1207
1208
1209
1210 private static void writeExtDateLong(
1211 ByteBuffer buffer, long val, int numChars) {
1212
1213 int end = buffer.position();
1214 int start = end + numChars - 1;
1215 for(int i = start; i >= end; --i) {
1216 char digit = (char)('0' + (char)(val % 10L));
1217 buffer.put(i, (byte)digit);
1218 val /= 10L;
1219 }
1220 ByteUtil.forward(buffer, numChars);
1221 }
1222
1223
1224
1225
1226
1227
1228 public double toDateDouble(Object value)
1229 throws InvalidValueException
1230 {
1231 try {
1232 return toDateDouble(value, this);
1233 } catch(IllegalArgumentException iae) {
1234 throw new InvalidValueException(withErrorContext(iae.getMessage()), iae);
1235 }
1236 }
1237
1238
1239
1240
1241
1242
1243 private static double toDateDouble(Object value, DateTimeContext dtc) {
1244 return dtc.getDateTimeFactory().toDateDouble(value, dtc);
1245 }
1246
1247 static LocalDateTime toLocalDateTime(
1248 Object value, DateTimeContext dtc) {
1249 if(value instanceof TemporalAccessor) {
1250 return temporalToLocalDateTime((TemporalAccessor)value, dtc);
1251 }
1252 Instant inst = Instant.ofEpochMilli(toDateLong(value));
1253 return LocalDateTime.ofInstant(inst, dtc.getZoneId());
1254 }
1255
1256 private static LocalDateTime temporalToLocalDateTime(
1257 TemporalAccessor value, DateTimeContext dtc) {
1258
1259
1260 if(value instanceof LocalDateTime) {
1261 return (LocalDateTime)value;
1262 }
1263 if(value instanceof ZonedDateTime) {
1264
1265 return ((ZonedDateTime)value).withZoneSameInstant(
1266 dtc.getZoneId()).toLocalDateTime();
1267 }
1268 if(value instanceof Instant) {
1269 return LocalDateTime.ofInstant((Instant)value, dtc.getZoneId());
1270 }
1271 if(value instanceof LocalDate) {
1272 return ((LocalDate)value).atTime(BASE_LT);
1273 }
1274 if(value instanceof LocalTime) {
1275 return ((LocalTime)value).atDate(BASE_LD);
1276 }
1277
1278
1279 try {
1280
1281 LocalDate ld = value.query(TemporalQueries.localDate());
1282 if(ld == null) {
1283 ld = BASE_LD;
1284 }
1285 LocalTime lt = value.query(TemporalQueries.localTime());
1286 if(lt == null) {
1287 lt = BASE_LT;
1288 }
1289 ZoneId zone = value.query(TemporalQueries.zone());
1290 if(zone != null) {
1291
1292
1293 ZoneId zoneId = dtc.getZoneId();
1294 if(!zoneId.equals(zone)) {
1295 return ZonedDateTime.of(ld, lt, zone).withZoneSameInstant(zoneId)
1296 .toLocalDateTime();
1297 }
1298 }
1299
1300 return LocalDateTime.of(ld, lt);
1301
1302 } catch(DateTimeException | ArithmeticException e) {
1303 throw new IllegalArgumentException(
1304 "Unsupported temporal type " + value.getClass(), e);
1305 }
1306 }
1307
1308 private static Instant toInstant(TemporalAccessor value, DateTimeContext dtc) {
1309 if(value instanceof ZonedDateTime) {
1310 return ((ZonedDateTime)value).toInstant();
1311 }
1312 if(value instanceof Instant) {
1313 return (Instant)value;
1314 }
1315 return temporalToLocalDateTime(value, dtc).atZone(dtc.getZoneId())
1316 .toInstant();
1317 }
1318
1319 static double toLocalDateDouble(long time) {
1320 time += MILLIS_BETWEEN_EPOCH_AND_1900;
1321
1322 if(time < 0L) {
1323
1324 long timePart = -time % MILLISECONDS_PER_DAY;
1325 if(timePart > 0) {
1326 time -= (2 * (MILLISECONDS_PER_DAY - timePart));
1327 }
1328 }
1329
1330 return time / (double)MILLISECONDS_PER_DAY;
1331 }
1332
1333 public static double toDateDouble(LocalDateTime ldt) {
1334 Duration dateTimeOffset = Duration.between(BASE_LDT, ldt);
1335 return toLocalDateDouble(dateTimeOffset);
1336 }
1337
1338 private static double toLocalDateDouble(Duration time) {
1339 long dateTimeSeconds = time.getSeconds();
1340 long timeSeconds = dateTimeSeconds % SECONDS_PER_DAY;
1341 if(timeSeconds < 0) {
1342 timeSeconds += SECONDS_PER_DAY;
1343 }
1344 long dateSeconds = dateTimeSeconds - timeSeconds;
1345 long timeNanos = time.getNano();
1346
1347
1348
1349
1350 double timeDouble = (((double)timeNanos / NANOS_PER_SECOND +
1351 timeSeconds) / SECONDS_PER_DAY);
1352
1353 double dateDouble = ((double)dateSeconds / SECONDS_PER_DAY);
1354
1355 if(dateSeconds < 0) {
1356 timeDouble = -timeDouble;
1357 }
1358
1359 return dateDouble + timeDouble;
1360 }
1361
1362
1363
1364
1365 private static long toDateLong(Object value) {
1366 return ((value instanceof Date) ?
1367 ((Date)value).getTime() :
1368 ((value instanceof Calendar) ?
1369 ((Calendar)value).getTimeInMillis() :
1370 ((Number)value).longValue()));
1371 }
1372
1373
1374
1375
1376
1377 private static long getToLocalTimeZoneOffset(long time, TimeZone tz) {
1378 return tz.getOffset(time);
1379 }
1380
1381
1382
1383
1384
1385 private static long getFromLocalTimeZoneOffset(long time, TimeZone tz) {
1386
1387
1388
1389 return tz.getOffset(time - tz.getRawOffset());
1390 }
1391
1392
1393
1394
1395 static String readGUIDValue(ByteBuffer buffer, ByteOrder order)
1396 {
1397 if(order != ByteOrder.BIG_ENDIAN) {
1398 byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
1399
1400
1401
1402 ByteUtil.swap4Bytes(tmpArr, 0);
1403 ByteUtil.swap2Bytes(tmpArr, 4);
1404 ByteUtil.swap2Bytes(tmpArr, 6);
1405 buffer = ByteBuffer.wrap(tmpArr);
1406 }
1407
1408 StringBuilder sb = new StringBuilder(22);
1409 sb.append("{");
1410 sb.append(ByteUtil.toHexString(buffer, 0, 4,
1411 false));
1412 sb.append("-");
1413 sb.append(ByteUtil.toHexString(buffer, 4, 2,
1414 false));
1415 sb.append("-");
1416 sb.append(ByteUtil.toHexString(buffer, 6, 2,
1417 false));
1418 sb.append("-");
1419 sb.append(ByteUtil.toHexString(buffer, 8, 2,
1420 false));
1421 sb.append("-");
1422 sb.append(ByteUtil.toHexString(buffer, 10, 6,
1423 false));
1424 sb.append("}");
1425 return (sb.toString());
1426 }
1427
1428
1429
1430
1431
1432
1433
1434
1435 public static String toGUIDString(UUID uuid) {
1436 return "{" + StringUtil.toUpperCase(uuid.toString()) + "}";
1437 }
1438
1439
1440
1441
1442 private void writeGUIDValue(ByteBuffer buffer, Object value)
1443 throws IOException
1444 {
1445 if(!writeGUIDValue(buffer, toCharSequence(value))) {
1446 throw new InvalidValueException(
1447 withErrorContext("Invalid GUID: " + value));
1448 }
1449 }
1450
1451
1452
1453
1454
1455
1456 static boolean writeGUIDValue(ByteBuffer buffer, CharSequence value)
1457 {
1458 Matcher m = GUID_PATTERN.matcher(value);
1459 if(!m.matches()) {
1460 return false;
1461 }
1462
1463 ByteBuffer origBuffer = null;
1464 byte[] tmpBuf = null;
1465 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
1466
1467 origBuffer = buffer;
1468 tmpBuf = new byte[16];
1469 buffer = ByteBuffer.wrap(tmpBuf);
1470 }
1471
1472 try {
1473 ByteUtil.writeHexString(buffer, m.group(1));
1474 ByteUtil.writeHexString(buffer, m.group(2));
1475 ByteUtil.writeHexString(buffer, m.group(3));
1476 ByteUtil.writeHexString(buffer, m.group(4));
1477 ByteUtil.writeHexString(buffer, m.group(5));
1478 } catch(IOException e) {
1479
1480 throw new IllegalStateException(e);
1481 }
1482
1483 if(tmpBuf != null) {
1484
1485
1486 ByteUtil.swap4Bytes(tmpBuf, 0);
1487 ByteUtil.swap2Bytes(tmpBuf, 4);
1488 ByteUtil.swap2Bytes(tmpBuf, 6);
1489 origBuffer.put(tmpBuf);
1490 }
1491 return true;
1492 }
1493
1494
1495
1496
1497 static boolean isGUIDValue(Object value) throws IOException {
1498 return GUID_PATTERN.matcher(toCharSequence(value)).matches();
1499 }
1500
1501
1502
1503
1504 public Object generateDefaultValue() throws IOException {
1505 return ((_defValue != null) ? _defValue.eval() : null);
1506 }
1507
1508
1509
1510
1511
1512 public Object validate(Object obj) throws IOException {
1513 return _validator.validate(this, obj);
1514 }
1515
1516
1517
1518
1519 protected CalcColEvalContext getCalculationContext() {
1520 throw new UnsupportedOperationException();
1521 }
1522
1523 protected void setCalcColEvalContext(CalcColEvalContext calcCol) {
1524 throw new UnsupportedOperationException();
1525 }
1526
1527
1528
1529
1530
1531
1532
1533
1534 public ByteBuffer write(Object obj, int remainingRowLength)
1535 throws IOException
1536 {
1537 return write(obj, remainingRowLength, PageChannel.DEFAULT_BYTE_ORDER);
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547 public ByteBuffer write(Object obj, int remainingRowLength, ByteOrder order)
1548 throws IOException
1549 {
1550 if(isRawData(obj)) {
1551
1552 return ByteBuffer.wrap(((RawData)obj).getBytes());
1553 }
1554
1555 return writeRealData(obj, remainingRowLength, order);
1556 }
1557
1558 protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
1559 ByteOrder order)
1560 throws IOException
1561 {
1562 if(!isVariableLength() || !getType().isVariableLength()) {
1563 return writeFixedLengthField(obj, order);
1564 }
1565
1566
1567 switch(getType()) {
1568 case NUMERIC:
1569
1570 ByteBuffer buffer = PageChannel.createBuffer(
1571 getType().getFixedSize(), order);
1572 writeNumericValue(buffer, obj);
1573 buffer.flip();
1574 return buffer;
1575
1576 case TEXT:
1577 return encodeTextValue(
1578 obj, 0, getLengthInUnits(), false).order(order);
1579
1580 case BINARY:
1581 case UNSUPPORTED_VARLEN:
1582
1583 break;
1584 default:
1585 throw new RuntimeException(withErrorContext(
1586 "unexpected inline var length type: " + getType()));
1587 }
1588
1589 return ByteBuffer.wrap(toByteArray(obj)).order(order);
1590 }
1591
1592
1593
1594
1595
1596
1597
1598
1599 protected ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
1600 throws IOException
1601 {
1602 int size = getFixedDataSize();
1603
1604 ByteBuffer buffer = writeFixedLengthField(
1605 obj, PageChannel.createBuffer(size, order));
1606 buffer.flip();
1607 return buffer;
1608 }
1609
1610 protected ByteBuffer writeFixedLengthField(Object obj, ByteBuffer buffer)
1611 throws IOException
1612 {
1613
1614
1615 obj = booleanToInteger(obj);
1616
1617 switch(getType()) {
1618 case BOOLEAN:
1619
1620 break;
1621 case BYTE:
1622 buffer.put(toNumber(obj).byteValue());
1623 break;
1624 case INT:
1625 buffer.putShort(toNumber(obj).shortValue());
1626 break;
1627 case LONG:
1628 buffer.putInt(toNumber(obj).intValue());
1629 break;
1630 case MONEY:
1631 writeCurrencyValue(buffer, obj);
1632 break;
1633 case FLOAT:
1634 buffer.putFloat(toNumber(obj).floatValue());
1635 break;
1636 case DOUBLE:
1637 buffer.putDouble(toNumber(obj).doubleValue());
1638 break;
1639 case SHORT_DATE_TIME:
1640 writeDateValue(buffer, obj);
1641 break;
1642 case TEXT:
1643
1644
1645 int numChars = getLengthInUnits();
1646
1647 buffer.put(encodeTextValue(obj, numChars, numChars, true));
1648 break;
1649 case GUID:
1650 writeGUIDValue(buffer, obj);
1651 break;
1652 case NUMERIC:
1653
1654
1655 writeNumericValue(buffer, obj);
1656 break;
1657 case BINARY:
1658 case BIG_BINARY:
1659 case COMPLEX_TYPE:
1660 buffer.putInt(toNumber(obj).intValue());
1661 break;
1662 case BIG_INT:
1663 buffer.putLong(toNumber(obj).longValue());
1664 break;
1665 case EXT_DATE_TIME:
1666 writeExtendedDateValue(buffer, obj);
1667 break;
1668 case UNSUPPORTED_FIXEDLEN:
1669 byte[] bytes = toByteArray(obj);
1670 if(bytes.length != getLength()) {
1671 throw new InvalidValueException(withErrorContext(
1672 "Invalid fixed size binary data, size "
1673 + getLength() + ", got " + bytes.length));
1674 }
1675 buffer.put(bytes);
1676 break;
1677 default:
1678 throw new IOException(withErrorContext(
1679 "Unsupported data type: " + getType()));
1680 }
1681 return buffer;
1682 }
1683
1684
1685
1686
1687 String decodeTextValue(byte[] data)
1688 {
1689
1690
1691 boolean isCompressed = ((data.length > 1) &&
1692 (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
1693 (data[1] == TEXT_COMPRESSION_HEADER[1]));
1694
1695 if(isCompressed) {
1696
1697
1698
1699
1700 StringBuilder textBuf = new StringBuilder(data.length);
1701
1702 int dataStart = TEXT_COMPRESSION_HEADER.length;
1703 int dataEnd = dataStart;
1704 boolean inCompressedMode = true;
1705 while(dataEnd < data.length) {
1706 if(data[dataEnd] == (byte)0x00) {
1707
1708
1709 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
1710 textBuf);
1711 inCompressedMode = !inCompressedMode;
1712 ++dataEnd;
1713 dataStart = dataEnd;
1714
1715 } else {
1716 ++dataEnd;
1717 }
1718 }
1719
1720 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode, textBuf);
1721
1722 return textBuf.toString();
1723
1724 }
1725
1726 return decodeUncompressedText(data, getCharset());
1727 }
1728
1729
1730
1731
1732
1733 private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
1734 boolean inCompressedMode,
1735 StringBuilder textBuf)
1736 {
1737 if(dataEnd <= dataStart) {
1738
1739 return;
1740 }
1741 int dataLength = dataEnd - dataStart;
1742
1743 if(inCompressedMode) {
1744 byte[] tmpData = new byte[dataLength * 2];
1745 int tmpIdx = 0;
1746 for(int i = dataStart; i < dataEnd; ++i) {
1747 tmpData[tmpIdx] = data[i];
1748 tmpIdx += 2;
1749 }
1750 data = tmpData;
1751 dataStart = 0;
1752 dataLength = data.length;
1753 }
1754
1755 textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
1756 getCharset()));
1757 }
1758
1759
1760
1761
1762
1763 private static CharBuffer decodeUncompressedText(
1764 byte[] textBytes, int startPos, int length, Charset charset)
1765 {
1766 return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
1767 }
1768
1769
1770
1771
1772 ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
1773 boolean forceUncompressed)
1774 throws IOException
1775 {
1776 CharSequence text = toCharSequence(obj);
1777 if((text.length() > maxChars) || (text.length() < minChars)) {
1778 throw new InvalidValueException(withErrorContext(
1779 "Text is wrong length for " + getType() +
1780 " column, max " + maxChars
1781 + ", min " + minChars + ", got " + text.length()));
1782 }
1783
1784
1785 if(!forceUncompressed && isCompressedUnicode() &&
1786 (text.length() <= getFormat().MAX_COMPRESSED_UNICODE_SIZE) &&
1787 isUnicodeCompressible(text)) {
1788
1789 byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
1790 text.length()];
1791 encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
1792 encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
1793 for(int i = 0; i < text.length(); ++i) {
1794 encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
1795 (byte)text.charAt(i);
1796 }
1797 return ByteBuffer.wrap(encodedChars);
1798 }
1799
1800 return encodeUncompressedText(text, getCharset());
1801 }
1802
1803
1804
1805
1806
1807 private static boolean isUnicodeCompressible(CharSequence text) {
1808
1809
1810 if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
1811 return false;
1812 }
1813
1814 for(int i = 0; i < text.length(); ++i) {
1815 char c = text.charAt(i);
1816 if((c < MIN_COMPRESS_CHAR) || (c > MAX_COMPRESS_CHAR)) {
1817 return false;
1818 }
1819 }
1820 return true;
1821 }
1822
1823
1824
1825
1826 private static byte getColumnBitFlags(ColumnBuilder col) {
1827 byte flags = UPDATABLE_FLAG_MASK;
1828 if(!col.isVariableLength()) {
1829 flags |= FIXED_LEN_FLAG_MASK;
1830 }
1831 if(col.isAutoNumber()) {
1832 byte autoNumFlags = 0;
1833 switch(col.getType()) {
1834 case LONG:
1835 case COMPLEX_TYPE:
1836 autoNumFlags = AUTO_NUMBER_FLAG_MASK;
1837 break;
1838 case GUID:
1839 autoNumFlags = AUTO_NUMBER_GUID_FLAG_MASK;
1840 break;
1841 default:
1842
1843 }
1844 flags |= autoNumFlags;
1845 }
1846 if(col.isHyperlink()) {
1847 flags |= HYPERLINK_FLAG_MASK;
1848 }
1849 return flags;
1850 }
1851
1852 @Override
1853 public String toString() {
1854 ToStringBuilder sb = ToStringBuilder.builder(this)
1855 .append("name", "(" + _table.getName() + ") " + _name);
1856 byte typeValue = getOriginalDataType();
1857 sb.append("type", "0x" + Integer.toHexString(typeValue) +
1858 " (" + _type + ")")
1859 .append("number", _columnNumber)
1860 .append("id", _columnId)
1861 .append("length", _columnLength)
1862 .append("variableLength", _variableLength);
1863 if(_hidden) {
1864 sb.append("hidden", _hidden);
1865 }
1866 if(_securityIdentifier) {
1867 sb.append("securityIdentifier", _securityIdentifier);
1868 }
1869 if(_calculated) {
1870 sb.append("calculated", _calculated)
1871 .appendIfNotNull("expression", getCalculationContext());
1872 }
1873 if(_type.isTextual()) {
1874 sb.append("compressedUnicode", isCompressedUnicode())
1875 .append("textSortOrder", getTextSortOrder());
1876 if(getTextCodePage() > 0) {
1877 sb.append("textCodePage", getTextCodePage());
1878 }
1879 if(isAppendOnly()) {
1880 sb.append("appendOnly", isAppendOnly());
1881 }
1882 if(isHyperlink()) {
1883 sb.append("hyperlink", isHyperlink());
1884 }
1885 }
1886 if(_type.getHasScalePrecision()) {
1887 sb.append("precision", getPrecision())
1888 .append("scale", getScale());
1889 }
1890 if(_autoNumber) {
1891 sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
1892 }
1893 sb.appendIfNotNull("complexInfo", getComplexInfo())
1894 .appendIfNotNull("validator",
1895 ((_validator != SimpleColumnValidator.INSTANCE) ?
1896 _validator : null))
1897 .appendIfNotNull("defaultValue", _defValue);
1898 return sb.toString();
1899 }
1900
1901
1902
1903
1904
1905
1906
1907 public static String decodeUncompressedText(byte[] textBytes,
1908 Charset charset)
1909 {
1910 return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
1911 .toString();
1912 }
1913
1914
1915
1916
1917
1918
1919
1920 public static ByteBuffer encodeUncompressedText(CharSequence text,
1921 Charset charset)
1922 {
1923 CharBuffer cb = ((text instanceof CharBuffer) ?
1924 (CharBuffer)text : CharBuffer.wrap(text));
1925 return charset.encode(cb);
1926 }
1927
1928
1929
1930
1931
1932
1933 public static short countVariableLength(List<ColumnBuilder> columns) {
1934 short rtn = 0;
1935 for (ColumnBuilder col : columns) {
1936 if (col.isVariableLength()) {
1937 rtn++;
1938 }
1939 }
1940 return rtn;
1941 }
1942
1943
1944
1945
1946
1947
1948 BigDecimal toBigDecimal(Object value)
1949 {
1950 return toBigDecimal(value, getDatabase());
1951 }
1952
1953
1954
1955
1956
1957
1958 static BigDecimal toBigDecimal(Object value, DatabaseImpl db)
1959 {
1960 if(value == null) {
1961 return BigDecimal.ZERO;
1962 } else if(value instanceof BigDecimal) {
1963 return (BigDecimal)value;
1964 } else if(value instanceof BigInteger) {
1965 return new BigDecimal((BigInteger)value);
1966 } else if(value instanceof Number) {
1967 return new BigDecimal(((Number)value).doubleValue());
1968 } else if(value instanceof Boolean) {
1969
1970 return ((Boolean)value) ? BigDecimal.valueOf(-1) : BigDecimal.ZERO;
1971 } else if(value instanceof Date) {
1972 return new BigDecimal(toDateDouble(value, db));
1973 } else if(value instanceof LocalDateTime) {
1974 return new BigDecimal(toDateDouble((LocalDateTime)value));
1975 }
1976 return new BigDecimal(value.toString());
1977 }
1978
1979
1980
1981
1982
1983
1984 private Number toNumber(Object value)
1985 {
1986 return toNumber(value, getDatabase());
1987 }
1988
1989
1990
1991
1992
1993
1994 private static Number toNumber(Object value, DatabaseImpl db)
1995 {
1996 if(value == null) {
1997 return BigDecimal.ZERO;
1998 } else if(value instanceof Number) {
1999 return (Number)value;
2000 } else if(value instanceof Boolean) {
2001
2002 return ((Boolean)value) ? -1 : 0;
2003 } else if(value instanceof Date) {
2004 return toDateDouble(value, db);
2005 } else if(value instanceof LocalDateTime) {
2006 return toDateDouble((LocalDateTime)value);
2007 }
2008 return Double.valueOf(value.toString());
2009 }
2010
2011
2012
2013
2014
2015 public static CharSequence toCharSequence(Object value)
2016 throws IOException
2017 {
2018 if(value == null) {
2019 return null;
2020 } else if(value instanceof CharSequence) {
2021 return (CharSequence)value;
2022 } else if(SqlHelper.INSTANCE.isClob(value)) {
2023 return SqlHelper.INSTANCE.getClobString(value);
2024 } else if(value instanceof Reader) {
2025 char[] buf = new char[8 * 1024];
2026 StringBuilder sout = new StringBuilder();
2027 Reader in = (Reader)value;
2028 int read = 0;
2029 while((read = in.read(buf)) != -1) {
2030 sout.append(buf, 0, read);
2031 }
2032 return sout;
2033 }
2034
2035 return value.toString();
2036 }
2037
2038
2039
2040
2041
2042 public static byte[] toByteArray(Object value)
2043 throws IOException
2044 {
2045 if(value == null) {
2046 return null;
2047 } else if(value instanceof byte[]) {
2048 return (byte[])value;
2049 } else if(value instanceof InMemoryBlob) {
2050 return ((InMemoryBlob)value).getBytes();
2051 } else if(SqlHelper.INSTANCE.isBlob(value)) {
2052 return SqlHelper.INSTANCE.getBlobBytes(value);
2053 }
2054
2055 ByteArrayOutputStream bout = new ByteArrayOutputStream();
2056
2057 if(value instanceof InputStream) {
2058 ByteUtil.copy((InputStream)value, bout);
2059 } else {
2060
2061 try(ObjectOutputStream oos = new ObjectOutputStream(bout)){
2062 oos.writeObject(value);
2063 }
2064 }
2065
2066 return bout.toByteArray();
2067 }
2068
2069
2070
2071
2072
2073 public static boolean toBooleanValue(Object obj) {
2074 if(obj == null) {
2075 return false;
2076 } else if(obj instanceof Boolean) {
2077 return ((Boolean)obj).booleanValue();
2078 } else if(obj instanceof Number) {
2079
2080 if(obj instanceof BigDecimal) {
2081 return (((BigDecimal)obj).compareTo(BigDecimal.ZERO) != 0);
2082 }
2083 if(obj instanceof BigInteger) {
2084 return (((BigInteger)obj).compareTo(BigInteger.ZERO) != 0);
2085 }
2086 return (((Number)obj).doubleValue() != 0.0d);
2087 }
2088 return Boolean.parseBoolean(obj.toString());
2089 }
2090
2091
2092
2093
2094 private static void fixNumericByteOrder(byte[] bytes)
2095 {
2096
2097 for(int i = 0; i < bytes.length; i+=4) {
2098 ByteUtil.swap4Bytes(bytes, i);
2099 }
2100 }
2101
2102
2103
2104
2105 protected static Object booleanToInteger(Object obj) {
2106 if (obj instanceof Boolean) {
2107 obj = ((Boolean) obj) ? -1 : 0;
2108 }
2109 return obj;
2110 }
2111
2112
2113
2114
2115
2116
2117 public static RawData rawDataWrapper(byte[] bytes) {
2118 return new RawData(bytes);
2119 }
2120
2121
2122
2123
2124
2125
2126 public static boolean isRawData(Object value) {
2127 return(value instanceof RawData);
2128 }
2129
2130
2131
2132
2133
2134 protected static void writeDefinitions(TableCreator creator, ByteBuffer buffer)
2135 {
2136
2137
2138
2139 int longVariableOffset = creator.countNonLongVariableLength();
2140 creator.setColumnOffsets(0, 0, longVariableOffset);
2141
2142 for (ColumnBuilder col : creator.getColumns()) {
2143 writeDefinition(creator, col, buffer);
2144 }
2145
2146 for (ColumnBuilder col : creator.getColumns()) {
2147 TableImpl.writeName(buffer, col.getName(), creator.getCharset());
2148 }
2149 }
2150
2151 protected static void writeDefinition(
2152 TableMutator mutator, ColumnBuilder col, ByteBuffer buffer)
2153 {
2154 TableMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
2155
2156 TableMutator.ColumnState colState = mutator.getColumnState(col);
2157
2158 buffer.put(col.getType().getValue());
2159 buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER);
2160 buffer.putShort(col.getColumnNumber());
2161
2162 buffer.putShort(colOffsets.getNextVariableOffset(col));
2163
2164 buffer.putShort(col.getColumnId());
2165
2166 if(col.getType().isTextual()) {
2167
2168
2169 writeSortOrder(buffer, col.getTextSortOrder(), mutator.getFormat());
2170 } else if(col.getType() == DataType.COMPLEX_TYPE) {
2171
2172
2173 buffer.putInt(colState.getComplexId());
2174 } else {
2175
2176 if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
2177 buffer.put(col.getPrecision());
2178 buffer.put(col.getScale());
2179 } else {
2180 buffer.put((byte) 0x00);
2181 buffer.put((byte) 0x00);
2182 }
2183
2184
2185 buffer.putShort((short) 0);
2186 }
2187
2188 buffer.put(getColumnBitFlags(col));
2189
2190
2191 byte extFlags = ((colState != null) ? colState.getExtraFlags() : 0);
2192 ComplexColumnDesc complexDesc = col.getComplexDesc();
2193 if((complexDesc != null) &&
2194 (complexDesc.getType() == ComplexDataType.VERSION_HISTORY)) {
2195
2196
2197 extFlags |= VERSION_HISTORY_EXT_FLAG_MASK;
2198 }
2199 if(col.isCalculated()) {
2200 extFlags |= CALCULATED_EXT_FLAG_MASK;
2201 } else if (col.isCompressedUnicode()) {
2202 extFlags |= COMPRESSED_UNICODE_EXT_FLAG_MASK;
2203 }
2204 buffer.put(extFlags);
2205
2206 buffer.putInt(0);
2207
2208
2209 if(col.isVariableLength()) {
2210 buffer.putShort((short) 0);
2211 } else {
2212 buffer.putShort(colOffsets.getNextFixedOffset(col));
2213 }
2214
2215 if(!col.getType().isLongValue()) {
2216 short length = col.getLength();
2217 if(col.isCalculated()) {
2218
2219 if(!col.getType().isVariableLength() ||
2220 col.getType().getHasScalePrecision()) {
2221 length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN;
2222 } else {
2223 length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
2224 }
2225 }
2226 buffer.putShort(length);
2227 } else {
2228 buffer.putShort((short)0x0000);
2229 }
2230 }
2231
2232 protected static void writeColUsageMapDefinitions(
2233 TableCreator creator, ByteBuffer buffer)
2234 {
2235
2236 for(ColumnBuilder lvalCol : creator.getLongValueColumns()) {
2237 writeColUsageMapDefinition(creator, lvalCol, buffer);
2238 }
2239 }
2240
2241 protected static void writeColUsageMapDefinition(
2242 TableMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
2243 {
2244 TableMutator.ColumnState colState = creator.getColumnState(lvalCol);
2245
2246 buffer.putShort(lvalCol.getColumnNumber());
2247
2248
2249 buffer.put(colState.getUmapOwnedRowNumber());
2250 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2251
2252 buffer.put(colState.getUmapFreeRowNumber());
2253 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2254 }
2255
2256
2257
2258
2259 static SortOrder readSortOrder(ByteBuffer buffer, int position,
2260 JetFormat format)
2261 {
2262 short value = buffer.getShort(position);
2263
2264 if(value == 0) {
2265
2266 return format.DEFAULT_SORT_ORDER;
2267 }
2268
2269 short variant = 0;
2270 short version = format.DEFAULT_SORT_ORDER.getVersion();
2271 if(format.SIZE_SORT_ORDER == 4) {
2272
2273
2274 variant = (short)ByteUtil.getUnsignedByte(buffer, position + 2);
2275 version = buffer.get(position + 3);
2276 }
2277
2278 if((value == GENERAL_SORT_ORDER_VALUE) && (variant == 0)) {
2279 if(version == GENERAL_SORT_ORDER.getVersion()) {
2280 return GENERAL_SORT_ORDER;
2281 }
2282 if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
2283 return GENERAL_LEGACY_SORT_ORDER;
2284 }
2285 if(version == GENERAL_97_SORT_ORDER.getVersion()) {
2286 return GENERAL_97_SORT_ORDER;
2287 }
2288 }
2289 return new SortOrder(value, variant, version);
2290 }
2291
2292
2293
2294
2295
2296 static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
2297 {
2298 int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
2299 return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
2300 }
2301
2302
2303
2304
2305 static byte readExtraFlags(ByteBuffer buffer, int offset, JetFormat format)
2306 {
2307 int extFlagsOffset = format.OFFSET_COLUMN_EXT_FLAGS;
2308 return ((extFlagsOffset >= 0) ? buffer.get(offset + extFlagsOffset) : 0);
2309 }
2310
2311
2312
2313
2314 static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
2315 JetFormat format) {
2316 if(sortOrder == null) {
2317 sortOrder = format.DEFAULT_SORT_ORDER;
2318 }
2319 buffer.putShort(sortOrder.getValue());
2320 if(format.SIZE_SORT_ORDER == 4) {
2321 buffer.put((byte)sortOrder.getVariant());
2322 buffer.put((byte)sortOrder.getVersion());
2323 }
2324 }
2325
2326
2327
2328
2329
2330 static boolean isImmutableValue(Object value) {
2331
2332 return !(value instanceof byte[]);
2333 }
2334
2335
2336
2337
2338
2339 public static Object toInternalValue(DataType dataType, Object value,
2340 DatabaseImpl db)
2341 throws IOException
2342 {
2343 return toInternalValue(dataType, value, db, null);
2344 }
2345
2346 static Object toInternalValue(DataType dataType, Object value,
2347 DatabaseImpl db,
2348 ColumnImpl.DateTimeFactory factory)
2349 throws IOException
2350 {
2351 if(value == null) {
2352 return null;
2353 }
2354
2355 switch(dataType) {
2356 case BOOLEAN:
2357 return ((value instanceof Boolean) ? value : toBooleanValue(value));
2358 case BYTE:
2359 return ((value instanceof Byte) ? value : toNumber(value, db).byteValue());
2360 case INT:
2361 return ((value instanceof Short) ? value :
2362 toNumber(value, db).shortValue());
2363 case LONG:
2364 return ((value instanceof Integer) ? value :
2365 toNumber(value, db).intValue());
2366 case MONEY:
2367 return toBigDecimal(value, db);
2368 case FLOAT:
2369 return ((value instanceof Float) ? value :
2370 toNumber(value, db).floatValue());
2371 case DOUBLE:
2372 return ((value instanceof Double) ? value :
2373 toNumber(value, db).doubleValue());
2374 case SHORT_DATE_TIME:
2375 if(factory == null) {
2376 factory = db.getDateTimeFactory();
2377 }
2378 return factory.toInternalValue(db, value);
2379 case TEXT:
2380 case MEMO:
2381 case GUID:
2382 return ((value instanceof String) ? value :
2383 toCharSequence(value).toString());
2384 case NUMERIC:
2385 return toBigDecimal(value, db);
2386 case COMPLEX_TYPE:
2387
2388 return value;
2389 case BIG_INT:
2390 return ((value instanceof Long) ? value :
2391 toNumber(value, db).longValue());
2392 case EXT_DATE_TIME:
2393 return toLocalDateTime(value, db);
2394 default:
2395
2396 return toByteArray(value);
2397 }
2398 }
2399
2400 protected static DateTimeFactory getDateTimeFactory(DateTimeType type) {
2401 return ((type == DateTimeType.LOCAL_DATE_TIME) ?
2402 LDT_DATE_TIME_FACTORY : DEF_DATE_TIME_FACTORY);
2403 }
2404
2405 String withErrorContext(String msg) {
2406 return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
2407 }
2408
2409 boolean isThisColumn(Identifier identifier) {
2410 return(getTable().isThisTable(identifier) &&
2411 getName().equalsIgnoreCase(identifier.getObjectName()));
2412 }
2413
2414 private static String withErrorContext(
2415 String msg, DatabaseImpl db, String tableName, String colName) {
2416 return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" +
2417 colName + ")";
2418 }
2419
2420
2421
2422
2423
2424
2425 @SuppressWarnings("deprecation")
2426 private static final class DateExt extends Date
2427 {
2428 private static final long serialVersionUID = 0L;
2429
2430
2431 private transient final long _dateBits;
2432
2433 private DateExt(long time, long dateBits) {
2434 super(time);
2435 _dateBits = dateBits;
2436 }
2437
2438 public long getDateBits() {
2439 return _dateBits;
2440 }
2441
2442 @Override
2443 public void setDate(int time) {
2444 throw new UnsupportedOperationException();
2445 }
2446
2447 @Override
2448 public void setHours(int time) {
2449 throw new UnsupportedOperationException();
2450 }
2451
2452 @Override
2453 public void setMinutes(int time) {
2454 throw new UnsupportedOperationException();
2455 }
2456
2457 @Override
2458 public void setMonth(int time) {
2459 throw new UnsupportedOperationException();
2460 }
2461
2462 @Override
2463 public void setSeconds(int time) {
2464 throw new UnsupportedOperationException();
2465 }
2466
2467 @Override
2468 public void setYear(int time) {
2469 throw new UnsupportedOperationException();
2470 }
2471
2472 @Override
2473 public void setTime(long time) {
2474 throw new UnsupportedOperationException();
2475 }
2476
2477 private Object writeReplace() throws ObjectStreamException {
2478
2479
2480 return new Date(super.getTime());
2481 }
2482 }
2483
2484
2485
2486
2487 private static final class RawData implements Serializable, InMemoryBlob
2488 {
2489 private static final long serialVersionUID = 0L;
2490
2491 private final byte[] _bytes;
2492
2493 private RawData(byte[] bytes) {
2494 _bytes = bytes;
2495 }
2496
2497 @Override
2498 public byte[] getBytes() {
2499 return _bytes;
2500 }
2501
2502 @Override
2503 public String toString() {
2504 return ToStringBuilder.valueBuilder(this)
2505 .append(null, getBytes())
2506 .toString();
2507 }
2508
2509 private Object writeReplace() throws ObjectStreamException {
2510
2511
2512 return getBytes();
2513 }
2514 }
2515
2516
2517
2518
2519
2520 public abstract class AutoNumberGenerator
2521 {
2522 protected AutoNumberGenerator() {}
2523
2524
2525
2526
2527
2528 public abstract Object getLast();
2529
2530
2531
2532
2533
2534
2535
2536 public abstract Object getNext(TableImpl.WriteRowState writeRowState);
2537
2538
2539
2540
2541
2542
2543
2544 public abstract Object handleInsert(
2545 TableImpl.WriteRowState writeRowState, Object inRowValue)
2546 throws IOException;
2547
2548
2549
2550
2551 public abstract void restoreLast(Object last);
2552
2553
2554
2555
2556 public abstract DataType getType();
2557 }
2558
2559 private final class LongAutoNumberGenerator extends AutoNumberGenerator
2560 {
2561 private LongAutoNumberGenerator() {}
2562
2563 @Override
2564 public Object getLast() {
2565
2566 return getTable().getLastLongAutoNumber();
2567 }
2568
2569 @Override
2570 public Object getNext(TableImpl.WriteRowState writeRowState) {
2571
2572 return getTable().getNextLongAutoNumber();
2573 }
2574
2575 @Override
2576 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2577 Object inRowValue)
2578 throws IOException
2579 {
2580 int inAutoNum = toNumber(inRowValue).intValue();
2581 if(inAutoNum <= INVALID_AUTO_NUMBER &&
2582 !getTable().isAllowAutoNumberInsert()) {
2583 throw new InvalidValueException(withErrorContext(
2584 "Invalid auto number value " + inAutoNum));
2585 }
2586
2587 getTable().adjustLongAutoNumber(inAutoNum);
2588 return inAutoNum;
2589 }
2590
2591 @Override
2592 public void restoreLast(Object last) {
2593 if(last instanceof Integer) {
2594 getTable().restoreLastLongAutoNumber((Integer)last);
2595 }
2596 }
2597
2598 @Override
2599 public DataType getType() {
2600 return DataType.LONG;
2601 }
2602 }
2603
2604 private final class GuidAutoNumberGenerator extends AutoNumberGenerator
2605 {
2606 private Object _lastAutoNumber;
2607
2608 private GuidAutoNumberGenerator() {}
2609
2610 @Override
2611 public Object getLast() {
2612 return _lastAutoNumber;
2613 }
2614
2615 @Override
2616 public Object getNext(TableImpl.WriteRowState writeRowState) {
2617 _lastAutoNumber = toGUIDString(UUID.randomUUID());
2618 return _lastAutoNumber;
2619 }
2620
2621 @Override
2622 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2623 Object inRowValue)
2624 throws IOException
2625 {
2626 _lastAutoNumber = toCharSequence(inRowValue);
2627 return _lastAutoNumber;
2628 }
2629
2630 @Override
2631 public void restoreLast(Object last) {
2632 _lastAutoNumber = null;
2633 }
2634
2635 @Override
2636 public DataType getType() {
2637 return DataType.GUID;
2638 }
2639 }
2640
2641 private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
2642 {
2643 private ComplexTypeAutoNumberGenerator() {}
2644
2645 @Override
2646 public Object getLast() {
2647
2648 return getTable().getLastComplexTypeAutoNumber();
2649 }
2650
2651 @Override
2652 public Object getNext(TableImpl.WriteRowState writeRowState) {
2653
2654 int nextComplexAutoNum = writeRowState.getComplexAutoNumber();
2655 if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) {
2656
2657 nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber();
2658 writeRowState.setComplexAutoNumber(nextComplexAutoNum);
2659 }
2660 return new ComplexValueForeignKeyImpl(ColumnImpl.this,
2661 nextComplexAutoNum);
2662 }
2663
2664 @Override
2665 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2666 Object inRowValue)
2667 throws IOException
2668 {
2669 ComplexValueForeignKey inComplexFK = null;
2670 if(inRowValue instanceof ComplexValueForeignKey) {
2671 inComplexFK = (ComplexValueForeignKey)inRowValue;
2672 } else {
2673 inComplexFK = new ComplexValueForeignKeyImpl(
2674 ColumnImpl.this, toNumber(inRowValue).intValue());
2675 }
2676
2677 if(inComplexFK.getColumn() != ColumnImpl.this) {
2678 throw new InvalidValueException(withErrorContext(
2679 "Wrong column for complex value foreign key, found " +
2680 inComplexFK.getColumn().getName()));
2681 }
2682 if(inComplexFK.get() < 1) {
2683 throw new InvalidValueException(withErrorContext(
2684 "Invalid complex value foreign key value " + inComplexFK.get()));
2685 }
2686
2687 int prevRowValue = writeRowState.getComplexAutoNumber();
2688 if(prevRowValue <= INVALID_AUTO_NUMBER) {
2689 writeRowState.setComplexAutoNumber(inComplexFK.get());
2690 } else if(prevRowValue != inComplexFK.get()) {
2691 throw new InvalidValueException(withErrorContext(
2692 "Inconsistent complex value foreign key values: found " +
2693 prevRowValue + ", given " + inComplexFK));
2694 }
2695
2696
2697 getTable().adjustComplexTypeAutoNumber(inComplexFK.get());
2698
2699 return inComplexFK;
2700 }
2701
2702 @Override
2703 public void restoreLast(Object last) {
2704 if(last instanceof ComplexValueForeignKey) {
2705 getTable().restoreLastComplexTypeAutoNumber(
2706 ((ComplexValueForeignKey)last).get());
2707 }
2708 }
2709
2710 @Override
2711 public DataType getType() {
2712 return DataType.COMPLEX_TYPE;
2713 }
2714 }
2715
2716 private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
2717 {
2718 private final DataType _genType;
2719
2720 private UnsupportedAutoNumberGenerator(DataType genType) {
2721 _genType = genType;
2722 }
2723
2724 @Override
2725 public Object getLast() {
2726 return null;
2727 }
2728
2729 @Override
2730 public Object getNext(TableImpl.WriteRowState writeRowState) {
2731 throw new UnsupportedOperationException();
2732 }
2733
2734 @Override
2735 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2736 Object inRowValue) {
2737 throw new UnsupportedOperationException();
2738 }
2739
2740 @Override
2741 public void restoreLast(Object last) {
2742 throw new UnsupportedOperationException();
2743 }
2744
2745 @Override
2746 public DataType getType() {
2747 return _genType;
2748 }
2749 }
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764 public static final class SortOrder
2765 {
2766 private final short _value;
2767 private final short _variant;
2768 private final short _version;
2769
2770 public SortOrder(short value, short version) {
2771 this(value, (short)0, version);
2772 }
2773
2774 public SortOrder(short value, short variant, short version) {
2775 _value = value;
2776 _variant = variant;
2777 _version = version;
2778 }
2779
2780 public short getValue() {
2781 return _value;
2782 }
2783
2784
2785
2786
2787 public short getVariant() {
2788 return _variant;
2789 }
2790
2791 public short getVersion() {
2792 return _version;
2793 }
2794
2795
2796
2797
2798
2799
2800 public int getSortId() {
2801 if(_version < 0) {
2802 throw new IllegalStateException(
2803 "sort order " + this + " has no sort id");
2804 }
2805 return ((_version << 24) | ((_variant & 0xFF) << 16) |
2806 (_value & 0xFFFF));
2807 }
2808
2809 @Override
2810 public int hashCode() {
2811 return _value;
2812 }
2813
2814 @Override
2815 public boolean equals(Object o) {
2816 return ((this == o) ||
2817 ((o != null) && (getClass() == o.getClass()) &&
2818 (_value == ((SortOrder)o)._value) &&
2819 (_variant == ((SortOrder)o)._variant) &&
2820 (_version == ((SortOrder)o)._version)));
2821 }
2822
2823 @Override
2824 public String toString() {
2825 LocaleUtil.LcidInfo info = LocaleUtil.getInfo(_value);
2826 String valueStr = ((_version >= 0) ?
2827 String.format("0x%08X", getSortId()) :
2828 (_value + "(" + _variant + "," + _version + ")"));
2829 return ToStringBuilder.valueBuilder(this)
2830 .append(null, (info != null) ? (valueStr + ", " + info) : valueStr)
2831 .toString();
2832 }
2833 }
2834
2835
2836
2837
2838 static final class InitArgs
2839 {
2840 public final TableImpl table;
2841 public final ByteBuffer buffer;
2842 public final int offset;
2843 public final String name;
2844 public final int displayIndex;
2845 public final byte colType;
2846 public final byte flags;
2847 public final byte extFlags;
2848 public DataType type;
2849
2850 InitArgs(TableImpl newTable, ByteBuffer newBuffer, int newOffset,
2851 String newName, int newDisplayIndex) {
2852 this.table = newTable;
2853 this.buffer = newBuffer;
2854 this.offset = newOffset;
2855 this.name = newName;
2856 this.displayIndex = newDisplayIndex;
2857
2858 this.colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
2859 this.flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
2860 this.extFlags = readExtraFlags(buffer, offset, table.getFormat());
2861 }
2862 }
2863
2864
2865
2866
2867
2868 private static final class RequiredColValidator extends InternalColumnValidator
2869 {
2870 private RequiredColValidator(ColumnValidator delegate) {
2871 super(delegate);
2872 }
2873
2874 @Override
2875 protected Object internalValidate(Column col, Object val)
2876 throws IOException
2877 {
2878 if(val == null) {
2879 throw new InvalidValueException(
2880 ((ColumnImpl)col).withErrorContext(
2881 "Missing value for required column"));
2882 }
2883 return val;
2884 }
2885
2886 @Override
2887 protected void appendToString(StringBuilder sb) {
2888 sb.append("required=true");
2889 }
2890 }
2891
2892
2893
2894
2895
2896 private static final class NoZeroLenColValidator extends InternalColumnValidator
2897 {
2898 private NoZeroLenColValidator(ColumnValidator delegate) {
2899 super(delegate);
2900 }
2901
2902 @Override
2903 protected Object internalValidate(Column col, Object val)
2904 throws IOException
2905 {
2906 CharSequence valStr = toCharSequence(val);
2907
2908 if((valStr != null) && valStr.length() == 0) {
2909 throw new InvalidValueException(
2910 ((ColumnImpl)col).withErrorContext(
2911 "Zero length string is not allowed"));
2912 }
2913 return valStr;
2914 }
2915
2916 @Override
2917 protected void appendToString(StringBuilder sb) {
2918 sb.append("allowZeroLength=false");
2919 }
2920 }
2921
2922
2923
2924
2925 protected static abstract class DateTimeFactory
2926 {
2927 public abstract DateTimeType getType();
2928
2929 public abstract Object fromDateBits(ColumnImpl col, long dateBits);
2930
2931 public abstract double toDateDouble(Object value, DateTimeContext dtc);
2932
2933 public abstract Object toInternalValue(DatabaseImpl db, Object value);
2934 }
2935
2936
2937
2938
2939 private static final class DefaultDateTimeFactory extends DateTimeFactory
2940 {
2941 @Override
2942 public DateTimeType getType() {
2943 return DateTimeType.DATE;
2944 }
2945
2946 @Override
2947 public Object fromDateBits(ColumnImpl col, long dateBits) {
2948 long time = col.fromDateDouble(
2949 Double.longBitsToDouble(dateBits));
2950 return new DateExt(time, dateBits);
2951 }
2952
2953 @Override
2954 public double toDateDouble(Object value, DateTimeContext dtc) {
2955
2956
2957
2958 long time = 0L;
2959 if(value instanceof TemporalAccessor) {
2960 time = toInstant((TemporalAccessor)value, dtc).toEpochMilli();
2961 } else {
2962 time = toDateLong(value);
2963 }
2964
2965
2966 time += getToLocalTimeZoneOffset(time, dtc.getTimeZone());
2967 return toLocalDateDouble(time);
2968 }
2969
2970 @Override
2971 public Object toInternalValue(DatabaseImpl db, Object value) {
2972 return ((value instanceof Date) ? value :
2973 new Date(toDateLong(value)));
2974 }
2975 }
2976
2977
2978
2979
2980 private static final class LDTDateTimeFactory extends DateTimeFactory
2981 {
2982 @Override
2983 public DateTimeType getType() {
2984 return DateTimeType.LOCAL_DATE_TIME;
2985 }
2986
2987 @Override
2988 public Object fromDateBits(ColumnImpl col, long dateBits) {
2989 return ldtFromLocalDateDouble(Double.longBitsToDouble(dateBits));
2990 }
2991
2992 @Override
2993 public double toDateDouble(Object value, DateTimeContext dtc) {
2994
2995
2996
2997 if(!(value instanceof TemporalAccessor)) {
2998 value = Instant.ofEpochMilli(toDateLong(value));
2999 }
3000 return ColumnImpl.toDateDouble(
3001 temporalToLocalDateTime((TemporalAccessor)value, dtc));
3002 }
3003
3004 @Override
3005 public Object toInternalValue(DatabaseImpl db, Object value) {
3006 return toLocalDateTime(value, db);
3007 }
3008 }
3009
3010
3011 static interface InMemoryBlob {
3012 public byte[] getBytes() throws IOException;
3013 }
3014 }