Files
chaisql/internal/sql/parser/insert_test.go
2024-02-17 14:27:02 +04:00

227 lines
7.4 KiB
Go

package parser_test
import (
"context"
"testing"
"github.com/chaisql/chai/internal/expr"
"github.com/chaisql/chai/internal/query"
"github.com/chaisql/chai/internal/query/statement"
"github.com/chaisql/chai/internal/sql/parser"
"github.com/chaisql/chai/internal/stream"
"github.com/chaisql/chai/internal/stream/path"
"github.com/chaisql/chai/internal/stream/rows"
"github.com/chaisql/chai/internal/stream/table"
"github.com/chaisql/chai/internal/testutil"
"github.com/chaisql/chai/internal/testutil/assert"
"github.com/stretchr/testify/require"
)
func TestParserInsert(t *testing.T) {
tests := []struct {
name string
s string
expected *stream.Stream
fails bool
}{
{"Values / With fields", "INSERT INTO test (a, b) VALUES ('c', 'd')",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Values / With too many values", "INSERT INTO test (a, b) VALUES ('c', 'd', 'e')",
nil, true},
{"Values / Multiple", "INSERT INTO test (a, b) VALUES ('c', 'd'), ('e', 'f')",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("e"),
testutil.TextValue("f"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Values / Returning", "INSERT INTO test (a, b) VALUES ('c', 'd') RETURNING *, a, b as B, c",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(rows.Project(expr.Wildcard{}, testutil.ParseNamedExpr(t, "a"), testutil.ParseNamedExpr(t, "b", "B"), testutil.ParseNamedExpr(t, "c"))),
false},
{"Values / With fields / Wrong values", "INSERT INTO test (a, b) VALUES {a: 1}, ('e', 'f')",
nil, true},
{"Values / Without fields / Wrong values", "INSERT INTO test VALUES {a: 1}, ('e', 'f')",
nil, true},
{"Values / ON CONFLICT DO NOTHING", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT DO NOTHING RETURNING *",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(stream.OnConflict(nil)).
Pipe(table.Insert("test")).
Pipe(rows.Project(expr.Wildcard{})),
false},
{"Values / ON CONFLICT IGNORE", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT IGNORE RETURNING *",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).Pipe(table.Validate("test")).
Pipe(stream.OnConflict(nil)).
Pipe(table.Insert("test")).
Pipe(rows.Project(expr.Wildcard{})),
false},
{"Values / ON CONFLICT DO REPLACE", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT DO REPLACE RETURNING *",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(stream.OnConflict(stream.New(table.Replace("test")))).
Pipe(table.Insert("test")).
Pipe(rows.Project(expr.Wildcard{})),
false},
{"Values / ON CONFLICT REPLACE", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT REPLACE RETURNING *",
stream.New(rows.Emit(
expr.Row{
Columns: []string{"a", "b"},
Exprs: []expr.Expr{
testutil.TextValue("c"),
testutil.TextValue("d"),
},
},
)).
Pipe(table.Validate("test")).
Pipe(stream.OnConflict(stream.New(table.Replace("test")))).
Pipe(table.Insert("test")).
Pipe(rows.Project(expr.Wildcard{})),
false},
{"Values / ON CONFLICT BLA", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT BLA RETURNING *",
nil, true},
{"Values / ON CONFLICT DO BLA", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT DO BLA RETURNING *",
nil, true},
{"Select / Without fields", "INSERT INTO test SELECT * FROM foo",
stream.New(table.Scan("foo")).
Pipe(rows.Project(expr.Wildcard{})).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Select / Without fields / With projection", "INSERT INTO test SELECT a, b FROM foo",
stream.New(table.Scan("foo")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "a"), testutil.ParseNamedExpr(t, "b"))).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Select / With fields", "INSERT INTO test (a, b) SELECT * FROM foo",
stream.New(table.Scan("foo")).
Pipe(rows.Project(expr.Wildcard{})).
Pipe(path.PathsRename("a", "b")).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Select / With fields / With projection", "INSERT INTO test (a, b) SELECT a, b FROM foo",
stream.New(table.Scan("foo")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "a"), testutil.ParseNamedExpr(t, "b"))).
Pipe(path.PathsRename("a", "b")).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Select / With fields / With projection / different fields", "INSERT INTO test (a, b) SELECT c, d FROM foo",
stream.New(table.Scan("foo")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
Pipe(path.PathsRename("a", "b")).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(stream.Discard()),
false},
{"Select / With fields / With projection / different fields / Returning", "INSERT INTO test (a, b) SELECT c, d FROM foo RETURNING a",
stream.New(table.Scan("foo")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
Pipe(path.PathsRename("a", "b")).
Pipe(table.Validate("test")).
Pipe(table.Insert("test")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "a"))),
false},
{"Select / With fields / With projection / different fields / On conflict / Returning", "INSERT INTO test (a, b) SELECT c, d FROM foo ON CONFLICT DO NOTHING RETURNING a",
stream.New(table.Scan("foo")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
Pipe(path.PathsRename("a", "b")).
Pipe(table.Validate("test")).
Pipe(stream.OnConflict(nil)).
Pipe(table.Insert("test")).
Pipe(rows.Project(testutil.ParseNamedExpr(t, "a"))),
false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
db := testutil.NewTestDB(t)
testutil.MustExec(t, db, nil, "CREATE TABLE test(a TEXT, b TEXT); CREATE TABLE foo(c TEXT, d TEXT);")
q, err := parser.ParseQuery(test.s)
if test.fails {
assert.Error(t, err)
return
}
assert.NoError(t, err)
err = q.Prepare(&query.Context{
Ctx: context.Background(),
DB: db,
})
assert.NoError(t, err)
require.Len(t, q.Statements, 1)
require.Equal(t, test.expected.String(), q.Statements[0].(*statement.PreparedStreamStmt).Stream.String())
})
}
}