diff --git a/.github/workflows/release-submodule.yaml b/.github/workflows/release-submodule.yaml index 84ab2fc49a1c..af0ae78c32b2 100644 --- a/.github/workflows/release-submodule.yaml +++ b/.github/workflows/release-submodule.yaml @@ -75,9 +75,9 @@ jobs: package-name: ${{ matrix.package }} monorepo-tags: true command: release-pr - - id: label # Adds the "autorelease: pending" label. - if: ${{steps.release-please.outputs.pr}}) - uses: actions/github-script@v3 + - uses: actions/github-script@v3 + id: label # Adds the "autorelease: pending" label. + if: ${{steps.release-please.outputs.pr}} with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 34a176da17c6..d8489a725061 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,6 +1,7 @@ # This workflow creates release PRs and tags the top level release for # the top level repository: -push: +on: + push: branches: - master name: release-please @@ -17,9 +18,9 @@ jobs: fork: true package-name: google-cloud-go command: release-pr - - id: label # Add the magic "autorelease: pending" label. + - uses: actions/github-script@v3 + id: label # Add the magic "autorelease: pending" label. if: ${{ steps.release-pr.outputs.pr }} - uses: actions/github-script@v3 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/README.md b/README.md index fdf6f73c1c38..1ffe6700b5c5 100644 --- a/README.md +++ b/README.md @@ -175,3 +175,11 @@ for more information. [cloud-video]: https://cloud.google.com/video-intelligence/ [cloud-vision]: https://cloud.google.com/vision [cloud-webrisk]: https://cloud.google.com/web-risk/ + +## Links + +- [Go on Google Cloud](https://cloud.google.com/go/home) +- [Getting started with Go on Google Cloud](https://cloud.google.com/go/getting-started) +- [App Engine Quickstart](https://cloud.google.com/appengine/docs/standard/go/quickstart) +- [Cloud Functions Quickstart](https://cloud.google.com/functions/docs/quickstart-go) +- [Cloud Run Quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy#go) diff --git a/bigquery/integration_test.go b/bigquery/integration_test.go index e450e207ec5d..94d98f1401db 100644 --- a/bigquery/integration_test.go +++ b/bigquery/integration_test.go @@ -1630,6 +1630,63 @@ func TestIntegration_TableUpdate(t *testing.T) { } } +func TestIntegration_QueryStatistics(t *testing.T) { + // Make a bunch of assertions on a simple query. + if client == nil { + t.Skip("Integration tests skipped") + } + ctx := context.Background() + + q := client.Query("SELECT 17 as foo, 3.14 as bar") + // disable cache to ensure we have query statistics + q.DisableQueryCache = true + + job, err := q.Run(ctx) + if err != nil { + t.Fatalf("job Run failure: %v", err) + } + status, err := job.Wait(ctx) + if err != nil { + t.Fatalf("job Wait failure: %v", err) + } + if status.Statistics == nil { + t.Fatal("expected job statistics, none found") + } + + if status.Statistics.NumChildJobs != 0 { + t.Errorf("expected no children, %d reported", status.Statistics.NumChildJobs) + } + + if status.Statistics.ParentJobID != "" { + t.Errorf("expected no parent, but parent present: %s", status.Statistics.ParentJobID) + } + + if status.Statistics.Details == nil { + t.Fatal("expected job details, none present") + } + + qStats, ok := status.Statistics.Details.(*QueryStatistics) + if !ok { + t.Fatalf("expected query statistics not present") + } + + if qStats.CacheHit { + t.Error("unexpected cache hit") + } + + if qStats.StatementType != "SELECT" { + t.Errorf("expected SELECT statement type, got: %s", qStats.StatementType) + } + + if len(qStats.QueryPlan) == 0 { + t.Error("expected query plan, none present") + } + + if len(qStats.Timeline) == 0 { + t.Error("expected query timeline, none present") + } +} + func TestIntegration_Load(t *testing.T) { if client == nil { t.Skip("Integration tests skipped") diff --git a/bigquery/job.go b/bigquery/job.go index 6e0745d8d558..1e08e899c519 100644 --- a/bigquery/job.go +++ b/bigquery/job.go @@ -347,6 +347,9 @@ type JobStatistics struct { // ScriptStatistics includes information run as part of a child job within // a script. ScriptStatistics *ScriptStatistics + + // ReservationUsage attributes slot consumption to reservations. + ReservationUsage []*ReservationUsage } // Statistics is one of ExtractStatistics, LoadStatistics or QueryStatistics. @@ -561,6 +564,25 @@ type QueryTimelineSample struct { SlotMillis int64 } +// ReservationUsage contains information about a job's usage of a single reservation. +type ReservationUsage struct { + // SlotMillis reports the slot milliseconds utilized within in the given reservation. + SlotMillis int64 + // Name indicates the utilized reservation name, or "unreserved" for ondemand usage. + Name string +} + +func bqToReservationUsage(ru []*bq.JobStatisticsReservationUsage) []*ReservationUsage { + var usage []*ReservationUsage + for _, in := range ru { + usage = append(usage, &ReservationUsage{ + SlotMillis: in.SlotMs, + Name: in.Name, + }) + } + return usage +} + // ScriptStatistics report information about script-based query jobs. type ScriptStatistics struct { EvaluationKind string @@ -810,6 +832,7 @@ func (j *Job) setStatistics(s *bq.JobStatistics, c *Client) { NumChildJobs: s.NumChildJobs, ParentJobID: s.ParentJobId, ScriptStatistics: bqToScriptStatistics(s.ScriptStatistics), + ReservationUsage: bqToReservationUsage(s.ReservationUsage), } switch { case s.Extract != nil: diff --git a/spanner/spannertest/README.md b/spanner/spannertest/README.md index 5f4317e4cd92..bee71528e70f 100644 --- a/spanner/spannertest/README.md +++ b/spanner/spannertest/README.md @@ -18,6 +18,7 @@ Here's a list of features that are missing or incomplete. It is roughly ordered by ascending esotericism: - expression functions +- NUMERIC - more aggregation functions - SELECT HAVING - case insensitivity diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index ce9badd8483e..9677c03bf282 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -1650,6 +1650,7 @@ var baseTypes = map[string]TypeBase{ "BOOL": Bool, "INT64": Int64, "FLOAT64": Float64, + "NUMERIC": Numeric, "STRING": String, "BYTES": Bytes, "DATE": Date, @@ -1664,7 +1665,7 @@ func (p *parser) parseType() (Type, *parseError) { ARRAY< scalar_type > scalar_type: - { BOOL | INT64 | FLOAT64 | STRING( length ) | BYTES( length ) | DATE | TIMESTAMP } + { BOOL | INT64 | FLOAT64 | NUMERIC | STRING( length ) | BYTES( length ) | DATE | TIMESTAMP } length: { int64_value | MAX } */ diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index d5457de8676a..2c7cdcdf9f38 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -234,6 +234,8 @@ func (tb TypeBase) SQL() string { return "INT64" case Float64: return "FLOAT64" + case Numeric: + return "NUMERIC" case String: return "STRING" case Bytes: diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index 5854e0e0d64d..1c2831a07298 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -293,7 +293,7 @@ func (Check) isConstraint() {} // Type represents a column type. type Type struct { Array bool - Base TypeBase // Bool, Int64, Float64, String, Bytes, Date, Timestamp + Base TypeBase // Bool, Int64, Float64, Numeric, String, Bytes, Date, Timestamp Len int64 // if Base is String or Bytes; may be MaxLen } @@ -306,6 +306,7 @@ const ( Bool TypeBase = iota Int64 Float64 + Numeric String Bytes Date