// Copyright 2014 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ql

import (
	"bytes"
	"fmt"
	"math/big"
	//"reflect"
	"testing"
	"time"

	"github.com/cznic/mathutil"
)

type (
	testSchema struct {
		a  int8
		ID int64
		A  int8
		b  int
		B  int `ql:"-"`
	}

	testSchema2 struct{}

	testSchema3 struct {
		a  int8
		ID uint64
		A  int8
		b  int
		B  int `ql:"-"`
		c  bool
		C  bool `ql:"name cc"`
	}

	testSchema4 struct {
		a  int8
		ID int64 `ql:"name id"`
		A  int8
		b  int
		B  int `ql:"-"`
		c  bool
		C  bool `ql:"name cc"`
	}

	testSchema5 struct {
		I int `ql:"index x,uindex u"`
	}

	testSchema6 struct {
		A string `ql:"index x"`
	}

	testSchema7 struct {
		A int64
		B string `ql:"uindex x"`
		C bool
	}

	testSchema8 struct {
		A bool
		//B  int
		C int8
		D int16
		E int32
		F int64
		//G  uint
		H  uint8
		I  uint16
		J  uint32
		K  uint64
		L  float32
		M  float64
		N  complex64
		O  complex128
		P  []byte
		Q  big.Int
		R  big.Rat
		S  string
		T  time.Time
		U  time.Duration
		PA *bool
		//PB *int
		PC *int8
		PD *int16
		PE *int32
		PF *int64
		//PG *uint
		PH *uint8
		PI *uint16
		PJ *uint32
		PK *uint64
		PL *float32
		PM *float64
		PN *complex64
		PO *complex128
		PP *[]byte
		PQ *big.Int
		PR *big.Rat
		PS *string
		PT *time.Time
		PU *time.Duration
	}

	testSchema9 struct {
		i              int
		ID             int64  `ql:"index xID"`
		Other          string `ql:"-"`
		DepartmentName string `ql:"uindex xDepartmentName"`
	}
)

const (
	testSchemaSFFF = "begin transaction; create table if not exists testSchema (A int8); commit;"
	testSchemaSFFT = "begin transaction; create table if not exists ql_testSchema (A int8); commit;"
	testSchemaSFTF = "begin transaction; create table testSchema (A int8); commit;"
	testSchemaSFTT = "begin transaction; create table ql_testSchema (A int8); commit;"
	testSchemaSTFF = "create table if not exists testSchema (A int8)"
	testSchemaSTFT = "create table if not exists ql_testSchema (A int8)"
	testSchemaSTTF = "create table testSchema (A int8)"
	testSchemaSTTT = "create table ql_testSchema (A int8)"
	testSchema3S   = "begin transaction; create table if not exists testSchema3 (ID uint64, A int8, cc bool); commit;"
	testSchema4S   = "begin transaction; create table if not exists testSchema4 (id int64, A int8, cc bool); commit;"
	testSchema6S   = "create table testSchema6 (A string); create index x on testSchema6 (A);"
	testSchema7S   = "begin transaction; create table testSchema7 (A int64, B string, C bool); create unique index x on testSchema7 (B); commit;"
	testSchema8S   = `
		begin transaction;
			create table if not exists testSchema8 (
				A  bool,
				//B  int64,
				C  int8,
				D  int16,
				E  int32,
				F  int64,
				//G  uint64,
				H  uint8,
				I  uint16,
				J  uint32,
				K  uint64,
				L  float32,
				M  float64,
				N  complex64,
				O  complex128,
				P  blob,
				Q  bigInt,
				R  bigRat,
				S  string,
				T  time,
				U  duration,
				PA bool,
				//PB int64,
				PC int8,
				PD int16,
				PE int32,
				PF int64,
				//PG uint64,
				PH uint8,
				PI uint16,
				PJ uint32,
				PK uint64,
				PL float32,
				PM float64,
				PN complex64,
				PO complex128,
				PP blob,
				PQ bigInt,
				PR bigRat,
				PS string,
				PT time,
				PU  duration,
			);
		commit;`
	testSchema9S = `
		begin transaction;
			create table if not exists testSchema9 (DepartmentName string);
			create index if not exists xID on testSchema9 (id());
			create unique index if not exists xDepartmentName on testSchema9 (DepartmentName);
		commit;`
)

