diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Interval.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Interval.java index 68b81490dc..c53524b3eb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Interval.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Interval.java @@ -51,7 +51,7 @@ public abstract class Interval implements Serializable { private static final Pattern INTERVAL_PATTERN = Pattern.compile( - "^P(?!$)(-?\\d+Y)?(-?\\d+M)?(-?\\d+D)?(T(?=-?\\d)(-?\\d+H)?(-?\\d+M)?(-?\\d+(\\.\\d{1,9})?S)?)?$"); + "^P(?!$)(-?\\d+Y)?(-?\\d+M)?(-?\\d+D)?(T(?=-?.?\\d)(-?\\d+H)?(-?\\d+M)?(-?((\\d+(\\.\\d{1,9})?)|(\\.\\d{1,9}))S)?)?$"); /** Returns the months component of the interval. */ public abstract int months(); @@ -154,8 +154,6 @@ public static Interval fromMonthsDaysNanos(int months, int days, BigInteger nano (nanos.subtract(BigInteger.valueOf(micros).multiply(BigInteger.valueOf(NANOS_PER_MICRO)))) .shortValue(); - System.out.println("Micros: " + micros + " Nanos: " + nanoFractions); - return builder() .setMonths(months) .setDays(days) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java index 59229037e1..a6198b7d63 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java @@ -19,7 +19,15 @@ import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.*; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.Interval; +import com.google.cloud.spanner.ProtobufResultSet; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.Struct; +import com.google.cloud.spanner.Type; +import com.google.cloud.spanner.Value; import com.google.common.base.Preconditions; import com.google.protobuf.AbstractMessage; import com.google.protobuf.ProtocolMessageEnum; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index 6b2544c191..cef76b2998 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -1855,7 +1855,7 @@ public void testValueToProto() { .addAllValues( Arrays.asList( com.google.protobuf.Value.newBuilder() - .setStringValue("P1Y2M3DT5H6M2.456787800S") + .setStringValue("P1Y2M3DT5H6M2.4567878S") .build(), com.google.protobuf.Value.newBuilder() .setNullValue(NullValue.NULL_VALUE) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITIntervalTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITIntervalTest.java index 710e9ed483..e7ff052e5b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITIntervalTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITIntervalTest.java @@ -17,7 +17,6 @@ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; -import static com.google.common.base.Strings.isNullOrEmpty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -30,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.junit.AfterClass; @@ -63,32 +63,30 @@ public static List data() { new String[] { "CREATE TABLE IntervalTable (\n" + " key STRING(MAX),\n" - + " slo_days INT64,\n" - + " update_time TIMESTAMP,\n" - + " expiry_days INT64 AS (EXTRACT(DAY FROM make_interval(day => GREATEST(LEAST(slo_days, 365), 1)))),\n" - + " interval_array_len bigint AS (ARRAY_LENGTH(ARRAY[INTERVAL '1-2 3 4:5:6' YEAR TO SECOND]))\n" - + ") PRIMARY KEY (key);" + + " create_time TIMESTAMP,\n" + + " expiry_time TIMESTAMP,\n" + + " expiry_within_month bool AS (expiry_time - create_time < INTERVAL 30 DAY),\n" + + " interval_array_len INT64 AS (ARRAY_LENGTH(ARRAY[INTERVAL '1-2 3 4:5:6' YEAR TO SECOND]))\n" + + ") PRIMARY KEY (key)" }; private static final String[] POSTGRESQL_SCHEMA = new String[] { "CREATE TABLE IntervalTable (\n" + " key text primary key,\n" - + " slo_days bigint,\n" - + " update_time timestamptz,\n" - + " expiry_days bigint GENERATED ALWAYS AS (EXTRACT(DAY FROM make_interval(days =>GREATEST(LEAST(slo_days, 365), 1)))) STORED,\n" + + " create_time timestamptz,\n" + + " expiry_time timestamptz,\n" + + " expiry_within_month bool GENERATED ALWAYS AS (INTERVAL '1' DAY < INTERVAL '30' DAY) STORED,\n" + " interval_array_len bigint GENERATED ALWAYS AS (ARRAY_LENGTH(ARRAY[INTERVAL '1-2 3 4:5:6'], 1)) STORED\n" - + ");" + + ")" }; private static DatabaseClient client; private static boolean isUsingCloudDevel() { - String jobType = System.getenv("JOB_TYPE"); - - // Assumes that the jobType contains the string "cloud-devel" to signal that - // the environment is cloud-devel. - return !isNullOrEmpty(jobType) && jobType.contains("cloud-devel"); + return Objects.equals( + System.getProperty("spanner.gce.config.server_url"), + "https://staging-wrenchworks.sandbox.googleapis.com"); } @BeforeClass @@ -131,7 +129,8 @@ private Timestamp write(Mutation m) { } private Mutation.WriteBuilder baseInsert() { - return Mutation.newInsertOrUpdateBuilder("T").set("Key").to(lastKey = uniqueString()); + return Mutation. + newInsertOrUpdateBuilder("IntervalTable").set("Key").to(lastKey = uniqueString()); } private Struct readRow(String table, String key, String... columns) { @@ -141,23 +140,26 @@ private Struct readRow(String table, String key, String... columns) { } private Struct readLastRow(String... columns) { - return readRow("T", lastKey, columns); + return readRow("IntervalTable", lastKey, columns); } @Test public void writeToTableWithIntervalExpressions() { write( baseInsert() - .set("slo_days") - .to(5) - .set("update_time") - .to(Timestamp.ofTimeMicroseconds(12345678L)) + .set("create_time") + .to(Timestamp.parseTimestamp("2004-11-30T04:53:54Z")) + .set("expiry_time") + .to(Timestamp.parseTimestamp("2004-12-15T04:53:54Z")) .build()); - Struct row = readLastRow("expiryDays", "interval_array_len"); - assertFalse(row.isNull(0)); - assertEquals(5, row.getLong(0)); - assertFalse(row.isNull(1)); - assertEquals(1, row.getLong(1)); + try (ResultSet resultSet = + client + .singleUse() + .executeQuery(Statement.of("SELECT expiry_within_month, interval_array_len FROM IntervalTable WHERE key='"+lastKey+"'"))) { + assertTrue(resultSet.next()); + assertTrue(resultSet.getBoolean(0)); + assertEquals(1, resultSet.getLong(1)); + } } @Test @@ -167,7 +169,39 @@ public void queryInterval() { .singleUse() .executeQuery(Statement.of("SELECT INTERVAL '1' DAY + INTERVAL '1' MONTH AS Col1"))) { assertTrue(resultSet.next()); - assertTrue(resultSet.getInterval(0).equals(Interval.fromMonthsDaysMicros(1, 1, 0))); + assertEquals(resultSet.getInterval(0), Interval.fromMonthsDaysMicros(1, 1, 0)); + } + } + + @Test + public void queryWithIntervalParam() { + write( + baseInsert() + .set("create_time") + .to(Timestamp.parseTimestamp("2004-08-30T04:53:54Z")) + .set("expiry_time") + .to(Timestamp.parseTimestamp("2004-12-15T04:53:54Z")) + .build()); + + String query; + if (dialect.dialect == Dialect.POSTGRESQL) { + query = + "SELECT COUNT(*) FROM IntervalTable WHERE create_time < TIMESTAMPTZ '2004-11-30T10:23:54+0530' - $1"; + } else { + query = + "SELECT COUNT(*) FROM IntervalTable WHERE create_time < TIMESTAMP('2004-11-30T10:23:54+0530') - @p1"; + } + + try (ResultSet resultSet = + client + .singleUse() + .executeQuery( + Statement.newBuilder(query) + .bind("p1") + .to(Value.interval(Interval.ofDays(30))) + .build())) { + assertTrue(resultSet.next()); + assertEquals(resultSet.getLong(0), 1L); } } @@ -203,12 +237,12 @@ public void queryIntervalArray() { "SELECT ARRAY[CAST('P1Y2M3DT4H5M6.789123S' AS INTERVAL), null, CAST('P-1Y-2M-3DT-4H-5M-6.789123S' AS INTERVAL)] AS Col1"; try (ResultSet resultSet = client.singleUse().executeQuery(Statement.of(query))) { assertTrue(resultSet.next()); - assertTrue( + assertEquals( Arrays.asList( - Interval.parseFromString("P1Y2M3DT4H5M6.789123S"), - null, - Interval.parseFromString("P-1Y-2M-3DT-4H-5M-6.789123S")) - .equals(resultSet.getIntervalList(0))); + Interval.parseFromString("P1Y2M3DT4H5M6.789123S"), + null, + Interval.parseFromString("P-1Y-2M-3DT-4H-5M-6.789123S")), + resultSet.getIntervalList(0)); } } }