diff --git a/bigquery/go.mod b/bigquery/go.mod index 69f721bb8e2..5c718485ae4 100644 --- a/bigquery/go.mod +++ b/bigquery/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 // indirect golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/bigquery/go.sum b/bigquery/go.sum index 7e1f23f5f81..472451b51b5 100644 --- a/bigquery/go.sum +++ b/bigquery/go.sum @@ -237,6 +237,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/bigtable/go.mod b/bigtable/go.mod index 7efc68045df..9eb574927c8 100644 --- a/bigtable/go.mod +++ b/bigtable/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/btree v1.0.0 github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/bigtable/go.sum b/bigtable/go.sum index 3e8d55149ab..8b72d19d69e 100644 --- a/bigtable/go.sum +++ b/bigtable/go.sum @@ -242,6 +242,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/datastore/go.mod b/datastore/go.mod index c7012bcdb03..93e3153a4e5 100644 --- a/datastore/go.mod +++ b/datastore/go.mod @@ -7,6 +7,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 // indirect golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/datastore/go.sum b/datastore/go.sum index d59ab9317a0..ed69e8ddcaa 100644 --- a/datastore/go.sum +++ b/datastore/go.sum @@ -237,6 +237,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/firestore/go.mod b/firestore/go.mod index c1a53cf4a41..a3acf0d8206 100644 --- a/firestore/go.mod +++ b/firestore/go.mod @@ -7,6 +7,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 // indirect golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/firestore/go.sum b/firestore/go.sum index 2c09d059b6e..32fd17f1419 100644 --- a/firestore/go.sum +++ b/firestore/go.sum @@ -238,6 +238,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/go.mod b/go.mod index fba0ff5d366..62b01cd80aa 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/jstemmer/go-junit-report v0.9.1 go.opencensus.io v0.22.5 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 - golang.org/x/mod v0.4.0 // indirect + golang.org/x/mod v0.4.1 // indirect golang.org/x/net v0.0.0-20201224014010-6772e930b67b golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 golang.org/x/text v0.3.5 diff --git a/go.sum b/go.sum index 1b1c1bb0171..63df7122593 100644 --- a/go.sum +++ b/go.sum @@ -239,8 +239,8 @@ golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/internal/godocfx/go.sum b/internal/godocfx/go.sum index bc29fb60bd3..1a52742f32a 100644 --- a/internal/godocfx/go.sum +++ b/internal/godocfx/go.sum @@ -138,8 +138,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/logging/go.mod b/logging/go.mod index 288754c0c09..affb70e91b9 100644 --- a/logging/go.mod +++ b/logging/go.mod @@ -9,6 +9,7 @@ require ( github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 go.opencensus.io v0.22.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/logging/go.sum b/logging/go.sum index 30a26233d7e..91035ac2903 100644 --- a/logging/go.sum +++ b/logging/go.sum @@ -242,6 +242,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pubsub/go.mod b/pubsub/go.mod index bb170926a7d..dceada5485a 100644 --- a/pubsub/go.mod +++ b/pubsub/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 go.opencensus.io v0.22.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 diff --git a/pubsub/go.sum b/pubsub/go.sum index 794bca0d317..bb81fc163ff 100644 --- a/pubsub/go.sum +++ b/pubsub/go.sum @@ -201,6 +201,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pubsublite/go.mod b/pubsublite/go.mod index e5c755dc8f9..53d414c2b8d 100644 --- a/pubsublite/go.mod +++ b/pubsublite/go.mod @@ -9,6 +9,7 @@ require ( github.com/google/go-cmp v0.5.4 github.com/google/uuid v1.1.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 // indirect golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect diff --git a/pubsublite/go.sum b/pubsublite/go.sum index 873c2c9ea8f..028ae684677 100644 --- a/pubsublite/go.sum +++ b/pubsublite/go.sum @@ -188,6 +188,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/spanner/go.mod b/spanner/go.mod index 32c58ab7759..cda14ed5db6 100644 --- a/spanner/go.mod +++ b/spanner/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 go.opencensus.io v0.22.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 // indirect golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 diff --git a/spanner/go.sum b/spanner/go.sum index 39316f6f4ff..b3509a48a75 100644 --- a/spanner/go.sum +++ b/spanner/go.sum @@ -237,6 +237,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/spanner/spannertest/README.md b/spanner/spannertest/README.md index bee71528e70..14caba869b5 100644 --- a/spanner/spannertest/README.md +++ b/spanner/spannertest/README.md @@ -22,7 +22,7 @@ by ascending esotericism: - more aggregation functions - SELECT HAVING - case insensitivity -- alternate literal types (esp. strings) +- more literal types - generated columns - expression type casting, coercion - multiple joins diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index ed93afb4d4e..cbd2ae003aa 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -47,7 +47,10 @@ import ( "os" "strconv" "strings" + "time" "unicode/utf8" + + "cloud.google.com/go/civil" ) const debug = false @@ -780,7 +783,7 @@ func (p *parser) advance() { p.cur.err = nil p.cur.line, p.cur.offset = p.line, p.offset p.cur.typ = unknownToken - // TODO: struct, date, timestamp literals + // TODO: struct literals switch p.s[0] { case ',', ';', '(', ')', '{', '}', '[', ']', '*', '+', '-': // Single character symbol. @@ -2601,13 +2604,20 @@ func (p *parser) parseLit() (Expr, *parseError) { // case insensitivity for keywords. } - // Handle array literals. - if tok.value == "ARRAY" || tok.value == "[" { + // Handle typed literals. + switch tok.value { + case "ARRAY", "[": p.back() return p.parseArrayLit() + case "DATE": + p.back() + return p.parseDateLit() + case "TIMESTAMP": + p.back() + return p.parseTimestampLit() } - // TODO: more types of literals (struct, date, timestamp). + // TODO: struct literals // Try a parameter. // TODO: check character sets. @@ -2645,6 +2655,76 @@ func (p *parser) parseArrayLit() (Array, *parseError) { return arr, err } +// TODO: There should be exported Parse{Date,Timestamp}Literal package-level funcs +// to support spannertest coercing plain string literals when used in a typed context. +// Those should wrap parseDateLit and parseTimestampLit below. + +func (p *parser) parseDateLit() (DateLiteral, *parseError) { + if err := p.expect("DATE"); err != nil { + return DateLiteral{}, err + } + s, err := p.parseStringLit() + if err != nil { + return DateLiteral{}, err + } + d, perr := civil.ParseDate(string(s)) + if perr != nil { + return DateLiteral{}, p.errorf("bad date literal %q: %v", s, perr) + } + // TODO: Enforce valid range. + return DateLiteral(d), nil +} + +// TODO: A manual parser is probably better than this. +// There are a lot of variations that this does not handle. +var timestampFormats = []string{ + // 'YYYY-[M]M-[D]D [[H]H:[M]M:[S]S[.DDDDDD] [timezone]]' + "2006-01-02", + "2006-01-02 15:04:05", + "2006-01-02 15:04:05.000000", + "2006-01-02 15:04:05 -07:00", + "2006-01-02 15:04:05.000000 -07:00", +} + +var defaultLocation = func() *time.Location { + // The docs say "America/Los_Angeles" is the default. + // Use that if we can load it, but fall back on UTC if we don't have timezone data. + loc, err := time.LoadLocation("America/Los_Angeles") + if err == nil { + return loc + } + return time.UTC +}() + +func (p *parser) parseTimestampLit() (TimestampLiteral, *parseError) { + if err := p.expect("TIMESTAMP"); err != nil { + return TimestampLiteral{}, err + } + s, err := p.parseStringLit() + if err != nil { + return TimestampLiteral{}, err + } + for _, format := range timestampFormats { + t, err := time.ParseInLocation(format, string(s), defaultLocation) + if err == nil { + // TODO: Enforce valid range. + return TimestampLiteral(t), nil + } + } + return TimestampLiteral{}, p.errorf("invalid timestamp literal %q", s) +} + +func (p *parser) parseStringLit() (StringLiteral, *parseError) { + tok := p.next() + if tok.err != nil { + return "", tok.err + } + if tok.typ != stringToken { + return "", p.errorf("got %q, want string literal", tok.value) + } + return StringLiteral(tok.string), nil +} + func (p *parser) parsePathExp() (PathExp, *parseError) { var pe PathExp for { diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go index ae5577d5844..f4c425c175f 100644 --- a/spanner/spansql/parser_test.go +++ b/spanner/spansql/parser_test.go @@ -21,6 +21,9 @@ import ( "math" "reflect" "testing" + "time" + + "cloud.google.com/go/civil" ) func TestParseQuery(t *testing.T) { @@ -302,6 +305,9 @@ func TestParseExpr(t *testing.T) { {`RB"""\\//\\//"""`, BytesLiteral("\\\\//\\\\//")}, {"RB'''\\\\//\n\\\\//'''", BytesLiteral("\\\\//\n\\\\//")}, + // Date and timestamp literals: + {`DATE '2014-09-27'`, DateLiteral(civil.Date{Year: 2014, Month: time.September, Day: 27})}, + // Array literals: // https://cloud.google.com/spanner/docs/lexical#array_literals {`[1, 2, 3]`, Array{IntegerLiteral(1), IntegerLiteral(2), IntegerLiteral(3)}}, diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index 99160c83d91..99f7b7d1335 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -27,6 +27,7 @@ import ( "fmt" "strconv" "strings" + "time" ) func buildSQL(x interface{ addSQL(*strings.Builder) }) string { @@ -585,3 +586,13 @@ func (sl StringLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "%q", sl) func (bl BytesLiteral) SQL() string { return buildSQL(bl) } func (bl BytesLiteral) addSQL(sb *strings.Builder) { fmt.Fprintf(sb, "B%q", bl) } + +func (dl DateLiteral) SQL() string { return buildSQL(dl) } +func (dl DateLiteral) addSQL(sb *strings.Builder) { + fmt.Fprintf(sb, "DATE '%04d-%02d-%02d'", dl.Year, dl.Month, dl.Day) +} + +func (tl TimestampLiteral) SQL() string { return buildSQL(tl) } +func (tl TimestampLiteral) addSQL(sb *strings.Builder) { + fmt.Fprintf(sb, "TIMESTAMP '%s'", time.Time(tl).Format("2006-01-02 15:04:05.000000 -07:00")) +} diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go index d50089d63e1..1f9db7db597 100644 --- a/spanner/spansql/sql_test.go +++ b/spanner/spansql/sql_test.go @@ -19,6 +19,9 @@ package spansql import ( "reflect" "testing" + "time" + + "cloud.google.com/go/civil" ) func boolAddr(b bool) *bool { @@ -53,6 +56,11 @@ func TestSQL(t *testing.T) { return e, nil } + latz, err := time.LoadLocation("America/Los_Angeles") + if err != nil { + t.Fatalf("Loading Los Angeles time zone info: %v", err) + } + line := func(n int) Position { return Position{Line: n} } tests := []struct { data interface{ SQL() string } @@ -300,6 +308,16 @@ func TestSQL(t *testing.T) { "SELECT `Desc`", reparseQuery, }, + { + DateLiteral(civil.Date{Year: 2014, Month: time.September, Day: 27}), + `DATE '2014-09-27'`, + reparseExpr, + }, + { + TimestampLiteral(time.Date(2014, time.September, 27, 12, 34, 56, 123456e3, latz)), + `TIMESTAMP '2014-09-27 12:34:56.123456 -07:00'`, + reparseExpr, + }, } for _, test := range tests { sql := test.data.SQL() diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index 940df82e4b6..70ea1889ef4 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -23,6 +23,9 @@ import ( "math" "sort" "strings" + "time" + + "cloud.google.com/go/civil" ) // TODO: More Position fields throughout; maybe in Query/Select. @@ -630,6 +633,18 @@ type BytesLiteral string func (BytesLiteral) isExpr() {} +// DateLiteral represents a date literal. +// https://cloud.google.com/spanner/docs/lexical#date_literals +type DateLiteral civil.Date + +func (DateLiteral) isExpr() {} + +// TimestampLiteral represents a timestamp literal. +// https://cloud.google.com/spanner/docs/lexical#timestamp_literals +type TimestampLiteral time.Time + +func (TimestampLiteral) isExpr() {} + type StarExpr int // Star represents a "*" in an expression. diff --git a/storage/go.mod b/storage/go.mod index 7484e7ccf4d..e06f1a0d71d 100644 --- a/storage/go.mod +++ b/storage/go.mod @@ -7,6 +7,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 github.com/googleapis/gax-go/v2 v2.0.5 + golang.org/x/mod v0.4.1 // indirect golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect google.golang.org/api v0.36.0 diff --git a/storage/go.sum b/storage/go.sum index af7e319d143..d62cdfca3cb 100644 --- a/storage/go.sum +++ b/storage/go.sum @@ -231,6 +231,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=