diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b2f03c8e..a6d7ff3f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -20,3 +20,49 @@ jobs: uses: actions/checkout@v4 - name: Run unit tests run: go test -race -short + + lint: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - name: Checkout code + uses: actions/checkout@v4 + + - name: vet . + run: go vet ./... + + - name: vet ./examples + working-directory: ./examples + run: go vet ./... + + - name: vet ./benchmarks + working-directory: ./benchmarks + run: go vet ./... + + - name: gofmt + run: | + OUT=$(gofmt -s -d .) + if [ -n "$OUT" ]; then + echo -e "$OUT" + echo "Code unformatted, please run 'gofmt -w -s .'" + exit 1 + fi + + - name: staticcheck . + uses: dominikh/staticcheck-action@v1.3.1 + with: + version: "latest" + install-go: false + checks: "inherit,-U1000" + working-directory: "." + + - name: staticcheck ./examples + uses: dominikh/staticcheck-action@v1.3.1 + with: + version: "latest" + install-go: false + checks: "inherit,-U1000" + working-directory: "./examples" diff --git a/checksum_row_iterator_test.go b/checksum_row_iterator_test.go index 12281087..cf31f36d 100644 --- a/checksum_row_iterator_test.go +++ b/checksum_row_iterator_test.go @@ -196,6 +196,9 @@ func TestUpdateChecksumForNullValues(t *testing.T) { enc2 := gob.NewEncoder(buffer2) initial2 := new([32]byte) checksum2, err := updateChecksum(enc2, buffer2, initial2, row) + if err != nil { + t.Fatalf("failed to update checksum: %v", err) + } if *checksum != *checksum2 { t.Fatalf("recalculated checksum does not match the initial calculation") } diff --git a/client_side_statement.go b/client_side_statement.go index 38dfbca2..c8134858 100644 --- a/client_side_statement.go +++ b/client_side_statement.go @@ -25,7 +25,7 @@ import ( "time" "cloud.google.com/go/spanner" - sppb "google.golang.org/genproto/googleapis/spanner/v1" + "cloud.google.com/go/spanner/apiv1/spannerpb" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -232,33 +232,33 @@ func matchesToMap(re *regexp.Regexp, s string) map[string]string { // one row. This is used for client side statements that return a result set // containing a BOOL value. func createBooleanIterator(column string, value bool) (*clientSideIterator, error) { - return createSingleValueIterator(column, value, sppb.TypeCode_BOOL) + return createSingleValueIterator(column, value, spannerpb.TypeCode_BOOL) } // createStringIterator creates a row iterator with a single STRING column with // one row. This is used for client side statements that return a result set // containing a STRING value. func createStringIterator(column string, value string) (*clientSideIterator, error) { - return createSingleValueIterator(column, value, sppb.TypeCode_STRING) + return createSingleValueIterator(column, value, spannerpb.TypeCode_STRING) } // createTimestampIterator creates a row iterator with a single TIMESTAMP column with // one row. This is used for client side statements that return a result set // containing a TIMESTAMP value. func createTimestampIterator(column string, value *time.Time) (*clientSideIterator, error) { - return createSingleValueIterator(column, value, sppb.TypeCode_TIMESTAMP) + return createSingleValueIterator(column, value, spannerpb.TypeCode_TIMESTAMP) } -func createSingleValueIterator(column string, value interface{}, code sppb.TypeCode) (*clientSideIterator, error) { +func createSingleValueIterator(column string, value interface{}, code spannerpb.TypeCode) (*clientSideIterator, error) { row, err := spanner.NewRow([]string{column}, []interface{}{value}) if err != nil { return nil, err } return &clientSideIterator{ - metadata: &sppb.ResultSetMetadata{ - RowType: &sppb.StructType{ - Fields: []*sppb.StructType_Field{ - {Name: column, Type: &sppb.Type{Code: code}}, + metadata: &spannerpb.ResultSetMetadata{ + RowType: &spannerpb.StructType{ + Fields: []*spannerpb.StructType_Field{ + {Name: column, Type: &spannerpb.Type{Code: code}}, }, }, }, @@ -270,7 +270,7 @@ func createSingleValueIterator(column string, value interface{}, code sppb.TypeC // statements. All values are created and kept in memory, and this struct // should only be used for small result sets. type clientSideIterator struct { - metadata *sppb.ResultSetMetadata + metadata *spannerpb.ResultSetMetadata rows []*spanner.Row index int stopped bool @@ -291,6 +291,6 @@ func (t *clientSideIterator) Stop() { t.metadata = nil } -func (t *clientSideIterator) Metadata() *sppb.ResultSetMetadata { +func (t *clientSideIterator) Metadata() *spannerpb.ResultSetMetadata { return t.metadata } diff --git a/driver.go b/driver.go index 147b78b4..d1a11dcf 100644 --- a/driver.go +++ b/driver.go @@ -238,7 +238,7 @@ func newConnector(d *Driver, dsn string) (*connector, error) { } if strval, ok := connectorConfig.params["numchannels"]; ok { if val, err := strconv.Atoi(strval); err == nil && val > 0 { - config.NumChannels = val + opts = append(opts, option.WithGRPCConnectionPool(val)) } } if strval, ok := connectorConfig.params["rpcpriority"]; ok { diff --git a/driver_test.go b/driver_test.go index a5a6f6a5..5837322c 100644 --- a/driver_test.go +++ b/driver_test.go @@ -183,12 +183,11 @@ func TestExtractDnsParts(t *testing.T) { WriteSessions: 0.2, HealthCheckInterval: spanner.DefaultSessionPoolConfig.HealthCheckInterval, HealthCheckWorkers: spanner.DefaultSessionPoolConfig.HealthCheckWorkers, - MaxBurst: spanner.DefaultSessionPoolConfig.MaxBurst, + MaxBurst: spanner.DefaultSessionPoolConfig.MaxBurst, //lint:ignore SA1019 because it's a spanner default. MaxIdle: spanner.DefaultSessionPoolConfig.MaxIdle, TrackSessionHandles: spanner.DefaultSessionPoolConfig.TrackSessionHandles, InactiveTransactionRemovalOptions: spanner.DefaultSessionPoolConfig.InactiveTransactionRemovalOptions, }, - NumChannels: 10, UserAgent: userAgent, DisableRouteToLeader: true, EnableEndToEndTracing: true, @@ -216,15 +215,15 @@ func TestExtractDnsParts(t *testing.T) { if tc.wantErr { t.Error("did not encounter expected error") } - if !cmp.Equal(config, tc.wantConnectorConfig, cmp.AllowUnexported(connectorConfig{})) { - t.Errorf("connector config mismatch for %q\ngot: %v\nwant %v", tc.input, config, tc.wantConnectorConfig) + if diff := cmp.Diff(config, tc.wantConnectorConfig, cmp.AllowUnexported(connectorConfig{})); diff != "" { + t.Errorf("connector config mismatch for %q\n%v", tc.input, diff) } conn, err := newConnector(&Driver{connectors: make(map[string]*connector)}, tc.input) if err != nil { t.Errorf("failed to get connector for %q: %v", tc.input, err) } - if !cmp.Equal(conn.spannerClientConfig, tc.wantSpannerConfig, cmpopts.IgnoreUnexported(spanner.ClientConfig{}, spanner.SessionPoolConfig{}, spanner.InactiveTransactionRemovalOptions{}, spannerpb.ExecuteSqlRequest_QueryOptions{})) { - t.Errorf("connector Spanner client config mismatch for %q\n Got: %v\nWant: %v", tc.input, conn.spannerClientConfig, tc.wantSpannerConfig) + if diff := cmp.Diff(conn.spannerClientConfig, tc.wantSpannerConfig, cmpopts.IgnoreUnexported(spanner.ClientConfig{}, spanner.SessionPoolConfig{}, spanner.InactiveTransactionRemovalOptions{}, spannerpb.ExecuteSqlRequest_QueryOptions{})); diff != "" { + t.Errorf("connector Spanner client config mismatch for %q\n%v", tc.input, diff) } } }) diff --git a/driver_with_mockserver_test.go b/driver_with_mockserver_test.go index 9b234ddd..c03b27cc 100644 --- a/driver_with_mockserver_test.go +++ b/driver_with_mockserver_test.go @@ -1502,6 +1502,9 @@ func TestDdlBatch(t *testing.T) { statements := []string{"CREATE TABLE FOO", "CREATE TABLE BAR"} conn, err := db.Conn(ctx) + if err != nil { + t.Fatal(err) + } defer conn.Close() if _, err = conn.ExecContext(ctx, "START BATCH DDL"); err != nil { @@ -1543,6 +1546,9 @@ func TestAbortDdlBatch(t *testing.T) { statements := []string{"CREATE TABLE FOO", "CREATE TABLE BAR"} c, err := db.Conn(ctx) + if err != nil { + t.Fatal(err) + } defer c.Close() if _, err = c.ExecContext(ctx, "START BATCH DDL"); err != nil { @@ -1912,7 +1918,11 @@ func TestCommitTimestamp(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -1947,7 +1957,11 @@ func TestCommitTimestampAutocommit(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -1978,7 +1992,11 @@ func TestCommitTimestampFailsAfterRollback(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2007,7 +2025,11 @@ func TestCommitTimestampFailsAfterAutocommitQuery(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2034,7 +2056,11 @@ func TestShowVariableCommitTimestamp(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2199,7 +2225,8 @@ func TestStressClientReuse(t *testing.T) { _, server, teardown := setupTestDBConnection(t) defer teardown() - rand.Seed(time.Now().UnixNano()) + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + numSessions := 10 numClients := 5 numParallel := 50 @@ -2215,6 +2242,8 @@ func TestStressClientReuse(t *testing.T) { } // Execute random operations in parallel on the database. for i := 0; i < numParallel; i++ { + doUpdate := rng.Int()%2 == 0 + wg.Add(1) go func() { defer wg.Done() @@ -2222,7 +2251,7 @@ func TestStressClientReuse(t *testing.T) { if err != nil { t.Errorf("failed to get a connection: %v", err) } - if rand.Int()%2 == 0 { + if doUpdate { if _, err := conn.ExecContext(ctx, testutil.UpdateBarSetFoo); err != nil { t.Errorf("failed to execute update on connection: %v", err) } @@ -2262,7 +2291,11 @@ func TestExcludeTxnFromChangeStreams_AutoCommitUpdate(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2306,7 +2339,11 @@ func TestExcludeTxnFromChangeStreams_AutoCommitBatchDml(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2341,7 +2378,11 @@ func TestExcludeTxnFromChangeStreams_PartitionedDml(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } @@ -2368,7 +2409,11 @@ func TestExcludeTxnFromChangeStreams_Transaction(t *testing.T) { defer teardown() conn, err := db.Conn(ctx) - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + t.Fatal(err) + } + }() if err != nil { t.Fatalf("failed to get a connection: %v", err) } diff --git a/examples/commit-timestamp/main.go b/examples/commit-timestamp/main.go index 8ad3ece9..1608acef 100644 --- a/examples/commit-timestamp/main.go +++ b/examples/commit-timestamp/main.go @@ -20,7 +20,6 @@ import ( "fmt" "time" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) diff --git a/examples/data-types/main.go b/examples/data-types/main.go index 3b707db0..3972f006 100644 --- a/examples/data-types/main.go +++ b/examples/data-types/main.go @@ -59,7 +59,7 @@ func dataTypes(projectId, instanceId, databaseId string) error { ctx := context.Background() db, err := sql.Open("spanner", fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId)) if err != nil { - return fmt.Errorf("failed to open database connection: %v\n", err) + return fmt.Errorf("failed to open database connection: %v", err) } defer db.Close() @@ -75,7 +75,7 @@ func dataTypes(projectId, instanceId, databaseId string) error { 1, true, "string", []byte("bytes"), 100, float32(3.14), 3.14, *big.NewRat(1, 1), civil.DateOf(time.Now()), time.Now(), []bool{true, false}, []string{"s1", "s2"}, [][]byte{[]byte("b1"), []byte("b2")}, []int64{1, 2}, []float32{1.1, 2.2}, []float64{1.1, 2.2}, []big.Rat{*big.NewRat(1, 2), *big.NewRat(1, 3)}, - []civil.Date{{2021, 10, 12}, {2021, 10, 13}}, + []civil.Date{{Year: 2021, Month: 10, Day: 12}, {Year: 2021, Month: 10, Day: 13}}, []time.Time{time.Now(), time.Now().Add(24 * time.Hour)}); err != nil { return fmt.Errorf("failed to insert a record with all non-null values using DML: %v", err) } diff --git a/examples/ddl-batches/main.go b/examples/ddl-batches/main.go index f2d1ce3e..7c5102b8 100644 --- a/examples/ddl-batches/main.go +++ b/examples/ddl-batches/main.go @@ -19,7 +19,6 @@ import ( "database/sql" "fmt" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) @@ -40,7 +39,7 @@ func ddlBatches(projectId, instanceId, databaseId string) error { ctx := context.Background() db, err := sql.Open("spanner", fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId)) if err != nil { - return fmt.Errorf("failed to open database connection: %v\n", err) + return fmt.Errorf("failed to open database connection: %v", err) } defer db.Close() diff --git a/examples/dml-batches/main.go b/examples/dml-batches/main.go index 73a0a41e..026144d3 100644 --- a/examples/dml-batches/main.go +++ b/examples/dml-batches/main.go @@ -19,7 +19,6 @@ import ( "database/sql" "fmt" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) @@ -59,9 +58,15 @@ func dmlBatch(projectId, instanceId, databaseId string) error { } // Insert a number of DML statements on the transaction. These statements will be buffered locally in the // transaction and will only be sent to Spanner once RUN BATCH is executed. - _, err = tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 1, "Singer 1") - _, err = tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 2, "Singer 2") - _, err = tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 3, "Singer 3") + if _, err := tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 1, "Singer 1"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } + if _, err := tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 2, "Singer 2"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } + if _, err := tx.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 3, "Singer 3"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } // Run the active DML batch. if _, err := tx.ExecContext(ctx, "RUN BATCH"); err != nil { return fmt.Errorf("failed to execute RUN BATCH: %v", err) @@ -90,9 +95,15 @@ func dmlBatch(projectId, instanceId, databaseId string) error { return fmt.Errorf("failed to start DML batch: %v", err) } // Note that we execute the DML statements on the connection that started the DML batch. - _, err = conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 4, "Singer 4") - _, err = conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 5, "Singer 5") - _, err = conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 6, "Singer 6") + if _, err := conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 4, "Singer 4"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } + if _, err := conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 5, "Singer 5"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } + if _, err := conn.ExecContext(ctx, "INSERT INTO Singers (SingerId, Name) VALUES (@id, @name)", 6, "Singer 6"); err != nil { + return fmt.Errorf("failed to insert: %v", err) + } // Run the batch. This will apply all the batched DML statements to the database in one atomic operation. if err := conn.Raw(func(driverConn interface{}) error { return driverConn.(spannerdriver.SpannerConn).RunBatch(ctx) diff --git a/examples/emulator_runner.go b/examples/emulator_runner.go index 71cf6643..ecc13564 100644 --- a/examples/emulator_runner.go +++ b/examples/emulator_runner.go @@ -100,7 +100,7 @@ func startEmulator() error { return err } containerId = resp.ID - if err := cli.ContainerStart(ctx, containerId, types.ContainerStartOptions{}); err != nil { + if err := cli.ContainerStart(ctx, containerId, container.StartOptions{}); err != nil { return err } // Wait max 10 seconds or until the emulator is running. diff --git a/examples/helloworld/main.go b/examples/helloworld/main.go index e258a2dc..ddde8139 100644 --- a/examples/helloworld/main.go +++ b/examples/helloworld/main.go @@ -30,7 +30,7 @@ func helloWorld(projectId, instanceId, databaseId string) error { ctx := context.Background() db, err := sql.Open("spanner", fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId)) if err != nil { - return fmt.Errorf("failed to open database connection: %v\n", err) + return fmt.Errorf("failed to open database connection: %v", err) } defer db.Close() diff --git a/examples/partitioned-dml/main.go b/examples/partitioned-dml/main.go index f43e32f0..a284a43e 100644 --- a/examples/partitioned-dml/main.go +++ b/examples/partitioned-dml/main.go @@ -20,7 +20,6 @@ import ( "fmt" "cloud.google.com/go/spanner" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) diff --git a/examples/run-transaction/main.go b/examples/run-transaction/main.go index 4065e754..c9c2a327 100644 --- a/examples/run-transaction/main.go +++ b/examples/run-transaction/main.go @@ -20,7 +20,6 @@ import ( "fmt" "sync" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) diff --git a/examples/struct-types/main.go b/examples/struct-types/main.go index a5240fd6..ce4efeb5 100644 --- a/examples/struct-types/main.go +++ b/examples/struct-types/main.go @@ -31,7 +31,7 @@ func structTypes(projectId, instanceId, databaseId string) error { ctx := context.Background() db, err := sql.Open("spanner", fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId)) if err != nil { - return fmt.Errorf("failed to open database connection: %v\n", err) + return fmt.Errorf("failed to open database connection: %v", err) } defer db.Close() diff --git a/examples/underlying-client/main.go b/examples/underlying-client/main.go index d98d4053..6b44f26d 100644 --- a/examples/underlying-client/main.go +++ b/examples/underlying-client/main.go @@ -20,7 +20,6 @@ import ( "fmt" "cloud.google.com/go/spanner" - _ "github.com/googleapis/go-sql-spanner" spannerdriver "github.com/googleapis/go-sql-spanner" "github.com/googleapis/go-sql-spanner/examples" ) @@ -30,7 +29,7 @@ func underlyingClient(projectId, instanceId, databaseId string) error { ctx := context.Background() db, err := sql.Open("spanner", fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId)) if err != nil { - return fmt.Errorf("failed to open database connection: %v\n", err) + return fmt.Errorf("failed to open database connection: %v", err) } defer db.Close() diff --git a/integration_test.go b/integration_test.go index fffebe8f..1c8192a4 100644 --- a/integration_test.go +++ b/integration_test.go @@ -1862,6 +1862,9 @@ func TestAllTypes(t *testing.T) { @key, @bool, @string, @bytes, @int64, @float64, @numeric, @date, @timestamp, @json, @boolArray, @stringArray, @bytesArray, @int64Array, @float64Array, @numericArray, @dateArray, @timestampArray, @jsonArray)`) + if err != nil { + t.Fatal(err) + } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if test.skipOnEmulator && runsOnEmulator() { @@ -1946,6 +1949,9 @@ func TestQueryInReadWriteTransaction(t *testing.T) { @key, @bool, @string, @bytes, @int64, @float64, @numeric, @date, @timestamp, @json, @boolArray, @stringArray, @bytesArray, @int64Array, @float64Array, @numericArray, @dateArray, @timestampArray, @jsonArray)`) + if err != nil { + t.Fatal(err) + } for row := int64(0); row < wantRowCount; row++ { res, err := stmt.ExecContext(ctx, row, row%2 == 0, fmt.Sprintf("%v", row), []byte(fmt.Sprintf("%v", row)), row, float64(row)/float64(3), numeric(fmt.Sprintf("%v.%v", row, row)), @@ -2026,7 +2032,7 @@ func TestPDML(t *testing.T) { } defer db.Close() // Insert a couple of test rows. - res, err := db.ExecContext( + _, err = db.ExecContext( ctx, `INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (1, 'First1', 'Last1'), (2, 'First2', 'Last2'), @@ -2047,7 +2053,7 @@ func TestPDML(t *testing.T) { if err != nil { t.Fatalf("failed to switch to PDML mode: %v", err) } - res, err = conn.ExecContext(ctx, "DELETE FROM Singers WHERE TRUE") + res, err := conn.ExecContext(ctx, "DELETE FROM Singers WHERE TRUE") if err != nil { t.Fatalf("delete statement failed: %v", err) } diff --git a/statement_parser.go b/statement_parser.go index 53117941..75c6dcc5 100644 --- a/statement_parser.go +++ b/statement_parser.go @@ -299,9 +299,6 @@ func findParams(positionalParamChar rune, sql string) (string, []string, error) return sql, namedParams, nil } sql = strings.TrimSpace(parsedSQL.String()) - if len(sql) > 0 && sql[len(sql)-1] == ';' { - sql = sql - } return sql, namedParams, nil } diff --git a/testutil/inmem_instance_admin_server_test.go b/testutil/inmem_instance_admin_server_test.go index 7c418976..e503e6e4 100644 --- a/testutil/inmem_instance_admin_server_test.go +++ b/testutil/inmem_instance_admin_server_test.go @@ -27,6 +27,7 @@ import ( "github.com/googleapis/go-sql-spanner/testutil" "google.golang.org/api/option" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/proto" ) @@ -48,7 +49,7 @@ func setupInstanceAdminServer() { } go serv.Serve(lis) - conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure()) + conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatal(err) } @@ -69,8 +70,8 @@ func TestInstanceAdminGetInstance(t *testing.T) { mockInstanceAdmin.SetResps(append(mockInstanceAdmin.Resps()[:0], expectedResponse)) - var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") - var request = &instancepb.GetInstanceRequest{ + formattedName := fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]") + request := &instancepb.GetInstanceRequest{ Name: formattedName, } diff --git a/testutil/inmem_spanner_server.go b/testutil/inmem_spanner_server.go index 946e0f59..882dac8f 100644 --- a/testutil/inmem_spanner_server.go +++ b/testutil/inmem_spanner_server.go @@ -56,6 +56,29 @@ var ( } ) +var random struct { + mu sync.Mutex + rand *rand.Rand +} + +func init() { random.rand = rand.New(rand.NewSource(time.Now().UnixNano())) } + +func randBytes(n int) []byte { + random.mu.Lock() + defer random.mu.Unlock() + + data := make([]byte, n) + _, _ = random.rand.Read(data) + return data +} + +func randDuration(max time.Duration) time.Duration { + random.mu.Lock() + defer random.mu.Unlock() + + return time.Duration(random.rand.Int63n(int64(max))) +} + // StatementResultType indicates the type of result returned by a SQL // statement. type StatementResultType int @@ -658,14 +681,14 @@ func (s *inMemSpannerServer) simulateExecutionTime(method string, req interface{ executionTime, ok := s.executionTimes[method] s.mu.Unlock() if ok { - var randTime int64 + var randTime time.Duration if executionTime.RandomExecutionTime > 0 { - randTime = rand.Int63n(int64(executionTime.RandomExecutionTime)) + randTime = randDuration(executionTime.RandomExecutionTime) } - totalExecutionTime := time.Duration(int64(executionTime.MinimumExecutionTime) + randTime) + totalExecutionTime := executionTime.MinimumExecutionTime + randTime <-time.After(totalExecutionTime) s.mu.Lock() - if executionTime.Errors != nil && len(executionTime.Errors) > 0 { + if len(executionTime.Errors) > 0 { err := executionTime.Errors[0] if !executionTime.KeepError { executionTime.Errors = executionTime.Errors[1:] @@ -1105,12 +1128,10 @@ func (s *inMemSpannerServer) PartitionQuery(ctx context.Context, req *spannerpb. } var partitions []*spannerpb.Partition for i := int64(0); i < req.PartitionOptions.MaxPartitions; i++ { - token := make([]byte, 10) - _, err := rand.Read(token) if err != nil { return nil, gstatus.Error(codes.Internal, "failed to generate random partition token") } - partitions = append(partitions, &spannerpb.Partition{PartitionToken: token}) + partitions = append(partitions, &spannerpb.Partition{PartitionToken: randBytes(10)}) } return &spannerpb.PartitionResponse{ Partitions: partitions, diff --git a/testutil/inmem_spanner_server_test.go b/testutil/inmem_spanner_server_test.go index b5829f7f..d3df877a 100644 --- a/testutil/inmem_spanner_server_test.go +++ b/testutil/inmem_spanner_server_test.go @@ -34,6 +34,7 @@ import ( "google.golang.org/api/option" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" gstatus "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/structpb" ) @@ -69,7 +70,7 @@ func TestMain(m *testing.M) { go serv.Serve(lis) serverAddress = lis.Addr().String() - conn, err := grpc.Dial(serverAddress, grpc.WithInsecure()) + conn, err := grpc.NewClient(serverAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatal(err) } @@ -360,7 +361,7 @@ func TestSpannerExecuteSqlDml(t *testing.T) { if err != nil { t.Fatal(err) } - var rowCount int64 = response.Stats.GetRowCountExact() + rowCount := response.Stats.GetRowCountExact() if rowCount != updateRowCount { t.Fatalf("Update count mismatch\nGot: %d\nWant: %d", rowCount, updateRowCount) } @@ -479,7 +480,7 @@ func TestSpannerExecuteBatchDml(t *testing.T) { } var totalRowCount int64 for _, res := range response.ResultSets { - var rowCount int64 = res.Stats.GetRowCountExact() + rowCount := res.Stats.GetRowCountExact() if rowCount != updateRowCount { t.Fatalf("Update count mismatch\nGot: %d\nWant: %d", rowCount, updateRowCount) }