Skip to content

Commit 3dda7b2

Browse files
govargorahul2393
andauthored
feat(spanner/spannertest): support INSERT DML (#7820)
Co-authored-by: rahul2393 <[email protected]>
1 parent 6c21558 commit 3dda7b2

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

spanner/spannertest/README.md

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ by ascending esotericism:
3333
- case insensitivity of table and column names and query aliases
3434
- transaction simulation
3535
- FOREIGN KEY and CHECK constraints
36-
- INSERT DML statements
3736
- set operations (UNION, INTERSECT, EXCEPT)
3837
- STRUCT types
3938
- partition support

spanner/spannertest/db.go

+58
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,64 @@ func (d *database) Execute(stmt spansql.DMLStmt, params queryParams) (int, error
12621262
}
12631263
}
12641264
return n, nil
1265+
case *spansql.Insert:
1266+
t, err := d.table(stmt.Table)
1267+
if err != nil {
1268+
return 0, err
1269+
}
1270+
1271+
t.mu.Lock()
1272+
defer t.mu.Unlock()
1273+
1274+
ec := evalContext{
1275+
cols: t.cols,
1276+
params: params,
1277+
}
1278+
1279+
values := make(row, len(t.cols))
1280+
input := stmt.Input.(spansql.Values)
1281+
if len(input) > 0 {
1282+
for i := 0; i < len(input); i++ {
1283+
val := input[i]
1284+
for k, v := range val {
1285+
switch v := v.(type) {
1286+
// if spanner.Statement.Params is not empty, scratch row with ec.parameters
1287+
case spansql.Param:
1288+
values[k] = ec.params[t.cols[k].Name.SQL()].Value
1289+
// if nil is included in parameters, pass nil
1290+
case spansql.ID:
1291+
cutset := `""`
1292+
str := strings.Trim(v.SQL(), cutset)
1293+
if str == "nil" {
1294+
values[k] = nil
1295+
} else {
1296+
expr, err := ec.evalExpr(v)
1297+
if err != nil {
1298+
return 0, status.Errorf(codes.InvalidArgument, "invalid parameter format")
1299+
}
1300+
values[k] = expr
1301+
}
1302+
// if parameter is embedded in SQL as string, not in statement.Params, analyze parameters
1303+
default:
1304+
expr, err := ec.evalExpr(v)
1305+
if err != nil {
1306+
return 0, status.Errorf(codes.InvalidArgument, "invalid parameter format")
1307+
}
1308+
values[k] = expr
1309+
}
1310+
}
1311+
}
1312+
}
1313+
1314+
// pk check if the primary key already exists
1315+
pk := values[:t.pkCols]
1316+
rowNum, found := t.rowForPK(pk)
1317+
if found {
1318+
return 0, status.Errorf(codes.AlreadyExists, "row already in table")
1319+
}
1320+
t.insertRow(rowNum, values)
1321+
1322+
return 1, nil
12651323
}
12661324
}
12671325

spanner/spannertest/integration_test.go

+75-5
Original file line numberDiff line numberDiff line change
@@ -708,23 +708,91 @@ func TestIntegration_ReadsAndQueries(t *testing.T) {
708708
spanner.Insert("SomeStrings", []string{"i", "str"}, []interface{}{1, "abar"}),
709709
spanner.Insert("SomeStrings", []string{"i", "str"}, []interface{}{2, nil}),
710710
spanner.Insert("SomeStrings", []string{"i", "str"}, []interface{}{3, "bbar"}),
711-
712-
spanner.Insert("Updateable", []string{"id", "first", "last"}, []interface{}{0, "joe", nil}),
713-
spanner.Insert("Updateable", []string{"id", "first", "last"}, []interface{}{1, "doe", "joan"}),
714-
spanner.Insert("Updateable", []string{"id", "first", "last"}, []interface{}{2, "wong", "wong"}),
715711
})
716712
if err != nil {
717713
t.Fatalf("Inserting sample data: %v", err)
718714
}
719715

716+
// Perform INSERT DML; the results are checked later on.
717+
n = 0
718+
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
719+
for _, u := range []string{
720+
`INSERT INTO Updateable (id, first, last) VALUES (0, "joe", nil)`,
721+
`INSERT INTO Updateable (id, first, last) VALUES (1, "doe", "joan")`,
722+
`INSERT INTO Updateable (id, first, last) VALUES (2, "wong", "wong")`,
723+
} {
724+
nr, err := tx.Update(ctx, spanner.NewStatement(u))
725+
if err != nil {
726+
return err
727+
}
728+
n += nr
729+
}
730+
return nil
731+
})
732+
if err != nil {
733+
t.Fatalf("Inserting with DML: %v", err)
734+
}
735+
if n != 3 {
736+
t.Errorf("Inserting with DML affected %d rows, want 3", n)
737+
}
738+
739+
// Perform INSERT DML with statement.Params; the results are checked later on.
740+
n = 0
741+
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
742+
stmt := spanner.Statement{
743+
SQL: "INSERT INTO Updateable (id, first, last) VALUES (@id, @first, @last)",
744+
Params: map[string]interface{}{
745+
"id": 3,
746+
"first": "tom",
747+
"last": "jerry",
748+
},
749+
}
750+
nr, err := tx.Update(ctx, stmt)
751+
if err != nil {
752+
return err
753+
}
754+
n += nr
755+
return nil
756+
})
757+
if err != nil {
758+
t.Fatalf("Inserting with DML: %v", err)
759+
}
760+
if n != 1 {
761+
t.Errorf("Inserting with DML affected %d rows, want 1", n)
762+
}
763+
764+
// Perform INSERT DML with statement.Params and inline parameter; the results are checked later on.
765+
n = 0
766+
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
767+
stmt := spanner.Statement{
768+
SQL: `INSERT INTO Updateable (id, first, last) VALUES (@id, "jim", @last)`,
769+
Params: map[string]interface{}{
770+
"id": 4,
771+
"last": nil,
772+
},
773+
}
774+
nr, err := tx.Update(ctx, stmt)
775+
if err != nil {
776+
return err
777+
}
778+
n += nr
779+
return nil
780+
})
781+
if err != nil {
782+
t.Fatalf("Inserting with DML: %v", err)
783+
}
784+
if n != 1 {
785+
t.Errorf("Inserting with DML affected %d rows, want 1", n)
786+
}
787+
720788
// Perform UPDATE DML; the results are checked later on.
721789
n = 0
722790
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
723791
for _, u := range []string{
724792
`UPDATE Updateable SET last = "bloggs" WHERE id = 0`,
725793
`UPDATE Updateable SET first = last, last = first WHERE id = 1`,
726794
`UPDATE Updateable SET last = DEFAULT WHERE id = 2`,
727-
`UPDATE Updateable SET first = "noname" WHERE id = 3`, // no id=3
795+
`UPDATE Updateable SET first = "noname" WHERE id = 5`, // no id=5
728796
} {
729797
nr, err := tx.Update(ctx, spanner.NewStatement(u))
730798
if err != nil {
@@ -1156,6 +1224,8 @@ func TestIntegration_ReadsAndQueries(t *testing.T) {
11561224
{int64(0), "joe", "bloggs"},
11571225
{int64(1), "joan", "doe"},
11581226
{int64(2), "wong", nil},
1227+
{int64(3), "tom", "jerry"},
1228+
{int64(4), "jim", nil},
11591229
},
11601230
},
11611231
// Regression test for aggregating no rows; it used to return an empty row.

0 commit comments

Comments
 (0)