func TestSchema(t *testing.T) {
	tab := []struct {
		inst interface{}
		name string
		opts *SchemaOptions
		err  bool
		s    string
	}{
		// 0
		{inst: nil, err: true},
		{inst: interface{}(nil), err: true},
		{testSchema{}, "", nil, false, testSchemaSFFF},
		{testSchema{}, "", &SchemaOptions{}, false, testSchemaSFFF},
		{testSchema{}, "", &SchemaOptions{KeepPrefix: true}, false, testSchemaSFFT},
		// 5
		{testSchema{}, "", &SchemaOptions{NoIfNotExists: true}, false, testSchemaSFTF},
		{testSchema{}, "", &SchemaOptions{NoIfNotExists: true, KeepPrefix: true}, false, testSchemaSFTT},
		{testSchema{}, "", &SchemaOptions{NoTransaction: true}, false, testSchemaSTFF},
		{testSchema{}, "", &SchemaOptions{NoTransaction: true, KeepPrefix: true}, false, testSchemaSTFT},
		{testSchema{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true}, false, testSchemaSTTF},
		// 10
		{testSchema{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true, KeepPrefix: true}, false, testSchemaSTTT},
		{testSchema2{}, "", nil, true, ""},
		{testSchema3{}, "", nil, false, testSchema3S},
		{testSchema4{}, "", nil, false, testSchema4S},
		{testSchema5{}, "", nil, true, ""},
		// 15
		{testSchema6{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true}, false, testSchema6S},
		{testSchema7{}, "", &SchemaOptions{NoIfNotExists: true}, false, testSchema7S},
		{testSchema8{}, "", nil, false, testSchema8S},
		{&testSchema8{}, "", nil, false, testSchema8S},
		{&testSchema9{}, "", nil, false, testSchema9S},
	}

	for iTest, test := range tab {
		l, err := Schema(test.inst, test.name, test.opts)
		if g, e := err != nil, test.err; g != e {
			t.Fatal(iTest, g, e, err)
		}

		if err != nil {
			t.Log(iTest, err)
			continue
		}

		s, err := Compile(test.s)
		if err != nil {
			panic("internal error 055")
		}

		if g, e := l.String(), s.String(); g != e {
			t.Fatalf("%d\n----\n%s\n----\n%s", iTest, g, e)
		}
	}
}

func ExampleSchema() {
	type department struct {
		a              int    // unexported -> ignored
		ID             int64  `ql:"index xID"`
		Other          string `xml:"-" ql:"-"` // ignored by QL tag
		DepartmentName string `ql:"name Name, uindex xName" json:"foo"`
		m              bool
		HQ             int32
		z              string
	}

	schema := MustSchema((*department)(nil), "", nil)
	sel := MustCompile(`
		SELECT * FROM __Table WHERE !hasPrefix(Name, "__") ORDER BY Name;
		SELECT * FROM __Column WHERE !hasPrefix(TableName, "__") ORDER BY TableName, Ordinal;
		SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY Name, ColumnName;`,
	)
	fmt.Print(schema)

	db, err := OpenMem()
	if err != nil {
		panic(err)
	}

	if _, _, err = db.Execute(NewRWCtx(), schema); err != nil {
		panic(err)
	}

	rs, _, err := db.Execute(nil, sel)
	if err != nil {
		panic(err)
	}

	for _, rs := range rs {
		fmt.Println("----")
		if err = rs.Do(true, func(data []interface{}) (bool, error) {
			fmt.Println(data)
			return true, nil
		}); err != nil {
			panic(err)
		}
	}
	// Output:
	// BEGIN TRANSACTION;
	// 	CREATE TABLE IF NOT EXISTS department (Name string, HQ int32);
	// 	CREATE INDEX IF NOT EXISTS xID ON department (id());
	// 	CREATE UNIQUE INDEX IF NOT EXISTS xName ON department (Name);
	// COMMIT;
	// ----
	// [Name Schema]
	// [department CREATE TABLE department (Name string, HQ int32);]
	// ----
	// [TableName Ordinal Name Type]
	// [department 1 Name string]
	// [department 2 HQ int32]
	// ----
	// [TableName ColumnName Name IsUnique]
	// [department id() xID false]
	// [department Name xName true]
}

