| //go:build cgo |
| |
| package sqlite3 |
| |
| import ( |
| "context" |
| "database/sql/driver" |
| "io" |
| "reflect" |
| "testing" |
| ) |
| |
| func TestRowsStepValues(t *testing.T) { |
| for _, cancellable := range []bool{false, true} { |
| name := "background" |
| if cancellable { |
| name = "cancellable" |
| } |
| t.Run(name, func(t *testing.T) { |
| conn := openContextTestConn(t) |
| ctx := context.Background() |
| if cancellable { |
| var cancel context.CancelFunc |
| ctx, cancel = context.WithCancel(ctx) |
| defer cancel() |
| } |
| stmt, err := conn.PrepareContext(ctx, `SELECT 'first', x'0102', 1, 1.5 WHERE :include |
| UNION ALL SELECT x'0304', 'second', NULL, 2 |
| UNION ALL SELECT '', x'', 3.5, NULL`) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer stmt.Close() |
| want := [][]driver.Value{ |
| {"first", []byte{1, 2}, int64(1), float64(1.5)}, |
| {[]byte{3, 4}, "second", nil, int64(2)}, |
| {"", []byte{}, float64(3.5), nil}, |
| } |
| // Alternate fused and generic binding with the same buffers. |
| for run := 0; run < 3; run++ { |
| args := []driver.NamedValue{{Ordinal: 1, Value: int64(1)}} |
| if run == 1 { |
| args[0].Name = "include" |
| } |
| rows, err := stmt.(driver.StmtQueryContext).QueryContext(ctx, args) |
| if err != nil { |
| t.Fatal(err) |
| } |
| var got [][]driver.Value |
| for range want { |
| values := make([]driver.Value, 4) |
| if err := rows.Next(values); err != nil { |
| t.Fatal(err) |
| } |
| got = append(got, values) |
| } |
| if err := rows.Next(make([]driver.Value, 4)); err != io.EOF { |
| t.Fatalf("Next = %v, want EOF", err) |
| } |
| if err := rows.Close(); err != nil { |
| t.Fatal(err) |
| } |
| // Retained strings and blobs must survive subsequent steps and reset. |
| if !reflect.DeepEqual(got, want) { |
| t.Fatalf("got %#v, want %#v", got, want) |
| } |
| } |
| }) |
| } |
| } |