1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess;
18
19 import java.io.IOException;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22 import java.sql.Types;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.time.LocalDateTime;
27
28 import com.healthmarketscience.jackcess.impl.DatabaseImpl;
29 import com.healthmarketscience.jackcess.impl.JetFormat;
30 import com.healthmarketscience.jackcess.impl.SqlHelper;
31
32
33
34
35
36
37
38 public enum DataType {
39
40
41
42
43
44
45 BOOLEAN((byte) 0x01, Types.BOOLEAN, 0),
46
47
48
49
50
51
52 BYTE((byte) 0x02, Types.TINYINT, 1),
53
54
55
56
57
58
59 INT((byte) 0x03, Types.SMALLINT, 2),
60
61
62
63
64
65
66 LONG((byte) 0x04, Types.INTEGER, 4),
67
68
69
70
71
72
73
74 MONEY((byte) 0x05, Types.DECIMAL, 8, false, false, 0, 0, 0, false, 4, 4, 4,
75 19, 19, 19, 1),
76
77
78
79
80
81
82 FLOAT((byte) 0x06, Types.FLOAT, 4),
83
84
85
86
87
88
89 DOUBLE((byte) 0x07, Types.DOUBLE, 8),
90
91
92
93
94
95
96 SHORT_DATE_TIME((byte) 0x08, Types.TIMESTAMP, 8),
97
98
99
100
101
102 BINARY((byte) 0x09, Types.BINARY, null, true, false, 0, 255, 255, 1),
103
104
105
106
107
108 TEXT((byte) 0x0A, Types.VARCHAR, null, true, false, 0,
109 JetFormat.TEXT_FIELD_MAX_LENGTH, JetFormat.TEXT_FIELD_MAX_LENGTH,
110 JetFormat.TEXT_FIELD_UNIT_SIZE),
111
112
113
114
115
116 OLE((byte) 0x0B, Types.LONGVARBINARY, null, true, true, 0, 0, 0x3FFFFFFF,
117 1),
118
119
120
121
122
123
124 MEMO((byte) 0x0C, Types.LONGVARCHAR, null, true, true, 0, 0, 0x3FFFFFFF,
125 JetFormat.TEXT_FIELD_UNIT_SIZE),
126
127
128
129 UNKNOWN_0D((byte) 0x0D, null, null, true, false, 0, 255, 255, 1),
130
131
132
133
134
135
136
137 GUID((byte) 0x0F, null, 16),
138
139
140
141
142
143
144
145 NUMERIC((byte) 0x10, Types.NUMERIC, 17, true, false, 17, 17, 17,
146 true, 0, 0, 28, 1, 18, 28, 1),
147
148
149
150
151 UNKNOWN_11((byte) 0x11, null, 3992),
152
153
154
155
156 COMPLEX_TYPE((byte) 0x12, null, 4),
157
158
159
160
161
162
163 BIG_INT((byte) 0x13, Types.BIGINT, 8),
164
165
166
167
168
169
170
171 EXT_DATE_TIME((byte) 0x14, null, 42),
172
173
174
175
176 UNSUPPORTED_FIXEDLEN((byte) 0xFE, null, null),
177
178
179
180
181 UNSUPPORTED_VARLEN((byte) 0xFF, null, null, true, false, 0, 0, 0x3FFFFFFF,
182 1);
183
184
185 private static final Map<Integer, DataType[]> SQL_TYPES =
186 new HashMap<Integer, DataType[]>();
187
188 private static final Map<Integer, DataType> ALT_SQL_TYPES =
189 new HashMap<Integer, DataType>();
190 static {
191 for (DataType type : DataType.values()) {
192 if (type._sqlType != null) {
193 SQL_TYPES.put(type._sqlType, new DataType[]{type});
194 }
195 }
196 SQL_TYPES.put(Types.BIT, new DataType[]{BYTE});
197 SQL_TYPES.put(Types.BLOB, new DataType[]{OLE});
198 SQL_TYPES.put(Types.CLOB, new DataType[]{MEMO});
199 SQL_TYPES.put(Types.BIGINT, new DataType[]{LONG, BIG_INT});
200 SQL_TYPES.put(Types.CHAR, new DataType[]{TEXT});
201 SQL_TYPES.put(Types.DATE, new DataType[]{SHORT_DATE_TIME});
202 SQL_TYPES.put(Types.REAL, new DataType[]{DOUBLE});
203 SQL_TYPES.put(Types.TIME, new DataType[]{SHORT_DATE_TIME});
204 SQL_TYPES.put(Types.VARBINARY, new DataType[]{BINARY});
205
206
207 ALT_SQL_TYPES.put(Types.VARCHAR, MEMO);
208 ALT_SQL_TYPES.put(Types.VARBINARY, OLE);
209 ALT_SQL_TYPES.put(Types.BINARY, OLE);
210
211
212 addNewSqlType("NCHAR", TEXT, null);
213 addNewSqlType("NVARCHAR", TEXT, MEMO);
214 addNewSqlType("LONGNVARCHAR", MEMO, null);
215 addNewSqlType("NCLOB", MEMO, null);
216 addNewSqlType("TIME_WITH_TIMEZONE", SHORT_DATE_TIME, null);
217 addNewSqlType("TIMESTAMP_WITH_TIMEZONE", SHORT_DATE_TIME, null);
218 }
219
220 private static Map<Byte, DataType> DATA_TYPES = new HashMap<Byte, DataType>();
221 static {
222 for (DataType type : DataType.values()) {
223 if(type.isUnsupported()) {
224 continue;
225 }
226 DATA_TYPES.put(type._value, type);
227 }
228 }
229
230
231 private final boolean _variableLength;
232
233 private final boolean _longValue;
234
235 private final boolean _hasScalePrecision;
236
237 private final byte _value;
238
239 private final Integer _fixedSize;
240
241 private final int _minSize;
242
243 private final int _defaultSize;
244
245 private final int _maxSize;
246
247 private final Integer _sqlType;
248
249 private final int _minScale;
250
251 private final int _defaultScale;
252
253 private final int _maxScale;
254
255 private final int _minPrecision;
256
257 private final int _defaultPrecision;
258
259 private final int _maxPrecision;
260
261 private final int _unitSize;
262
263 private DataType(byte value) {
264 this(value, null, null);
265 }
266
267 private DataType(byte value, Integer sqlType, Integer fixedSize) {
268 this(value, sqlType, fixedSize, false, false, 0, 0, 0, 1);
269 }
270
271 private DataType(byte value, Integer sqlType, Integer fixedSize,
272 boolean variableLength,
273 boolean longValue,
274 int minSize,
275 int defaultSize,
276 int maxSize,
277 int unitSize) {
278 this(value, sqlType, fixedSize, variableLength, longValue,
279 minSize, defaultSize, maxSize,
280 false, 0, 0, 0, 0, 0, 0, unitSize);
281 }
282
283 private DataType(byte value, Integer sqlType, Integer fixedSize,
284 boolean variableLength,
285 boolean longValue,
286 int minSize,
287 int defaultSize,
288 int maxSize,
289 boolean hasScalePrecision,
290 int minScale,
291 int defaultScale,
292 int maxScale,
293 int minPrecision,
294 int defaultPrecision,
295 int maxPrecision,
296 int unitSize) {
297 _value = value;
298 _sqlType = sqlType;
299 _fixedSize = fixedSize;
300 _variableLength = variableLength;
301 _longValue = longValue;
302 _minSize = minSize;
303 _defaultSize = defaultSize;
304 _maxSize = maxSize;
305 _hasScalePrecision = hasScalePrecision;
306 _minScale = minScale;
307 _defaultScale = defaultScale;
308 _maxScale = maxScale;
309 _minPrecision = minPrecision;
310 _defaultPrecision = defaultPrecision;
311 _maxPrecision = maxPrecision;
312 _unitSize = unitSize;
313 }
314
315 public byte getValue() {
316 return _value;
317 }
318
319 public boolean isVariableLength() {
320 return _variableLength;
321 }
322
323 public boolean isTrueVariableLength() {
324
325
326 return (isVariableLength() && (getMinSize() != getMaxSize()));
327 }
328
329 public boolean isLongValue() {
330 return _longValue;
331 }
332
333 public boolean getHasScalePrecision() {
334 return _hasScalePrecision;
335 }
336
337 public int getFixedSize() {
338 return getFixedSize(null);
339 }
340
341 public int getFixedSize(Short colLength) {
342 if(_fixedSize != null) {
343 if(colLength != null) {
344 return Math.max(_fixedSize, colLength);
345 }
346 return _fixedSize;
347 }
348 if(colLength != null) {
349 return colLength;
350 }
351 throw new IllegalArgumentException("Unexpected fixed length column " +
352 this);
353 }
354
355 public int getMinSize() {
356 return _minSize;
357 }
358
359 public int getDefaultSize() {
360 return _defaultSize;
361 }
362
363 public int getMaxSize() {
364 return _maxSize;
365 }
366
367 public int getSQLType() throws IOException {
368 if (_sqlType != null) {
369 return _sqlType;
370 }
371 throw new JackcessException("Unsupported data type: " + toString());
372 }
373
374 public int getMinScale() {
375 return _minScale;
376 }
377
378 public int getDefaultScale() {
379 return _defaultScale;
380 }
381
382 public int getMaxScale() {
383 return _maxScale;
384 }
385
386 public int getMinPrecision() {
387 return _minPrecision;
388 }
389
390 public int getDefaultPrecision() {
391 return _defaultPrecision;
392 }
393
394 public int getMaxPrecision() {
395 return _maxPrecision;
396 }
397
398 public int getUnitSize() {
399 return _unitSize;
400 }
401
402 public int getUnitSize(JetFormat format) {
403 if((format != null) && isTextual()) {
404 return format.SIZE_TEXT_FIELD_UNIT;
405 }
406 return _unitSize;
407 }
408
409 public int toUnitSize(int size) {
410 return toUnitSize(size, null);
411 }
412
413 public int toUnitSize(int size, JetFormat format) {
414 return(size / getUnitSize(format));
415 }
416
417 public int fromUnitSize(int unitSize) {
418 return fromUnitSize(unitSize, null);
419 }
420
421 public int fromUnitSize(int unitSize, JetFormat format) {
422 return(unitSize * getUnitSize(format));
423 }
424
425 public boolean isValidSize(int size) {
426 return isWithinRange(size, getMinSize(), getMaxSize());
427 }
428
429 public boolean isValidScale(int scale) {
430 return isWithinRange(scale, getMinScale(), getMaxScale());
431 }
432
433 public boolean isValidPrecision(int precision) {
434 return isWithinRange(precision, getMinPrecision(), getMaxPrecision());
435 }
436
437 private static boolean isWithinRange(int value, int minValue, int maxValue) {
438 return((value >= minValue) && (value <= maxValue));
439 }
440
441 public int toValidSize(int size) {
442 return toValidRange(size, getMinSize(), getMaxSize());
443 }
444
445 public int toValidScale(int scale) {
446 return toValidRange(scale, getMinScale(), getMaxScale());
447 }
448
449 public int toValidPrecision(int precision) {
450 return toValidRange(precision, getMinPrecision(), getMaxPrecision());
451 }
452
453 public boolean isTextual() {
454 return ((this == TEXT) || (this == MEMO));
455 }
456
457 public boolean mayBeAutoNumber() {
458 return((this == LONG) || (this == GUID) || (this == COMPLEX_TYPE));
459 }
460
461 public boolean isMultipleAutoNumberAllowed() {
462 return (this == COMPLEX_TYPE);
463 }
464
465 public boolean isUnsupported() {
466 return((this == UNSUPPORTED_FIXEDLEN) || (this == UNSUPPORTED_VARLEN));
467 }
468
469 private static int toValidRange(int value, int minValue, int maxValue) {
470 return((value > maxValue) ? maxValue :
471 ((value < minValue) ? minValue : value));
472 }
473
474 public static DataType fromByte(byte b) throws IOException {
475 DataType rtn = DATA_TYPES.get(b);
476 if (rtn != null) {
477 return rtn;
478 }
479 throw new IOException("Unrecognized data type: " + b);
480 }
481
482 public static DataType fromSQLType(int sqlType)
483 throws IOException
484 {
485 return fromSQLType(sqlType, 0, null);
486 }
487
488 public static DataType fromSQLType(int sqlType, int lengthInUnits)
489 throws IOException
490 {
491 return fromSQLType(sqlType, lengthInUnits, null);
492 }
493
494 public static DataType fromSQLType(int sqlType, int lengthInUnits,
495 Database.FileFormat fileFormat)
496 throws IOException
497 {
498 DataType[] rtnArr = SQL_TYPES.get(sqlType);
499 if(rtnArr == null) {
500 throw new JackcessException("Unsupported SQL type: " + sqlType);
501 }
502 JetFormat format =
503 ((fileFormat != null) ?
504 DatabaseImpl.getFileFormatDetails(fileFormat).getFormat() :
505 null);
506 DataType rtn = rtnArr[0];
507 if((rtnArr.length > 1) && (format != null)) {
508
509
510
511 for(int i = rtnArr.length - 1; i >= 0; --i) {
512 DataType tmp = rtnArr[i];
513 if(format.isSupportedDataType(tmp)) {
514 rtn = tmp;
515 break;
516 }
517 }
518 }
519
520
521 int size = rtn.fromUnitSize(lengthInUnits, format);
522 if(rtn.isVariableLength() && !rtn.isValidSize(size)) {
523
524
525 DataType altRtn = ALT_SQL_TYPES.get(sqlType);
526 if((altRtn != null) &&
527 (altRtn.isLongValue() || altRtn.isValidSize(size))) {
528
529 rtn = altRtn;
530 }
531 }
532
533 return rtn;
534 }
535
536
537
538
539
540 private static void addNewSqlType(String typeName, DataType type,
541 DataType altType)
542 {
543 try {
544 Integer value = SqlHelper.INSTANCE.getNewSqlType(typeName);
545 SQL_TYPES.put(value, new DataType[]{type});
546 if(altType != null) {
547 ALT_SQL_TYPES.put(value, altType);
548 }
549 } catch(Exception ignored) {
550
551 }
552 }
553
554 }