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