From 4fb8409266a5b586c1240282014dc31b53cfb31b Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 19 Dec 2017 04:59:39 +0000 Subject: [PATCH] Add full support for access 2016 databases including the new 'Large Number' (aka Bigint) data type. Fixes feature #37 git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1130 f203690c-595d-4dc9-a70b-905162fa7fd2 --- README.txt | 2 +- src/changes/changes.xml | 7 ++ .../jackcess/DataType.java | 7 ++ .../jackcess/Database.java | 2 + .../jackcess/impl/CalculatedColumnUtil.java | 2 +- .../jackcess/impl/ColumnImpl.java | 8 ++ .../jackcess/impl/ComplexColumnSupport.java | 3 +- .../jackcess/impl/DatabaseImpl.java | 3 +- .../jackcess/impl/IndexData.java | 1 + .../jackcess/impl/JetFormat.java | 66 +++++++++--- .../jackcess/impl/PropertyMapImpl.java | 2 + .../jackcess/empty2016.accdb | Bin 0 -> 364544 bytes src/site/fml/faq.fml | 2 +- src/site/xdoc/cookbook.xml | 2 +- src/site/xdoc/index.xml | 2 +- .../jackcess/impl/BigIntTest.java | 102 ++++++++++++++++++ 16 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 src/main/resources/com/healthmarketscience/jackcess/empty2016.accdb create mode 100644 src/test/java/com/healthmarketscience/jackcess/impl/BigIntTest.java diff --git a/README.txt b/README.txt index 178aea2..b46c7d7 100644 --- a/README.txt +++ b/README.txt @@ -1,7 +1,7 @@ Jackcess Jackcess is a pure Java library for reading from and writing to MS Access -databases (currently supporting versions 2000-2013). It is not an +databases (currently supporting versions 2000-2016). It is not an application. There is no GUI. It's a library, intended for other developers to use to build Java applications. Jackcess is licensed under the Apache License (as of version 2.1.0). diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1825256..0ff4525 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -4,6 +4,13 @@ Tim McCune + + + Add full support for access 2016 databases including the new "Large + Number" (aka Bigint) data type. + + Handle more advanced query join constructs. diff --git a/src/main/java/com/healthmarketscience/jackcess/DataType.java b/src/main/java/com/healthmarketscience/jackcess/DataType.java index 5a5cebe..9baaba6 100644 --- a/src/main/java/com/healthmarketscience/jackcess/DataType.java +++ b/src/main/java/com/healthmarketscience/jackcess/DataType.java @@ -151,6 +151,13 @@ public enum DataType { * which is the key for a secondary table which holds the "real" data. */ COMPLEX_TYPE((byte) 0x12, null, 4), + /** + * Corresponds to a java {@link Long}. Accepts any {@link Number} (using + * {@link Number#longValue}), Boolean as 1 or 0, any Object converted to a + * String and parsed as Double, or {@code null}. Equivalent to SQL + * {@link Types#BIGINT}. + */ + BIG_INT((byte) 0x13, null, 8), /** * Dummy type for a fixed length type which is not currently supported. * Handled like a fixed length {@link #BINARY}. diff --git a/src/main/java/com/healthmarketscience/jackcess/Database.java b/src/main/java/com/healthmarketscience/jackcess/Database.java index 42eb216..5644d09 100644 --- a/src/main/java/com/healthmarketscience/jackcess/Database.java +++ b/src/main/java/com/healthmarketscience/jackcess/Database.java @@ -143,6 +143,8 @@ public interface Database extends Iterable, Closeable, Flushable V2007(".accdb"), /** A database which was created by MS Access 2010+ */ V2010(".accdb"), + /** A database which was created by MS Access 2016+ */ + V2016(".accdb"), /** A database which was created by MS Money */ MSISAM(".mny"); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java index f3fdc21..e77963f 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java @@ -26,7 +26,7 @@ import java.nio.ByteOrder; * Utility code for dealing with calculated columns. *

