Skip to content

Commit a214d86

Browse files
authored
fix: return explicit error message when using DDL statements with QueryContext (#438)
* fix: return explicit error message when using DDL statements with QueryContext * Added new tests for verifying errors in both RO and RW transactions
1 parent 63e9e4b commit a214d86

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

conn.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,9 +758,13 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions ExecO
758758
if err != nil {
759759
return nil, err
760760
}
761+
statementType := detectStatementType(query)
762+
// DDL statements are not supported in QueryContext so fail early.
763+
if statementType.statementType == statementTypeDdl {
764+
return nil, spanner.ToSpannerError(status.Errorf(codes.FailedPrecondition, "QueryContext does not support DDL statements, use ExecContext instead"))
765+
}
761766
var iter rowIterator
762767
if c.tx == nil {
763-
statementType := detectStatementType(query)
764768
if statementType.statementType == statementTypeDml {
765769
// Use a read/write transaction to execute the statement.
766770
var commitTs time.Time

conn_with_mockserver_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,65 @@ func TestIsolationLevelAutoCommit(t *testing.T) {
239239
}
240240
}
241241
}
242+
243+
func TestDDLUsingQueryContext(t *testing.T) {
244+
t.Parallel()
245+
246+
db, _, teardown := setupTestDBConnection(t)
247+
defer teardown()
248+
ctx := context.Background()
249+
250+
// DDL statements should not use the query context.
251+
_, err := db.QueryContext(ctx, "CREATE TABLE Foo (Bar STRING(100))")
252+
if err == nil {
253+
t.Fatal("expected error for DDL statement using QueryContext, got nil")
254+
}
255+
if g, w := err.Error(), `spanner: code = "FailedPrecondition", desc = "QueryContext does not support DDL statements, use ExecContext instead"`; g != w {
256+
t.Fatalf("error mismatch\n Got: %v\nWant: %v", g, w)
257+
}
258+
}
259+
260+
func TestDDLUsingQueryContextInReadOnlyTx(t *testing.T) {
261+
t.Parallel()
262+
263+
db, _, teardown := setupTestDBConnection(t)
264+
defer teardown()
265+
ctx := context.Background()
266+
tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
267+
if err != nil {
268+
t.Fatal(err)
269+
}
270+
defer tx.Rollback()
271+
272+
// DDL statements should not use the query context in a read-only transaction.
273+
_, err = tx.QueryContext(ctx, "CREATE TABLE Foo (Bar STRING(100))")
274+
if err == nil {
275+
t.Fatal("expected error for DDL statement using QueryContext in read-only transaction, got nil")
276+
}
277+
if g, w := err.Error(), `spanner: code = "FailedPrecondition", desc = "QueryContext does not support DDL statements, use ExecContext instead"`; g != w {
278+
t.Fatalf("error mismatch\n Got: %v\nWant: %v", g, w)
279+
}
280+
}
281+
282+
func TestDDLUsingQueryContextInReadWriteTransaction(t *testing.T) {
283+
t.Parallel()
284+
285+
db, _, teardown := setupTestDBConnection(t)
286+
defer teardown()
287+
ctx := context.Background()
288+
289+
tx, err := db.BeginTx(ctx, &sql.TxOptions{})
290+
if err != nil {
291+
t.Fatal(err)
292+
}
293+
defer tx.Rollback()
294+
295+
// DDL statements should not use the query context in a read-write transaction.
296+
_, err = tx.QueryContext(ctx, "CREATE TABLE Foo (Bar STRING(100))")
297+
if err == nil {
298+
t.Fatal("expected error for DDL statement using QueryContext in read-write transaction, got nil")
299+
}
300+
if g, w := err.Error(), `spanner: code = "FailedPrecondition", desc = "QueryContext does not support DDL statements, use ExecContext instead"`; g != w {
301+
t.Fatalf("error mismatch\n Got: %v\nWant: %v", g, w)
302+
}
303+
}

0 commit comments

Comments
 (0)