mirror of
https://github.com/chaisql/chai.git
synced 2026-04-22 23:07:05 +08:00
Add support for ON CONFLICT clause
By default, if there is a conflict when inserting a document, we abort the current statement. Are considered conflicts any error returned by duplicate primary keys or unique constraint violations. The ON CONFLICT clause only supports the DO NOTHING action, which returns no error.
This commit is contained in:
@@ -3,6 +3,7 @@ package parser_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/genjidb/genji/internal/database"
|
||||
"github.com/genjidb/genji/internal/expr"
|
||||
"github.com/genjidb/genji/internal/query"
|
||||
"github.com/genjidb/genji/internal/sql/parser"
|
||||
@@ -29,7 +30,7 @@ func TestParserInsert(t *testing.T) {
|
||||
{K: "f", V: testutil.TextValue("baz")},
|
||||
}}},
|
||||
}},
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Documents / Multiple", `INSERT INTO test VALUES {"a": 'a', b: -2.3}, {a: 1, d: true}`,
|
||||
stream.New(stream.Expressions(
|
||||
@@ -38,19 +39,19 @@ func TestParserInsert(t *testing.T) {
|
||||
{K: "b", V: testutil.DoubleValue(-2.3)},
|
||||
}},
|
||||
&expr.KVPairs{SelfReferenced: true, Pairs: []expr.KVPair{{K: "a", V: testutil.IntegerValue(1)}, {K: "d", V: testutil.BoolValue(true)}}},
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Documents / Positional Param", "INSERT INTO test VALUES ?, ?",
|
||||
stream.New(stream.Expressions(
|
||||
expr.PositionalParam(1),
|
||||
expr.PositionalParam(2),
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Documents / Named Param", "INSERT INTO test VALUES $foo, $bar",
|
||||
stream.New(stream.Expressions(
|
||||
expr.NamedParam("foo"),
|
||||
expr.NamedParam("bar"),
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Values / With fields", "INSERT INTO test (a, b) VALUES ('c', 'd')",
|
||||
stream.New(stream.Expressions(
|
||||
@@ -58,7 +59,7 @@ func TestParserInsert(t *testing.T) {
|
||||
{K: "a", V: testutil.TextValue("c")},
|
||||
{K: "b", V: testutil.TextValue("d")},
|
||||
}},
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Values / With too many values", "INSERT INTO test (a, b) VALUES ('c', 'd', 'e')",
|
||||
nil, true},
|
||||
@@ -72,7 +73,7 @@ func TestParserInsert(t *testing.T) {
|
||||
{K: "a", V: testutil.TextValue("e")},
|
||||
{K: "b", V: testutil.TextValue("f")},
|
||||
}},
|
||||
)).Pipe(stream.TableInsert("test")),
|
||||
)).Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Values / Returning", "INSERT INTO test (a, b) VALUES ('c', 'd') RETURNING *, a, b as B, c",
|
||||
stream.New(stream.Expressions(
|
||||
@@ -80,46 +81,62 @@ func TestParserInsert(t *testing.T) {
|
||||
{K: "a", V: testutil.TextValue("c")},
|
||||
{K: "b", V: testutil.TextValue("d")},
|
||||
}},
|
||||
)).Pipe(stream.TableInsert("test")).
|
||||
)).Pipe(stream.TableInsert("test", nil)).
|
||||
Pipe(stream.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", "INSERT INTO test (a, b) VALUES ('c', 'd') ON CONFLICT DO NOTHING RETURNING *",
|
||||
stream.New(stream.Expressions(
|
||||
&expr.KVPairs{Pairs: []expr.KVPair{
|
||||
{K: "a", V: testutil.TextValue("c")},
|
||||
{K: "b", V: testutil.TextValue("d")},
|
||||
}},
|
||||
)).Pipe(stream.TableInsert("test", database.OnInsertConflictDoNothing)).
|
||||
Pipe(stream.Project(expr.Wildcard{})),
|
||||
false},
|
||||
{"Select / Without fields", "INSERT INTO test SELECT * FROM foo",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(expr.Wildcard{})).
|
||||
Pipe(stream.TableInsert("test")),
|
||||
Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Select / Without fields / With projection", "INSERT INTO test SELECT a, b FROM foo",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "a"), testutil.ParseNamedExpr(t, "b"))).
|
||||
Pipe(stream.TableInsert("test")),
|
||||
Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Select / With fields", "INSERT INTO test (a, b) SELECT * FROM foo",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(expr.Wildcard{})).
|
||||
Pipe(stream.IterRename("a", "b")).
|
||||
Pipe(stream.TableInsert("test")),
|
||||
Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Select / With fields / With projection", "INSERT INTO test (a, b) SELECT a, b FROM foo",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "a"), testutil.ParseNamedExpr(t, "b"))).
|
||||
Pipe(stream.IterRename("a", "b")).
|
||||
Pipe(stream.TableInsert("test")),
|
||||
Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Select / With fields / With projection / different fields", "INSERT INTO test (a, b) SELECT c, d FROM foo",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
|
||||
Pipe(stream.IterRename("a", "b")).
|
||||
Pipe(stream.TableInsert("test")),
|
||||
Pipe(stream.TableInsert("test", nil)),
|
||||
false},
|
||||
{"Select / With fields / With projection / different fields / Returning", "INSERT INTO test (a, b) SELECT c, d FROM foo RETURNING a",
|
||||
stream.New(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
|
||||
Pipe(stream.IterRename("a", "b")).
|
||||
Pipe(stream.TableInsert("test")).
|
||||
Pipe(stream.TableInsert("test", nil)).
|
||||
Pipe(stream.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(stream.SeqScan("foo")).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "c"), testutil.ParseNamedExpr(t, "d"))).
|
||||
Pipe(stream.IterRename("a", "b")).
|
||||
Pipe(stream.TableInsert("test", database.OnInsertConflictDoNothing)).
|
||||
Pipe(stream.Project(testutil.ParseNamedExpr(t, "a"))),
|
||||
false},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user