* These are the currently possible calculated types: FLOAT, DOUBLE, INT, - * LONG, GUID, SHORT_DATE_TIME, MONEY, BOOLEAN, NUMERIC, TEXT, MEMO. + * LONG, BIG_INT, GUID, SHORT_DATE_TIME, MONEY, BOOLEAN, NUMERIC, TEXT, MEMO. * * @author James Ahlborn */ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index c5d0449..b5a0d0e 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -632,6 +632,8 @@ public class ColumnImpl implements Column, Comparable { return data; case COMPLEX_TYPE: return new ComplexValueForeignKeyImpl(this, buffer.getInt()); + case BIG_INT: + return Long.valueOf(buffer.getLong()); default: throw new IOException(withErrorContext("Unrecognized data type: " + _type)); } @@ -1128,6 +1130,9 @@ public class ColumnImpl implements Column, Comparable { case COMPLEX_TYPE: buffer.putInt(toNumber(obj).intValue()); break; + case BIG_INT: + buffer.putLong(toNumber(obj).longValue()); + break; case UNSUPPORTED_FIXEDLEN: byte[] bytes = toByteArray(obj); if(bytes.length != getLength()) { @@ -1800,6 +1805,9 @@ public class ColumnImpl implements Column, Comparable { case COMPLEX_TYPE: // leave alone for now? return value; + case BIG_INT: + return ((value instanceof Long) ? value : + toNumber(value).longValue()); default: // some variation of binary data return toByteArray(value); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java b/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java index 954791b..93dbf03 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java @@ -53,7 +53,8 @@ public class ComplexColumnSupport private static final Set MULTI_VALUE_TYPES = EnumSet.of( DataType.BYTE, DataType.INT, DataType.LONG, DataType.FLOAT, - DataType.DOUBLE, DataType.GUID, DataType.NUMERIC, DataType.TEXT); + DataType.DOUBLE, DataType.GUID, DataType.NUMERIC, DataType.TEXT, + DataType.BIG_INT); /** diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java index 15c79e0..cc76ccb 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java @@ -116,6 +116,7 @@ public class DatabaseImpl implements Database addFileFormatDetails(FileFormat.V2003, "empty2003", JetFormat.VERSION_4); addFileFormatDetails(FileFormat.V2007, "empty2007", JetFormat.VERSION_12); addFileFormatDetails(FileFormat.V2010, "empty2010", JetFormat.VERSION_14); + addFileFormatDetails(FileFormat.V2016, "empty2016", JetFormat.VERSION_16); addFileFormatDetails(FileFormat.MSISAM, null, JetFormat.VERSION_MSISAM); } @@ -182,7 +183,7 @@ public class DatabaseImpl implements Database private static final int DB_PARENT_ID = 0xF000000; /** the maximum size of any of the included "empty db" resources */ - private static final long MAX_EMPTYDB_SIZE = 350000L; + private static final long MAX_EMPTYDB_SIZE = 370000L; /** this object is a "system" object */ static final int SYSTEM_OBJECT_FLAG = 0x80000000; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java index e7ae7d5..e2b2dd5 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java @@ -1528,6 +1528,7 @@ public class IndexData { case LONG: case MONEY: case COMPLEX_TYPE: + case BIG_INT: return new IntegerColumnDescriptor(col, flags); case FLOAT: case DOUBLE: diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java b/src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java index ba848e4..a230edf 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java @@ -54,10 +54,12 @@ public abstract class JetFormat { private static final byte CODE_VERSION_3 = 0x0; /** Version code for Jet version 4 */ private static final byte CODE_VERSION_4 = 0x1; - /** Version code for Jet version 12 */ + /** Version code for Jet version 12.0 */ private static final byte CODE_VERSION_12 = 0x2; - /** Version code for Jet version 14 */ + /** Version code for Jet version 14.0 */ private static final byte CODE_VERSION_14 = 0x3; + /** Version code for Jet version 16.7 */ + private static final byte CODE_VERSION_16 = 0x5; /** location of the engine name in the header */ static final int OFFSET_ENGINE_NAME = 0x4; @@ -121,6 +123,9 @@ public abstract class JetFormat { private static final Map POSSIBLE_VERSION_14 = Collections.singletonMap((String)null, Database.FileFormat.V2010); + private static final Map POSSIBLE_VERSION_16 = + Collections.singletonMap((String)null, Database.FileFormat.V2016); + private static final Map POSSIBLE_VERSION_MSISAM = Collections.singletonMap((String)null, Database.FileFormat.MSISAM); @@ -138,16 +143,24 @@ public abstract class JetFormat { DataType.SHORT_DATE_TIME, DataType.MONEY, DataType.NUMERIC, DataType.TEXT, DataType.MEMO); + /** calculated types supported in version 16 */ + private static final Set V16_CALC_TYPES = EnumSet.of(DataType.BIG_INT); + static { + V16_CALC_TYPES.addAll(V14_CALC_TYPES); + } + /** the JetFormat constants for the Jet database version "3" */ public static final JetFormat VERSION_3 = new Jet3Format(); /** the JetFormat constants for the Jet database version "4" */ public static final JetFormat VERSION_4 = new Jet4Format(); /** the JetFormat constants for the MSISAM database */ public static final JetFormat VERSION_MSISAM = new MSISAMFormat(); - /** the JetFormat constants for the Jet database version "12" */ + /** the JetFormat constants for the Jet database version "12.0" */ public static final JetFormat VERSION_12 = new Jet12Format(); - /** the JetFormat constants for the Jet database version "14" */ + /** the JetFormat constants for the Jet database version "14.0" */ public static final JetFormat VERSION_14 = new Jet14Format(); + /** the JetFormat constants for the Jet database version "16.7" */ + public static final JetFormat VERSION_16 = new Jet16Format(); //These constants are populated by this class's constructor. They can't be //populated by the subclass's constructor because they are final, and Java @@ -289,6 +302,8 @@ public abstract class JetFormat { return VERSION_12; } else if (version == CODE_VERSION_14) { return VERSION_14; + } else if (version == CODE_VERSION_16) { + return VERSION_16; } throw new IOException("Unsupported " + ((version < CODE_VERSION_3) ? "older" : "newer") + @@ -738,7 +753,8 @@ public abstract class JetFormat { @Override public boolean isSupportedDataType(DataType type) { - return (type != DataType.COMPLEX_TYPE); + return ((type != DataType.COMPLEX_TYPE) && + (type != DataType.BIG_INT)); } @Override @@ -971,7 +987,8 @@ public abstract class JetFormat { @Override public boolean isSupportedDataType(DataType type) { - return (type != DataType.COMPLEX_TYPE); + return ((type != DataType.COMPLEX_TYPE) && + (type != DataType.BIG_INT)); } @Override @@ -1002,7 +1019,6 @@ public abstract class JetFormat { super("VERSION_12"); } - private Jet12Format(String name) { super(name); } @@ -1028,7 +1044,7 @@ public abstract class JetFormat { @Override public boolean isSupportedDataType(DataType type) { - return true; + return (type != DataType.BIG_INT); } @Override @@ -1037,10 +1053,14 @@ public abstract class JetFormat { } } - private static final class Jet14Format extends Jet12Format { - private Jet14Format() { - super("VERSION_14"); - } + private static class Jet14Format extends Jet12Format { + private Jet14Format() { + super("VERSION_14"); + } + + private Jet14Format(String name) { + super(name); + } @Override protected ColumnImpl.SortOrder defineDefaultSortOrder() { @@ -1063,4 +1083,26 @@ public abstract class JetFormat { } } + private static final class Jet16Format extends Jet14Format { + + private Jet16Format() { + super("VERSION_16"); + } + + @Override + public boolean isSupportedDataType(DataType type) { + return true; + } + + @Override + protected Map getPossibleFileFormats() { + return PossibleFileFormats.POSSIBLE_VERSION_16; + } + + @Override + public boolean isSupportedCalculatedDataType(DataType type) { + return V16_CALC_TYPES.contains(type); + } + } + } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java index 619da07..9ec5fca 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/PropertyMapImpl.java @@ -204,6 +204,8 @@ public class PropertyMapImpl implements PropertyMap type = DataType.SHORT_DATE_TIME; } else if(value instanceof byte[]) { type = DataType.OLE; + } else if(value instanceof Long) { + type = DataType.BIG_INT; } else { throw new IllegalArgumentException( "Could not determine type for property " + name + diff --git a/src/main/resources/com/healthmarketscience/jackcess/empty2016.accdb b/src/main/resources/com/healthmarketscience/jackcess/empty2016.accdb new file mode 100644 index 0000000000000000000000000000000000000000..94f4e088222708276dd30d58bc94f2700e1f4825 GIT binary patch literal 364544 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 VCIMGTKmAtqOaE+uIEUl^{Xb$K`Qrcp literal 0 HcmV?d00001 diff --git a/src/site/fml/faq.fml b/src/site/fml/faq.fml index 769936b..18bd093 100644 --- a/src/site/fml/faq.fml +++ b/src/site/fml/faq.fml @@ -16,7 +16,7 @@ What Access formats does it support? -

Jackcess supports Access database versions 2000-2013 read/write and +

Jackcess supports Access database versions 2000-2016 read/write and Access 97 read-only.

diff --git a/src/site/xdoc/cookbook.xml b/src/site/xdoc/cookbook.xml index fca2519..5e083af 100644 --- a/src/site/xdoc/cookbook.xml +++ b/src/site/xdoc/cookbook.xml @@ -195,7 +195,7 @@ As of version 2.1.5, Jackcess supports:

    -
  • Creating databases for Access all versions 2000-2013
  • +
  • Creating databases for Access all versions 2000-2016
  • Creating columns for all simple data types
  • Creating tables with single-table Indexes
  • Creating tables with (index backed) foreign-key constraints diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml index bb8b877..7f23a71 100644 --- a/src/site/xdoc/index.xml +++ b/src/site/xdoc/index.xml @@ -10,7 +10,7 @@

    Jackcess is a pure Java library for reading from and writing to MS - Access databases (currently supporting versions 2000-2013). It is part of the OpenHMS project from Health Market Science, Inc.. It is not an application. There is no GUI. It's a + Access databases (currently supporting versions 2000-2016). It is part of the OpenHMS project from Health Market Science, Inc.. It is not an application. There is no GUI. It's a library, intended for other developers to use to build Java applications. Jackcess is licensed under the Apache License (as of version 2.1.0). diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/BigIntTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/BigIntTest.java new file mode 100644 index 0000000..143ac9b --- /dev/null +++ b/src/test/java/com/healthmarketscience/jackcess/impl/BigIntTest.java @@ -0,0 +1,102 @@ +/* +Copyright (c) 2017 James Ahlborn + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package com.healthmarketscience.jackcess.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import com.healthmarketscience.jackcess.Column; +import com.healthmarketscience.jackcess.ColumnBuilder; +import com.healthmarketscience.jackcess.Cursor; +import com.healthmarketscience.jackcess.CursorBuilder; +import com.healthmarketscience.jackcess.DataType; +import com.healthmarketscience.jackcess.Database; +import com.healthmarketscience.jackcess.IndexBuilder; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.TableBuilder; +import junit.framework.TestCase; +import static com.healthmarketscience.jackcess.TestUtil.*; +import static com.healthmarketscience.jackcess.impl.JetFormatTest.*; + +/** + * + * @author James Ahlborn + */ +public class BigIntTest extends TestCase +{ + + public BigIntTest(String name) throws Exception { + super(name); + } + + public void testBigInt() throws Exception { + + for (final Database.FileFormat fileFormat : SUPPORTED_FILEFORMATS) { + JetFormat format = DatabaseImpl.getFileFormatDetails(fileFormat) + .getFormat(); + + if(!format.isSupportedDataType(DataType.BIG_INT)) { + continue; + } + + Database db = create(fileFormat); + + Table t = new TableBuilder("Test") + .addColumn(new ColumnBuilder("id", DataType.LONG) + .setAutoNumber(true)) + .addColumn(new ColumnBuilder("data1", DataType.TEXT)) + .addColumn(new ColumnBuilder("num1", DataType.BIG_INT)) + .addIndex(new IndexBuilder("idx").addColumns("num1")) + .toTable(db); + + long[] vals = new long[] { + 0L, -10L, 3844L, -45309590834L, 50392084913L, 65000L, -6489273L}; + + List> expectedTable = + new ArrayList>(); + + int idx = 1; + for(long lng : vals) { + t.addRow(Column.AUTO_NUMBER, "" + lng, lng); + + expectedTable.add(createExpectedRow( + "id", idx++, + "data1", "" + lng, + "num1", lng)); + } + + Collections.sort(expectedTable, new Comparator>() { + public int compare( + Map r1, + Map r2) { + Long l1 = (Long)r1.get("num1"); + Long l2 = (Long)r2.get("num1"); + return l1.compareTo(l2); + } + }); + + Cursor c = new CursorBuilder(t).setIndexByName("idx").toIndexCursor(); + + assertCursor(expectedTable, c); + + db.close(); + } + } +} -- 2.39.5