func TestMarshal(t *testing.T) {
	now := time.Now()
	dur := time.Millisecond
	schema8 := testSchema8{
		A: true,
		//B: 1,
		C: 2,
		D: 3,
		E: 4,
		F: 5,
		//G: 6,
		H: 7,
		I: 8,
		J: 9,
		K: 10,
		L: 11,
		M: 12,
		N: -1,
		O: -2,
		P: []byte("abc"),
		Q: *big.NewInt(1),
		R: *big.NewRat(3, 2),
		S: "string",
		T: now,
		U: dur,
	}
	schema8.PA = &schema8.A
	//schema8.PB = &schema8.B
	schema8.PC = &schema8.C
	schema8.PD = &schema8.D
	schema8.PE = &schema8.E
	schema8.PF = &schema8.F
	//schema8.PG = &schema8.G
	schema8.PH = &schema8.H
	schema8.PI = &schema8.I
	schema8.PJ = &schema8.J
	schema8.PK = &schema8.K
	schema8.PL = &schema8.L
	schema8.PM = &schema8.M
	schema8.PN = &schema8.N
	schema8.PO = &schema8.O
	schema8.PP = &schema8.P
	schema8.PQ = &schema8.Q
	schema8.PR = &schema8.R
	schema8.PS = &schema8.S
	schema8.PT = &schema8.T
	schema8.PU = &schema8.U

	type u int
	tab := []struct {
		inst interface{}
		err  bool
		r    []interface{}
	}{
		{42, true, nil},
		{new(u), true, nil},
		{testSchema8{}, false, []interface{}{
			false,
			//int64(0),
			int8(0),
			int16(0),
			int32(0),
			int64(0),
			//uint64(0),
			uint8(0),
			uint16(0),
			uint32(0),
			uint64(0),
			float32(0),
			float64(0),
			complex64(0),
			complex128(0),
			[]byte(nil),
			big.Int{},
			big.Rat{},
			"",
			time.Time{},
			time.Duration(0),
			nil,
			//nil,
			nil,
			nil,
			nil,
			nil,
			//nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		}},
		{&testSchema8{}, false, []interface{}{
			false,
			//int64(0),
			int8(0),
			int16(0),
			int32(0),
			int64(0),
			//uint64(0),
			uint8(0),
			uint16(0),
			uint32(0),
			uint64(0),
			float32(0),
			float64(0),
			complex64(0),
			complex128(0),
			[]byte(nil),
			big.Int{},
			big.Rat{},
			"",
			time.Time{},
			time.Duration(0),
			nil,
			//nil,
			nil,
			nil,
			nil,
			nil,
			//nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		}},
		{schema8, false, []interface{}{
			true,
			//int64(1),
			int8(2),
			int16(3),
			int32(4),
			int64(5),
			//uint64(6),
			uint8(7),
			uint16(8),
			uint32(9),
			uint64(10),
			float32(11),
			float64(12),
			complex64(-1),
			complex128(-2),
			[]byte("abc"),
			*big.NewInt(1),
			*big.NewRat(3, 2),
			"string",
			now,
			dur,
			true,
			//int64(1),
			int8(2),
			int16(3),
			int32(4),
			int64(5),
			//uint64(6),
			uint8(7),
			uint16(8),
			uint32(9),
			uint64(10),
			float32(11),
			float64(12),
			complex64(-1),
			complex128(-2),
			[]byte("abc"),
			*big.NewInt(1),
			*big.NewRat(3, 2),
			"string",
			now,
			dur,
		}},
		{&schema8, false, []interface{}{
			true,
			//int64(1),
			int8(2),
			int16(3),
			int32(4),
			int64(5),
			//uint64(6),
			uint8(7),
			uint16(8),
			uint32(9),
			uint64(10),
			float32(11),
			float64(12),
			complex64(-1),
			complex128(-2),
			[]byte("abc"),
			*big.NewInt(1),
			*big.NewRat(3, 2),
			"string",
			now,
			dur,
			true,
			//int64(1),
			int8(2),
			int16(3),
			int32(4),
			int64(5),
			//uint64(6),
			uint8(7),
			uint16(8),
			uint32(9),
			uint64(10),
			float32(11),
			float64(12),
			complex64(-1),
			complex128(-2),
			[]byte("abc"),
			*big.NewInt(1),
			*big.NewRat(3, 2),
			"string",
			now,
			dur,
		}},
	}
	for iTest, test := range tab {
		r, err := Marshal(test.inst)
		if g, e := err != nil, test.err; g != e {
			t.Fatal(iTest, g, e)
		}

		if err != nil {
			t.Log(err)
			continue
		}

		for i := 0; i < mathutil.Min(len(r), len(test.r)); i++ {
			g, e := r[i], test.r[i]
			use(e)
			switch x := g.(type) {
			case bool:
				switch y := e.(type) {
				case bool:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case int:
				switch y := e.(type) {
				case int64:
					if int64(x) != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case int8:
				switch y := e.(type) {
				case int8:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case int16:
				switch y := e.(type) {
				case int16:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case int32:
				switch y := e.(type) {
				case int32:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case int64:
				switch y := e.(type) {
				case int64:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case uint:
				switch y := e.(type) {
				case uint64:
					if uint64(x) != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case uint8:
				switch y := e.(type) {
				case uint8:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case uint16:
				switch y := e.(type) {
				case uint16:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case uint32:
				switch y := e.(type) {
				case uint32:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case uint64:
				switch y := e.(type) {
				case uint64:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case float32:
				switch y := e.(type) {
				case float32:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case float64:
				switch y := e.(type) {
				case float64:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case complex64:
				switch y := e.(type) {
				case complex64:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case complex128:
				switch y := e.(type) {
				case complex128:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case []byte:
				switch y := e.(type) {
				case []byte:
					if !bytes.Equal(x, y) {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case big.Int:
				switch y := e.(type) {
				case big.Int:
					if x.Cmp(&y) != 0 {
						t.Fatal(iTest, &x, &y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case big.Rat:
				switch y := e.(type) {
				case big.Rat:
					if x.Cmp(&y) != 0 {
						t.Fatal(iTest, &x, &y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case string:
				switch y := e.(type) {
				case string:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case time.Time:
				switch y := e.(type) {
				case time.Time:
					if !x.Equal(y) {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case time.Duration:
				switch y := e.(type) {
				case time.Duration:
					if x != y {
						t.Fatal(iTest, x, y)
					}
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			case nil:
				switch y := e.(type) {
				case nil:
					// ok
				default:
					t.Fatalf("%d: %T <-> %T", iTest, x, y)
				}
			default:
				panic(fmt.Errorf("%T", x))
			}
		}

		if g, e := len(r), len(test.r); g != e {
			t.Fatal(iTest, g, e)
		}

	}
}

func ExampleMarshal() {
	type myInt int16

	type myString string

	type item struct {
		ID   int64
		Name myString
		Qty  *myInt // pointer enables nil values
		Bar  int8
	}

	schema := MustSchema((*item)(nil), "", nil)
	ins := MustCompile(`
		BEGIN TRANSACTION;
			INSERT INTO item VALUES($1, $2, $3);
		COMMIT;`,
	)

	db, err := OpenMem()
	if err != nil {
		panic(err)
	}

	ctx := NewRWCtx()
	if _, _, err := db.Execute(ctx, schema); err != nil {
		panic(err)
	}

	if _, _, err := db.Execute(ctx, ins, MustMarshal(&item{Name: "foo", Bar: -1})...); err != nil {
		panic(err)
	}

	q := myInt(42)
	if _, _, err := db.Execute(ctx, ins, MustMarshal(&item{Name: "bar", Qty: &q})...); err != nil {
		panic(err)
	}

	rs, _, err := db.Run(nil, "SELECT * FROM item ORDER BY id();")
	if err != nil {
		panic(err)
	}

	if err = rs[0].Do(true, func(data []interface{}) (bool, error) {
		fmt.Println(data)
		return true, nil
	}); err != nil {
		panic(err)
	}
	// Output:
	// [Name Qty Bar]
	// [foo <nil> -1]
	// [bar 42 0]
}

func TestUnmarshal0(t *testing.T) {
	type t1 struct {
		I, J int64
	}

	// ---- value field
	v1 := &t1{-1, -2}
	if err := Unmarshal(v1, []interface{}{int64(42), int64(314)}); err != nil {
		t.Fatal(err)
	}

	if g, e := v1.I, int64(42); g != e {
		t.Fatal(g, e)
	}

	if g, e := v1.J, int64(314); g != e {
		t.Fatal(g, e)
	}

	type t2 struct {
		P *int64
	}

	// ---- nil into nil ptr field
	v2 := &t2{P: nil}
	if err := Unmarshal(v2, []interface{}{nil}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P, (*int64)(nil); g != e {
		t.Fatal(g, e)
	}

	v2 = &t2{P: nil}
	if err := Unmarshal(v2, []interface{}{interface{}(nil)}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P, (*int64)(nil); g != e {
		t.Fatal(g, e)
	}

	// ---- nil into non nil ptr field
	i := int64(42)
	v2 = &t2{P: &i}
	if err := Unmarshal(v2, []interface{}{nil}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P, (*int64)(nil); g != e {
		t.Fatal(g, e)
	}

	if g, e := i, int64(42); g != e {
		t.Fatal(g, e)
	}

	v2 = &t2{P: &i}
	if err := Unmarshal(v2, []interface{}{interface{}(nil)}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P, (*int64)(nil); g != e {
		t.Fatal(g, e)
	}

	if g, e := i, int64(42); g != e {
		t.Fatal(g, e)
	}

	// ---- non nil value into non nil ptr field
	i = 42
	v2 = &t2{P: &i}
	if err := Unmarshal(v2, []interface{}{int64(314)}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P, &i; g != e {
		t.Fatal(g, e)
	}

	if g, e := i, int64(314); g != e {
		t.Fatal(g, e)
	}

	// ---- non nil value into nil ptr field
	v2 = &t2{P: nil}
	if err := Unmarshal(v2, []interface{}{int64(314)}); err != nil {
		t.Fatal(err)
	}

	if g, e := v2.P != nil, true; g != e {
		t.Fatal(g, e)
	}

	if g, e := *v2.P, int64(314); g != e {
		t.Fatal(g, e)
	}
}

func TestUnmarshal(t *testing.T) {
	type myString string

	type t1 struct {
		A bool
		B myString
	}

	type t2 struct {
		A  bool
		ID int64
		B  myString
	}

	f := func(v interface{}) int64 {
		if x, ok := v.(*t2); ok {
			return x.ID
		}

		return -1
	}

	tab := []struct {
		inst interface{}
		data []interface{}
		err  bool
	}{
		// 0
		{t1{}, []interface{}{true, "foo"}, true},      // not a ptr
		{&t1{}, []interface{}{true}, true},            // too few values
		{&t1{}, []interface{}{"foo"}, true},           // too few values
		{&t1{}, []interface{}{true, "foo", 42}, true}, // too many values
		{&t1{}, []interface{}{"foo", true, 42}, true}, // too many values
		// 5
		{&t1{}, []interface{}{true, "foo"}, false},
		{&t1{}, []interface{}{false, "bar"}, false},
		{&t1{}, []interface{}{"bar", "baz"}, true},
		{&t1{}, []interface{}{true, 42.7}, true},
		{&t2{}, []interface{}{1}, true}, // too few values
		// 10
		{&t2{}, []interface{}{1, 2, 3, 4}, true}, // too many values
		{&t2{}, []interface{}{false, int64(314), "foo"}, false},
		{&t2{}, []interface{}{true, int64(42), "foo"}, false},
		{&t2{}, []interface{}{false, "foo"}, false},
		// 15
		{&t2{}, []interface{}{true, "foo"}, false},
	}

	for iTest, test := range tab {
		inst := test.inst
		err := Unmarshal(inst, test.data)
		if g, e := err != nil, test.err; g != e {
			t.Fatal(iTest, g, e)
		}

		if err != nil {
			t.Log(iTest, err)
			continue
		}

		data, err := Marshal(inst)
		if err != nil {
			t.Fatal(iTest, err)
		}

		if g, e := len(data), len(test.data); g > e {
			t.Fatal(iTest, g, e)
		}

		j := 0
		for _, v := range data {
			v2 := test.data[j]
			j++
			if _, ok := v2.(int64); ok {
				if g, e := f(inst), v2; g != e {
					t.Fatal(iTest, g, e)
				}

				continue
			}

			if g, e := v, v2; g != e {
				t.Fatal(iTest, g, e)
			}
		}
	}
}

func ExampleUnmarshal() {
	type myString string

	type row struct {
		ID int64
		S  myString
		P  *int64
	}

	schema := MustSchema((*row)(nil), "", nil)
	ins := MustCompile(`
		BEGIN TRANSACTION;
			INSERT INTO row VALUES($1, $2);
		COMMIT;`,
	)
	sel := MustCompile(`
		SELECT id(), S, P FROM row ORDER by id();
		SELECT * FROM row ORDER by id();`,
	)

	db, err := OpenMem()
	if err != nil {
		panic(err)
	}

	ctx := NewRWCtx()
	if _, _, err = db.Execute(ctx, schema); err != nil {
		panic(err)
	}

	r := &row{S: "foo"}
	if _, _, err = db.Execute(ctx, ins, MustMarshal(r)...); err != nil {
		panic(err)
	}

	i42 := int64(42)
	r = &row{S: "bar", P: &i42}
	if _, _, err = db.Execute(ctx, ins, MustMarshal(r)...); err != nil {
		panic(err)
	}

	rs, _, err := db.Execute(nil, sel)
	if err != nil {
		panic(err)
	}

	for _, rs := range rs {
		fmt.Println("----")
		if err := rs.Do(false, func(data []interface{}) (bool, error) {
			r := &row{}
			if err := Unmarshal(r, data); err != nil {
				return false, err
			}

			fmt.Printf("ID %d, S %q, P ", r.ID, r.S)
			switch r.P == nil {
			case true:
				fmt.Println("<nil>")
			default:
				fmt.Println(*r.P)
			}
			return true, nil
		}); err != nil {
			panic(err)
		}
	}
	// Output:
	// ----
	// ID 1, S "foo", P <nil>
	// ID 2, S "bar", P 42
	// ----
	// ID 0, S "foo", P <nil>
	// ID 0, S "bar", P 42
}
