diff --git a/build.gradle b/build.gradle index 523a8c52..6df44c3f 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { } group 'io.tiledb' -version '0.2.0-SNAPSHOT' +version '0.2.1-SNAPSHOT' repositories { jcenter() diff --git a/src/main/java/io/tiledb/java/api/Array.java b/src/main/java/io/tiledb/java/api/Array.java index 150c5a0a..8f8e4f9d 100644 --- a/src/main/java/io/tiledb/java/api/Array.java +++ b/src/main/java/io/tiledb/java/api/Array.java @@ -27,6 +27,7 @@ import static io.tiledb.java.api.QueryType.*; import io.tiledb.libtiledb.*; +import java.math.BigInteger; import java.util.HashMap; /** @@ -73,6 +74,25 @@ public Array(Context ctx, String uri) throws TileDBError { openArray(ctx, uri, TILEDB_READ, EncryptionType.TILEDB_NO_ENCRYPTION, new byte[] {}); } + /** + * Constructs an Array object opening the array for reading at a user-given timestamp + * (time-travelling). + * + *
Example:
+   * {@code
+   *   Context ctx = new Context();
+   *   Array array new Array(ctx, "s3://bucket-name/array-name");
+   * }
+ * + * @param ctx TileDB context + * @param uri The array URI + * @param timestamp The timestamp + * @exception TileDBError A TileDB exception + */ + public Array(Context ctx, String uri, BigInteger timestamp) throws TileDBError { + openArray(ctx, uri, TILEDB_READ, EncryptionType.TILEDB_NO_ENCRYPTION, new byte[] {}, timestamp); + } + /** * Constructs an Array object, opening the array for the given query type. * @@ -119,6 +139,39 @@ public Array( openArray(ctx, uri, query_type, encryption_type, key); } + /** + * Constructs an Array object, opening the encrypted array for the given query type. + * + *
Example:
+   * {@code
+   * Context ctx = new Context();
+   * String key = "0123456789abcdeF0123456789abcdeF";
+   * Array array new Array(ctx, "s3://bucket-name/array-name",
+   *                       TILEDB_READ,
+   *                       TILEDB_AES_256_GCM,
+   *                       key.getBytes(StandardCharsets.UTF_8));
+   * }
+   * 
+ * + * @param ctx TileDB context + * @param uri The array URI + * @param query_type Query type to open the array for + * @param encryption_type The encryption type to use + * @param key The encryption key to use + * @param timestamp The timestamp + * @throws TileDBError A TileDB exception + */ + public Array( + Context ctx, + String uri, + QueryType query_type, + EncryptionType encryption_type, + byte[] key, + BigInteger timestamp) + throws TileDBError { + openArray(ctx, uri, query_type, encryption_type, key, timestamp); + } + private synchronized void openArray( Context ctx, String uri, QueryType query_type, EncryptionType encryption_type, byte[] key) throws TileDBError { @@ -155,6 +208,48 @@ private synchronized void openArray( this.arrayp = _arrayp; } + private synchronized void openArray( + Context ctx, + String uri, + QueryType query_type, + EncryptionType encryption_type, + byte[] key, + BigInteger timestamp) + throws TileDBError { + SWIGTYPE_p_p_tiledb_array_t _arraypp = tiledb.new_tiledb_array_tpp(); + try { + ctx.handleError(tiledb.tiledb_array_alloc(ctx.getCtxp(), uri, _arraypp)); + } catch (TileDBError err) { + tiledb.delete_tiledb_array_tpp(_arraypp); + throw err; + } + SWIGTYPE_p_tiledb_array_t _arrayp = tiledb.tiledb_array_tpp_value(_arraypp); + ArraySchema _schema; + try (NativeArray keyArray = new NativeArray(ctx, key, Byte.class)) { + try { + ctx.handleError( + tiledb.tiledb_array_open_at_with_key( + ctx.getCtxp(), + _arrayp, + query_type.toSwigEnum(), + encryption_type.toSwigEnum(), + keyArray.toVoidPointer(), + keyArray.getSize(), + timestamp)); + } catch (TileDBError err) { + tiledb.delete_tiledb_array_tpp(_arraypp); + throw err; + } + _schema = new ArraySchema(ctx, uri, encryption_type, key); + } + this.ctx = ctx; + this.uri = uri; + this.query_type = query_type; + this.schema = _schema; + this.arraypp = _arraypp; + this.arrayp = _arrayp; + } + private void checkIsOpen() throws TileDBError { if (arrayp == null) { throw new TileDBError("TileDB Array " + uri + " is closed"); diff --git a/src/test/java/io/tiledb/java/api/ArrayTest.java b/src/test/java/io/tiledb/java/api/ArrayTest.java index 7f0dd341..d7e84cb0 100644 --- a/src/test/java/io/tiledb/java/api/ArrayTest.java +++ b/src/test/java/io/tiledb/java/api/ArrayTest.java @@ -1,6 +1,13 @@ package io.tiledb.java.api; +import static io.tiledb.java.api.Layout.TILEDB_ROW_MAJOR; +import static io.tiledb.java.api.QueryType.TILEDB_READ; +import static io.tiledb.java.api.QueryType.TILEDB_WRITE; + +import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.util.Arrays; import org.junit.*; import org.junit.rules.TemporaryFolder; @@ -8,6 +15,7 @@ public class ArrayTest { private Context ctx; private String arrayURI; + private String attributeName; private byte[] key; @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -16,6 +24,7 @@ public class ArrayTest { public void setup() throws Exception { ctx = new Context(); arrayURI = temp.getRoot().toString(); + attributeName = "a1"; String keyString = "0123456789abcdeF0123456789abcdeF"; key = keyString.getBytes(StandardCharsets.US_ASCII); } @@ -31,7 +40,7 @@ public ArraySchema schemaCreate() throws Exception { Domain domain = new Domain(ctx); domain.addDimension(d1); - Attribute a1 = new Attribute(ctx, "a1", Integer.class); + Attribute a1 = new Attribute(ctx, attributeName, Long.class); ArraySchema schema = new ArraySchema(ctx, ArrayType.TILEDB_DENSE); schema.setTileOrder(Layout.TILEDB_ROW_MAJOR); schema.setCellOrder(Layout.TILEDB_ROW_MAJOR); @@ -41,6 +50,59 @@ public ArraySchema schemaCreate() throws Exception { return schema; } + public void insertArbitraryValuesMeth(Array array, NativeArray a_data) throws TileDBError { + // Create query + try (Query query = new Query(array, TILEDB_WRITE)) { + query.setLayout(TILEDB_ROW_MAJOR).setBuffer(attributeName, a_data); + query.submit(); + } + array.close(); + } + + public void insertArbitraryValues(NativeArray a_data) throws TileDBError { + Array array = new Array(ctx, arrayURI, TILEDB_WRITE); + insertArbitraryValuesMeth(array, a_data); + array.close(); + } + + public void insertArbitraryValuesEncrypted(NativeArray a_data) throws TileDBError { + Array array = new Array(ctx, arrayURI, TILEDB_WRITE, EncryptionType.TILEDB_AES_256_GCM, key); + insertArbitraryValuesMeth(array, a_data); + array.close(); + } + + public long[] readArray(Array array) throws TileDBError { + NativeArray sub_array = new NativeArray(ctx, new long[] {1, 4, 1, 2}, Long.class); + // Create query + Query query = new Query(array, TILEDB_READ); + query.setLayout(TILEDB_ROW_MAJOR); + query.setSubarray(sub_array); + query.setBuffer(attributeName, new NativeArray(ctx, 10, Long.class)); + + // Submit query + query.submit(); + + long[] a_buff = (long[]) query.getBuffer(attributeName); + + query.close(); + array.close(); + + return a_buff; + } + + public long[] readArray() throws TileDBError { + return readArray(new Array(ctx, arrayURI)); + } + + public long[] readArrayAt(BigInteger timestamp) throws TileDBError { + return readArray(new Array(ctx, arrayURI, timestamp)); + } + + public long[] readArrayAtEncrypted(BigInteger timestamp) throws TileDBError { + return readArray( + new Array(ctx, arrayURI, TILEDB_READ, EncryptionType.TILEDB_AES_256_GCM, key, timestamp)); + } + @Test public void testArrayExists() throws Exception { // Test that we can create an array @@ -97,4 +159,54 @@ public void testLoadingEncryptedArrayWrongKeyLenErrors() throws Exception { EncryptionType.TILEDB_AES_256_GCM, keyString.getBytes(StandardCharsets.US_ASCII)); } + + @Test + public void testArrayOpenAt() throws Exception { + Array.create(arrayURI, schemaCreate()); + + long[] array_a = new long[] {1, 2, 3, 6}; + insertArbitraryValues(new NativeArray(ctx, array_a, Long.class)); + long ts_a = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + Thread.sleep(1000); + + long[] array_b = new long[] {1, 1, 1, 1}; + insertArbitraryValues(new NativeArray(ctx, array_b, Long.class)); + long ts_b = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + Thread.sleep(1000); + + long[] array_c = new long[] {0, 0, 0, 0}; + insertArbitraryValues(new NativeArray(ctx, array_c, Long.class)); + long ts_c = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + assert Arrays.equals(readArrayAt(BigInteger.valueOf(ts_a)), array_a); + assert Arrays.equals(readArrayAt(BigInteger.valueOf(ts_b)), array_b); + assert Arrays.equals(readArrayAt(BigInteger.valueOf(ts_c)), array_c); + } + + @Test + public void testArrayOpenAtEncrypted() throws Exception { + Array.create(arrayURI, schemaCreate(), EncryptionType.TILEDB_AES_256_GCM, key); + + long[] array_a = new long[] {1, 2, 3, 6}; + insertArbitraryValuesEncrypted(new NativeArray(ctx, array_a, Long.class)); + long ts_a = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + Thread.sleep(1000); + + long[] array_b = new long[] {1, 1, 1, 1}; + insertArbitraryValuesEncrypted(new NativeArray(ctx, array_b, Long.class)); + long ts_b = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + Thread.sleep(1000); + + long[] array_c = new long[] {0, 0, 0, 0}; + insertArbitraryValuesEncrypted(new NativeArray(ctx, array_c, Long.class)); + long ts_c = new Timestamp(System.currentTimeMillis()).toInstant().toEpochMilli(); + + assert Arrays.equals(readArrayAtEncrypted(BigInteger.valueOf(ts_a)), array_a); + assert Arrays.equals(readArrayAtEncrypted(BigInteger.valueOf(ts_b)), array_b); + assert Arrays.equals(readArrayAtEncrypted(BigInteger.valueOf(ts_c)), array_c); + } }