diff --git a/.czrc b/.czrc new file mode 100644 index 0000000000..d1bcc209ca --- /dev/null +++ b/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/embedded/sql/catalog.go b/embedded/sql/catalog.go index cfc2e75c3d..c0a42a21ca 100644 --- a/embedded/sql/catalog.go +++ b/embedded/sql/catalog.go @@ -29,17 +29,17 @@ type Database struct { } type Table struct { - db *Database - id uint32 - name string - cols []*Column - colsByID map[uint32]*Column - colsByName map[string]*Column - indexes map[string]*Index - indexesByColID map[uint32][]*Index - primaryIndex *Index - autoIncrementPK bool - maxPK int64 + db *Database + id uint32 + name string + cols []*Column + colsByID map[uint32]*Column + colsByName map[string]*Column + indexes map[string]*Index + indexesByColID map[uint32][]*Index + primaryIndex *Index + autoIncrement *Index + maxPK int64 } type Index struct { @@ -224,6 +224,26 @@ func (t *Table) GetColumnByID(id uint32) (*Column, error) { return col, nil } +func (t *Table) IsAutoIncremented() bool { + for _, c := range t.cols { + if c.autoIncrement { + return true + } + } + + return false +} + +func (t *Table) GetAutoIncrementedColumn() (cols []*Column) { + for _, c := range t.cols { + if c.autoIncrement { + cols = append(cols, c) + } + } + + return +} + func (i *Index) IsPrimary() bool { return i.id == PKIndexID } @@ -300,7 +320,7 @@ func (db *Database) newTable(name string, colsSpec []*ColSpec) (table *Table, er } if cs.autoIncrement && cs.colType != IntegerType { - return nil, ErrLimitedAutoIncrement + return nil, ErrAutoIncrementWrongType } if !validMaxLenForType(cs.maxLen, cs.colType) { @@ -374,13 +394,23 @@ func (t *Table) newIndex(unique bool, colIDs []uint32) (index *Index, err error) // having a direct way to get the indexes by colID for _, col := range index.cols { t.indexesByColID[col.id] = append(t.indexesByColID[col.id], index) + + if col.autoIncrement { + // cannot technically happen if the table creation, was properly handled. But it's better to handle the error + // TODO: support multi column auto_increment + if len(t.GetAutoIncrementedColumn()) > 1 { + return nil, ErrAutoIncrementMultiple + } + + t.autoIncrement = index + } } if index.id == PKIndexID { t.primaryIndex = index - t.autoIncrementPK = len(index.cols) == 1 && index.cols[0].autoIncrement } + return index, nil } diff --git a/embedded/sql/engine.go b/embedded/sql/engine.go index 4694d519ea..163ad31d49 100644 --- a/embedded/sql/engine.go +++ b/embedded/sql/engine.go @@ -39,7 +39,9 @@ var ErrTableDoesNotExist = errors.New("table does not exist") var ErrColumnDoesNotExist = errors.New("column does not exist") var ErrColumnNotIndexed = errors.New("column is not indexed") var ErrLimitedKeyType = errors.New("indexed key of invalid type. Supported types are: INTEGER, VARCHAR[256] OR BLOB[256]") -var ErrLimitedAutoIncrement = errors.New("only INTEGER single-column primary keys can be set as auto incremental") +var ErrTableNotAutoIncremented = errors.New("the table has no auto incremented column") +var ErrAutoIncrementWrongType = errors.New("auto incremented column need to be INTEGER type") +var ErrAutoIncrementMultiple = errors.New("several auto incremental column were found. Wrong schema") var ErrNoValueForAutoIncrementalColumn = errors.New("no value should be specified for auto incremental columns") var ErrLimitedMaxLen = errors.New("only VARCHAR and BLOB types support max length") var ErrDuplicatedColumn = errors.New("duplicated column") @@ -387,7 +389,7 @@ func (db *Database) loadTables(sqlPrefix []byte, tx *store.OngoingTx) error { return err } - if table.autoIncrementPK { + if table.IsAutoIncremented() { encMaxPK, err := loadMaxPK(sqlPrefix, tx, table) if err == store.ErrNoMoreEntries { continue @@ -426,7 +428,7 @@ func indexKeyFrom(cols []*Column) string { func loadMaxPK(sqlPrefix []byte, tx *store.OngoingTx, table *Table) ([]byte, error) { pkReaderSpec := &store.KeyReaderSpec{ - Prefix: mapKey(sqlPrefix, PIndexPrefix, EncodeID(table.db.id), EncodeID(table.id), EncodeID(PKIndexID)), + Prefix: mapKey(sqlPrefix, table.autoIncrement.prefix(), EncodeID(table.db.id), EncodeID(table.id), EncodeID(table.autoIncrement.id)), DescOrder: true, } @@ -441,7 +443,7 @@ func loadMaxPK(sqlPrefix []byte, tx *store.OngoingTx, table *Table) ([]byte, err return nil, err } - return unmapIndexEntry(table.primaryIndex, sqlPrefix, mkey) + return unmapIndexEntry(table.autoIncrement, sqlPrefix, mkey) } func loadColSpecs(dbID, tableID uint32, tx *store.OngoingTx, sqlPrefix []byte) (specs []*ColSpec, err error) { @@ -693,7 +695,7 @@ func unmapIndexEntry(index *Index, sqlPrefix, mkey []byte) (encPKVals []byte, er return nil, ErrCorruptedData } - if !index.IsPrimary() { + if !index.IsPrimary() && !index.table.IsAutoIncremented() { //read index values for _, col := range index.cols { if enc[off] == KeyValPrefixNull { diff --git a/embedded/sql/engine_test.go b/embedded/sql/engine_test.go index 7cd6942bb3..08e92a4978 100644 --- a/embedded/sql/engine_test.go +++ b/embedded/sql/engine_test.go @@ -597,6 +597,18 @@ func TestInsertIntoEdgeCases(t *testing.T) { _, _, err = engine.Exec("INSERT INTO table1 (id, title, active, payload) VALUES (1, 'title1', true, x'00A1')", nil, nil) require.NoError(t, err) + t.Run("on conflict cases", func(t *testing.T) { + _, _, err = engine.Exec("INSERT INTO table1 (id, title, active, payload) VALUES (1, 'title1', true, x'00A1')", nil, nil) + require.ErrorIs(t, err, store.ErrKeyAlreadyExists) + + ntx, ctxs, err := engine.Exec("INSERT INTO table1 (id, title, active, payload) VALUES (1, 'title1', true, x'00A1') ON CONFLICT DO NOTHING", nil, nil) + require.NoError(t, err) + require.Nil(t, ntx) + require.Len(t, ctxs, 1) + require.Zero(t, ctxs[0].UpdatedRows()) + require.Nil(t, ctxs[0].TxHeader()) + }) + t.Run("varchar key cases", func(t *testing.T) { _, _, err = engine.Exec("INSERT INTO table1 (id, title, active, payload) VALUES (2, 'title123456789', true, x'00A1')", nil, nil) require.ErrorIs(t, err, ErrMaxLengthExceeded) @@ -617,6 +629,14 @@ func TestInsertIntoEdgeCases(t *testing.T) { _, _, err = engine.Exec("INSERT INTO table1 (id, title, active, payload) VALUES (2, 'title1', true, '00A100A2')", nil, nil) require.ErrorIs(t, err, ErrInvalidValue) }) + + t.Run("insertion in table with varchar pk", func(t *testing.T) { + _, _, err = engine.Exec("CREATE TABLE languages (code VARCHAR[255],name VARCHAR[255],PRIMARY KEY code)", nil, nil) + require.NoError(t, err) + + _, _, err = engine.Exec("INSERT INTO languages (code,name) VALUES ('code1', 'name1')", nil, nil) + require.NoError(t, err) + }) } func TestAutoIncrementPK(t *testing.T) { @@ -633,18 +653,12 @@ func TestAutoIncrementPK(t *testing.T) { err = engine.SetDefaultDatabase("db1") require.NoError(t, err) - t.Run("invalid use of auto-increment", func(t *testing.T) { - _, _, err = engine.Exec("CREATE TABLE table1 (id INTEGER, title VARCHAR AUTO_INCREMENT, PRIMARY KEY id)", nil, nil) - require.ErrorIs(t, err, ErrLimitedAutoIncrement) - - _, _, err = engine.Exec("CREATE TABLE table1 (id INTEGER, title VARCHAR, age INTEGER AUTO_INCREMENT, PRIMARY KEY id)", nil, nil) - require.ErrorIs(t, err, ErrLimitedAutoIncrement) - + t.Run("wrong auto-increment type", func(t *testing.T) { _, _, err = engine.Exec("CREATE TABLE table1 (id VARCHAR AUTO_INCREMENT, title VARCHAR, PRIMARY KEY id)", nil, nil) - require.ErrorIs(t, err, ErrLimitedAutoIncrement) + require.ErrorIs(t, err, ErrAutoIncrementWrongType) }) - _, _, err = engine.Exec("CREATE TABLE table1 (id INTEGER AUTO_INCREMENT, title VARCHAR, PRIMARY KEY id)", nil, nil) + _, _, err = engine.Exec("CREATE TABLE table1 (id INTEGER NOT NULL AUTO_INCREMENT, title VARCHAR, PRIMARY KEY id)", nil, nil) require.NoError(t, err) _, ctxs, err := engine.Exec("INSERT INTO table1(title) VALUES ('name1')", nil, nil) @@ -654,26 +668,88 @@ func TestAutoIncrementPK(t *testing.T) { require.Equal(t, int64(1), ctxs[0].LastInsertedPKs()["table1"]) require.Equal(t, 1, ctxs[0].UpdatedRows()) - _, _, err = engine.Exec("INSERT INTO table1(id, title) VALUES (2, 'name2')", nil, nil) - require.ErrorIs(t, err, ErrNoValueForAutoIncrementalColumn) + _, _, err = engine.Exec("INSERT INTO table1(id, title) VALUES (1, 'name2')", nil, nil) + require.ErrorIs(t, err, store.ErrKeyAlreadyExists) - _, _, err = engine.Exec("UPSERT INTO table1(id, title) VALUES (2, 'name2')", nil, nil) - require.ErrorIs(t, err, store.ErrKeyNotFound) + _, _, err = engine.Exec("INSERT INTO table1(id, title) VALUES (1, 'name2') ON CONFLICT DO NOTHING", nil, nil) + require.NoError(t, err) _, _, err = engine.Exec("UPSERT INTO table1(id, title) VALUES (1, 'name11')", nil, nil) require.NoError(t, err) - _, ctxs, err = engine.Exec("INSERT INTO table1(title) VALUES ('name2')", nil, nil) + _, _, err = engine.Exec("INSERT INTO table1(id, title) VALUES (2, 'name2')", nil, nil) + require.NoError(t, err) + + _, _, err = engine.Exec("UPSERT INTO table1(id, title) VALUES (3, 'name3')", nil, nil) + require.NoError(t, err) + + _, ctxs, err = engine.Exec("INSERT INTO table1(title) VALUES ('name4')", nil, nil) + require.NoError(t, err) + require.Len(t, ctxs, 1) + require.True(t, ctxs[0].closed) + require.Equal(t, int64(4), ctxs[0].LastInsertedPKs()["table1"]) + require.Equal(t, 1, ctxs[0].UpdatedRows()) + + _, ctxs, err = engine.Exec(` + BEGIN TRANSACTION; + INSERT INTO table1(title) VALUES ('name5'); + INSERT INTO table1(title) VALUES ('name6'); + COMMIT; + `, nil, nil) + require.NoError(t, err) + require.Len(t, ctxs, 1) + require.True(t, ctxs[0].closed) + require.Equal(t, int64(6), ctxs[0].LastInsertedPKs()["table1"]) + require.Equal(t, 2, ctxs[0].UpdatedRows()) +} + +func TestAutoIncrementUniqueIndex(t *testing.T) { + st, err := store.Open("sqldata_s_auto_inc", store.DefaultOptions()) + require.NoError(t, err) + defer os.RemoveAll("sqldata_s_auto_inc") + + engine, err := NewEngine(st, DefaultOptions().WithPrefix(sqlPrefix)) + require.NoError(t, err) + + _, _, err = engine.Exec("CREATE DATABASE db1", nil, nil) + require.NoError(t, err) + + err = engine.SetDefaultDatabase("db1") + require.NoError(t, err) + + t.Run("invalid use of auto-increment", func(t *testing.T) { + _, _, err := engine.Exec("CREATE TABLE table1 (uid VARCHAR[256], number VARCHAR AUTO_INCREMENT, name VARCHAR, PRIMARY KEY uid)", nil, nil) + require.ErrorIs(t, err, ErrAutoIncrementWrongType) + + _, _, err = engine.Exec("CREATE TABLE table1 (id INTEGER AUTO_INCREMENT, number INTEGER AUTO_INCREMENT, name VARCHAR, PRIMARY KEY id)", nil, nil) + require.ErrorIs(t, err, ErrAutoIncrementMultiple) + }) + + _, _, err = engine.Exec("CREATE TABLE table1 (uid VARCHAR[256], number INTEGER AUTO_INCREMENT, name VARCHAR, PRIMARY KEY uid)", nil, nil) + require.NoError(t, err) + + _, ctxs, err := engine.Exec("INSERT INTO table1(uid, name) VALUES ('AE1R', 'Jhon')", nil, nil) + require.NoError(t, err) + require.Len(t, ctxs, 1) + require.True(t, ctxs[0].closed) + require.Equal(t, int64(1), ctxs[0].LastInsertedPKs()["table1"]) + require.Equal(t, 1, ctxs[0].UpdatedRows()) + + _, ctxs, err = engine.Exec("UPSERT INTO table1(uid, name) VALUES ('AE1R', 'Ben')", nil, nil) + require.NoError(t, err) + + _, ctxs, err = engine.Exec("INSERT INTO table1(uid, name) VALUES ('BEKZ', 'Marc')", nil, nil) require.NoError(t, err) require.Len(t, ctxs, 1) require.True(t, ctxs[0].closed) require.Equal(t, int64(2), ctxs[0].LastInsertedPKs()["table1"]) require.Equal(t, 1, ctxs[0].UpdatedRows()) + _, ctxs, err = engine.Exec(` BEGIN TRANSACTION; - INSERT INTO table1(title) VALUES ('name3'); - INSERT INTO table1(title) VALUES ('name4'); + INSERT INTO table1(uid, name) VALUES ('VBKZ', 'name3'); + INSERT INTO table1(uid, name) VALUES ('FZKB', 'name4'); COMMIT; `, nil, nil) require.NoError(t, err) diff --git a/embedded/sql/parser.go b/embedded/sql/parser.go index 593fc434c6..f6ae3731a2 100644 --- a/embedded/sql/parser.go +++ b/embedded/sql/parser.go @@ -46,6 +46,9 @@ var reservedWords = map[string]int{ "ADD": ADD, "COLUMN": COLUMN, "INSERT": INSERT, + "CONFLICT": CONFLICT, + "DO": DO, + "NOTHING": NOTHING, "UPSERT": UPSERT, "INTO": INTO, "VALUES": VALUES, diff --git a/embedded/sql/sql_grammar.y b/embedded/sql/sql_grammar.y index 5b2e95c439..195af42e5f 100644 --- a/embedded/sql/sql_grammar.y +++ b/embedded/sql/sql_grammar.y @@ -61,11 +61,12 @@ func setResult(l yyLexer, stmts []SQLStmt) { pparam int update *colUpdate updates []*colUpdate + onConflict *OnConflictDo } %token CREATE USE DATABASE SNAPSHOT SINCE UP TO TABLE UNIQUE INDEX ON ALTER ADD COLUMN PRIMARY KEY %token BEGIN TRANSACTION COMMIT ROLLBACK -%token INSERT UPSERT INTO VALUES DELETE UPDATE SET +%token INSERT UPSERT INTO VALUES DELETE UPDATE SET CONFLICT DO NOTHING %token SELECT DISTINCT FROM BEFORE TX JOIN HAVING WHERE GROUP BY LIMIT ORDER ASC DESC AS %token NOT LIKE IF EXISTS IN IS %token AUTO_INCREMENT NULL NPARAM CAST @@ -125,6 +126,7 @@ func setResult(l yyLexer, stmts []SQLStmt) { %type opt_if_not_exists opt_auto_increment opt_not_null opt_not %type update %type updates +%type opt_on_conflict %start sql @@ -234,9 +236,9 @@ one_or_more_ids: } dmlstmt: - INSERT INTO tableRef '(' opt_ids ')' VALUES rows + INSERT INTO tableRef '(' opt_ids ')' VALUES rows opt_on_conflict { - $$ = &UpsertIntoStmt{isInsert: true, tableRef: $3, cols: $5, rows: $8} + $$ = &UpsertIntoStmt{isInsert: true, tableRef: $3, cols: $5, rows: $8, onConflict: $9} } | UPSERT INTO tableRef '(' ids ')' VALUES rows @@ -254,6 +256,16 @@ dmlstmt: $$ = &UpdateStmt{tableRef: $2, updates: $4, where: $5, indexOn: $6, limit: int($7)} } +opt_on_conflict: + { + $$ = nil + } +| + ON CONFLICT DO NOTHING + { + $$ = &OnConflictDo{} + } + updates: update { @@ -399,9 +411,9 @@ colsSpec: } colSpec: - IDENTIFIER TYPE opt_max_len opt_auto_increment opt_not_null + IDENTIFIER TYPE opt_max_len opt_not_null opt_auto_increment { - $$ = &ColSpec{colName: $1, colType: $2, maxLen: int($3), autoIncrement: $4, notNull: $5} + $$ = &ColSpec{colName: $1, colType: $2, maxLen: int($3), notNull: $4, autoIncrement: $5} } opt_max_len: diff --git a/embedded/sql/sql_parser.go b/embedded/sql/sql_parser.go index becdf644ce..9ebe5b4333 100644 --- a/embedded/sql/sql_parser.go +++ b/embedded/sql/sql_parser.go @@ -10,43 +10,44 @@ func setResult(l yyLexer, stmts []SQLStmt) { } type yySymType struct { - yys int - stmts []SQLStmt - stmt SQLStmt - colsSpec []*ColSpec - colSpec *ColSpec - cols []*ColSelector - rows []*RowSpec - row *RowSpec - values []ValueExp - value ValueExp - id string - number uint64 - str string - boolean bool - blob []byte - sqlType SQLValueType - aggFn AggregateFn - ids []string - col *ColSelector - sel Selector - sels []Selector - distinct bool - ds DataSource - tableRef *tableRef - joins []*JoinSpec - join *JoinSpec - joinType JoinType - exp ValueExp - binExp ValueExp - err error - ordcols []*OrdCol - opt_ord bool - logicOp LogicOperator - cmpOp CmpOperator - pparam int - update *colUpdate - updates []*colUpdate + yys int + stmts []SQLStmt + stmt SQLStmt + colsSpec []*ColSpec + colSpec *ColSpec + cols []*ColSelector + rows []*RowSpec + row *RowSpec + values []ValueExp + value ValueExp + id string + number uint64 + str string + boolean bool + blob []byte + sqlType SQLValueType + aggFn AggregateFn + ids []string + col *ColSelector + sel Selector + sels []Selector + distinct bool + ds DataSource + tableRef *tableRef + joins []*JoinSpec + join *JoinSpec + joinType JoinType + exp ValueExp + binExp ValueExp + err error + ordcols []*OrdCol + opt_ord bool + logicOp LogicOperator + cmpOp CmpOperator + pparam int + update *colUpdate + updates []*colUpdate + onConflict *OnConflictDo } const CREATE = 57346 @@ -76,44 +77,47 @@ const VALUES = 57369 const DELETE = 57370 const UPDATE = 57371 const SET = 57372 -const SELECT = 57373 -const DISTINCT = 57374 -const FROM = 57375 -const BEFORE = 57376 -const TX = 57377 -const JOIN = 57378 -const HAVING = 57379 -const WHERE = 57380 -const GROUP = 57381 -const BY = 57382 -const LIMIT = 57383 -const ORDER = 57384 -const ASC = 57385 -const DESC = 57386 -const AS = 57387 -const NOT = 57388 -const LIKE = 57389 -const IF = 57390 -const EXISTS = 57391 -const IN = 57392 -const IS = 57393 -const AUTO_INCREMENT = 57394 -const NULL = 57395 -const NPARAM = 57396 -const CAST = 57397 -const PPARAM = 57398 -const JOINTYPE = 57399 -const LOP = 57400 -const CMPOP = 57401 -const IDENTIFIER = 57402 -const TYPE = 57403 -const NUMBER = 57404 -const VARCHAR = 57405 -const BOOLEAN = 57406 -const BLOB = 57407 -const AGGREGATE_FUNC = 57408 -const ERROR = 57409 -const STMT_SEPARATOR = 57410 +const CONFLICT = 57373 +const DO = 57374 +const NOTHING = 57375 +const SELECT = 57376 +const DISTINCT = 57377 +const FROM = 57378 +const BEFORE = 57379 +const TX = 57380 +const JOIN = 57381 +const HAVING = 57382 +const WHERE = 57383 +const GROUP = 57384 +const BY = 57385 +const LIMIT = 57386 +const ORDER = 57387 +const ASC = 57388 +const DESC = 57389 +const AS = 57390 +const NOT = 57391 +const LIKE = 57392 +const IF = 57393 +const EXISTS = 57394 +const IN = 57395 +const IS = 57396 +const AUTO_INCREMENT = 57397 +const NULL = 57398 +const NPARAM = 57399 +const CAST = 57400 +const PPARAM = 57401 +const JOINTYPE = 57402 +const LOP = 57403 +const CMPOP = 57404 +const IDENTIFIER = 57405 +const TYPE = 57406 +const NUMBER = 57407 +const VARCHAR = 57408 +const BOOLEAN = 57409 +const BLOB = 57410 +const AGGREGATE_FUNC = 57411 +const ERROR = 57412 +const STMT_SEPARATOR = 57413 var yyToknames = [...]string{ "$end", @@ -146,6 +150,9 @@ var yyToknames = [...]string{ "DELETE", "UPDATE", "SET", + "CONFLICT", + "DO", + "NOTHING", "SELECT", "DISTINCT", "FROM", @@ -207,188 +214,192 @@ var yyExca = [...]int{ 1, -1, -2, 0, -1, 94, - 47, 121, - 50, 121, - -2, 110, + 50, 123, + 53, 123, + -2, 112, -1, 154, - 36, 88, - -2, 83, - -1, 187, - 36, 88, + 39, 90, -2, 85, + -1, 187, + 39, 90, + -2, 87, } const yyPrivate = 57344 -const yyLast = 327 +const yyLast = 333 var yyAct = [...]int{ - 223, 264, 54, 132, 91, 200, 203, 114, 222, 6, - 75, 186, 67, 199, 123, 61, 70, 88, 17, 235, - 238, 130, 130, 130, 196, 247, 130, 242, 241, 239, - 219, 197, 240, 96, 131, 237, 98, 209, 191, 204, + 226, 269, 54, 132, 91, 200, 203, 114, 225, 6, + 75, 186, 67, 199, 123, 61, 70, 88, 17, 238, + 242, 130, 130, 130, 196, 251, 130, 246, 245, 243, + 220, 197, 244, 96, 131, 241, 98, 209, 191, 204, 110, 108, 106, 109, 183, 32, 159, 107, 158, 102, 103, 104, 105, 55, 205, 201, 96, 97, 129, 98, 116, 208, 101, 110, 108, 106, 109, 164, 148, 93, 107, 141, 102, 103, 104, 105, 55, 146, 139, 140, - 97, 120, 111, 99, 90, 101, 141, 125, 80, 135, - 136, 138, 137, 139, 140, 19, 181, 144, 145, 78, - 66, 128, 147, 65, 135, 136, 138, 137, 218, 79, - 160, 149, 79, 49, 153, 263, 151, 53, 258, 154, - 238, 210, 220, 161, 130, 74, 156, 141, 157, 152, - 168, 155, 127, 141, 139, 140, 170, 171, 172, 173, - 174, 175, 141, 163, 141, 135, 136, 138, 137, 182, - 140, 135, 136, 138, 137, 184, 180, 56, 117, 68, - 135, 136, 138, 137, 138, 137, 190, 56, 119, 56, - 85, 227, 77, 55, 193, 55, 194, 162, 51, 207, - 56, 202, 198, 89, 192, 166, 71, 76, 150, 112, - 124, 126, 121, 118, 82, 72, 57, 32, 211, 212, - 44, 41, 214, 36, 113, 189, 234, 177, 248, 141, - 206, 217, 178, 233, 176, 179, 124, 226, 225, 81, - 38, 230, 231, 224, 143, 58, 236, 265, 266, 251, - 133, 37, 257, 245, 229, 246, 68, 244, 213, 249, - 84, 63, 62, 73, 30, 252, 34, 17, 254, 48, - 167, 165, 29, 28, 256, 39, 259, 10, 11, 20, - 261, 262, 115, 2, 215, 86, 267, 64, 12, 268, - 255, 169, 60, 7, 83, 8, 9, 13, 14, 31, - 21, 15, 16, 35, 17, 22, 24, 23, 43, 59, - 134, 45, 46, 47, 40, 27, 25, 26, 92, 18, - 69, 142, 232, 216, 250, 260, 195, 228, 95, 94, - 243, 188, 187, 185, 42, 33, 52, 50, 100, 221, - 253, 87, 122, 5, 4, 3, 1, + 97, 120, 111, 125, 90, 101, 141, 80, 78, 135, + 136, 138, 137, 139, 140, 19, 181, 144, 145, 66, + 65, 128, 147, 141, 135, 136, 138, 137, 219, 79, + 160, 149, 79, 49, 153, 223, 151, 268, 263, 154, + 168, 210, 242, 138, 137, 99, 156, 141, 157, 152, + 222, 155, 161, 141, 139, 140, 170, 171, 172, 173, + 174, 175, 141, 163, 68, 135, 136, 138, 137, 182, + 140, 135, 136, 138, 137, 184, 180, 56, 56, 53, + 135, 136, 138, 137, 55, 56, 190, 130, 119, 51, + 74, 55, 222, 127, 112, 85, 194, 230, 77, 207, + 193, 202, 198, 162, 56, 89, 192, 166, 71, 150, + 124, 126, 121, 76, 118, 82, 72, 57, 211, 212, + 117, 32, 214, 44, 41, 36, 113, 189, 218, 177, + 237, 206, 236, 81, 38, 217, 176, 229, 228, 141, + 143, 233, 234, 227, 178, 124, 58, 179, 239, 270, + 271, 255, 133, 262, 249, 232, 68, 248, 250, 213, + 84, 63, 62, 253, 73, 30, 37, 34, 17, 256, + 260, 252, 258, 240, 48, 167, 165, 29, 261, 115, + 264, 10, 11, 28, 20, 266, 267, 215, 86, 64, + 39, 272, 12, 2, 273, 259, 31, 7, 169, 8, + 9, 13, 14, 83, 59, 15, 16, 60, 45, 46, + 47, 17, 21, 35, 134, 40, 27, 22, 24, 23, + 43, 25, 26, 92, 18, 221, 69, 142, 216, 235, + 254, 265, 195, 231, 95, 94, 247, 188, 187, 185, + 42, 33, 52, 50, 100, 224, 257, 87, 122, 5, + 4, 3, 1, } var yyPact = [...]int{ - 253, -1000, -1000, 21, -1000, -1000, -1000, 238, -1000, -1000, - 274, 290, 284, 227, 226, 211, 137, 214, -1000, 253, - -1000, 143, 172, 172, 281, 141, 280, 140, 137, 137, - 137, 219, 40, 107, -1000, -1000, -1000, 136, 179, 275, - 172, -1000, 208, 206, 251, 28, 25, 198, 126, 135, - 210, -1000, 57, 127, -1000, 24, 39, 13, 170, 134, - 260, -1000, 205, 108, 248, 123, 123, 293, 10, 121, - -1000, 145, -1000, -15, 109, -1000, -1000, 133, 97, 132, - 130, -1000, 12, 131, 70, -1000, 130, -18, 56, -1000, - -42, 189, 277, 35, 178, -1000, 10, 10, 2, -1000, - -1000, 10, -1000, -1000, -1000, -1000, -7, 36, 128, -1000, - -1000, 293, 126, 10, 293, 208, 216, 127, -1000, -28, - -30, 37, 55, -1000, 116, 123, -8, -1000, -1000, 224, - 125, 223, -1000, 68, 257, 10, 10, 10, 10, 10, - 10, 161, 165, -1000, 91, 93, 216, 20, 10, -32, - -1000, 189, -1000, 35, 148, 127, -38, -1000, -1000, -1000, - 124, 156, -53, -45, 123, -20, -1000, -20, -1000, -21, - 93, 93, 158, 158, 91, 82, -1000, 157, 10, -14, - -39, -1000, 76, -1000, -1000, 198, -1000, 148, 202, -1000, - -1000, 127, -1000, 245, -1000, 159, 46, -1000, -46, 54, - -1000, 10, 54, -1000, -1000, 123, -1000, 91, -13, -1000, - 110, 195, -1000, -15, -1000, -21, 160, -1000, -59, -1000, - -20, -41, 52, 35, -47, -44, -48, -49, 200, 193, - 293, -51, -1000, -1000, 155, -1000, -1000, -1000, 10, -1000, - -1000, -1000, -1000, 187, 10, 120, 256, -1000, -1000, 35, - 189, 192, 35, 50, -1000, 10, -1000, 120, 120, 35, - 47, 184, -1000, 120, -1000, -1000, -1000, 184, -1000, + 257, -1000, -1000, 18, -1000, -1000, -1000, 243, -1000, -1000, + 286, 295, 285, 237, 231, 209, 138, 212, -1000, 257, + -1000, 142, 163, 163, 282, 141, 292, 140, 138, 138, + 138, 224, 37, 95, -1000, -1000, -1000, 134, 177, 270, + 163, -1000, 205, 203, 253, 22, 21, 195, 125, 133, + 208, -1000, 99, 130, -1000, 10, 36, 9, 161, 132, + 269, -1000, 202, 110, 251, 122, 122, 298, 7, 103, + -1000, 144, -1000, -18, 102, -1000, -1000, 131, 94, 129, + 127, -1000, 5, 128, 108, -1000, 127, -21, 96, -1000, + -45, 188, 281, 32, 171, -1000, 7, 7, -1, -1000, + -1000, 7, -1000, -1000, -1000, -1000, -10, 33, 126, -1000, + -1000, 298, 125, 7, 298, 205, 214, 130, -1000, -31, + -33, 34, 61, -1000, 119, 122, -11, -1000, -1000, 229, + 124, 228, -1000, 55, 264, 7, 7, 7, 7, 7, + 7, 160, 174, -1000, 88, 49, 214, 17, 7, -35, + -1000, 188, -1000, 32, 147, 130, -41, -1000, -1000, -1000, + 123, 162, -56, -48, 122, -23, -1000, -23, -1000, -24, + 49, 49, 165, 165, 88, 79, -1000, 155, 7, -17, + -42, -1000, 73, -1000, -1000, 195, -1000, 147, 200, -1000, + -1000, 130, -1000, 248, -1000, 159, 43, -1000, -49, 101, + -1000, 7, 59, -1000, -1000, 122, -1000, 88, -16, -1000, + 113, 193, -1000, -18, -1000, -24, 157, -1000, 154, -62, + -1000, -1000, -23, 222, -44, 51, 32, -50, -47, -51, + -52, 197, 191, 298, -54, -1000, -1000, -1000, -1000, -1000, + 219, -1000, 7, -1000, -1000, -1000, -1000, 186, 7, 121, + 261, -1000, 217, 32, 188, 190, 32, 47, -1000, 7, + -1000, -1000, 121, 121, 32, 46, 183, -1000, 121, -1000, + -1000, -1000, 183, -1000, } var yyPgo = [...]int{ - 0, 326, 263, 325, 324, 9, 323, 322, 14, 17, - 6, 321, 320, 13, 5, 8, 319, 318, 83, 317, - 316, 2, 315, 7, 262, 314, 15, 313, 11, 312, - 311, 0, 12, 310, 309, 308, 307, 3, 306, 10, - 305, 304, 1, 4, 231, 303, 302, 301, 16, 300, - 299, + 0, 332, 273, 331, 330, 9, 329, 328, 14, 17, + 6, 327, 326, 13, 5, 8, 325, 324, 125, 323, + 322, 2, 321, 7, 259, 320, 15, 319, 11, 318, + 317, 0, 12, 316, 315, 314, 313, 3, 312, 10, + 311, 310, 1, 4, 246, 309, 308, 307, 16, 306, + 305, 304, } var yyR1 = [...]int{ - 0, 1, 2, 2, 50, 50, 3, 3, 3, 4, + 0, 1, 2, 2, 51, 51, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25, - 25, 44, 44, 10, 10, 6, 6, 6, 6, 49, - 49, 48, 11, 11, 13, 13, 14, 9, 9, 12, - 12, 16, 16, 15, 15, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 7, 7, 8, 38, 38, 45, - 45, 46, 46, 46, 5, 22, 22, 19, 19, 20, - 20, 18, 18, 18, 21, 21, 21, 23, 23, 24, - 24, 26, 26, 27, 27, 28, 28, 29, 30, 30, - 32, 32, 36, 36, 33, 33, 37, 37, 41, 41, - 43, 43, 40, 40, 42, 42, 42, 39, 39, 39, - 31, 31, 31, 31, 31, 31, 31, 31, 34, 34, - 34, 47, 47, 35, 35, 35, 35, 35, 35, 35, - 35, + 25, 44, 44, 10, 10, 6, 6, 6, 6, 50, + 50, 49, 49, 48, 11, 11, 13, 13, 14, 9, + 9, 12, 12, 16, 16, 15, 15, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 7, 7, 8, 38, + 38, 45, 45, 46, 46, 46, 5, 22, 22, 19, + 19, 20, 20, 18, 18, 18, 21, 21, 21, 23, + 23, 24, 24, 26, 26, 27, 27, 28, 28, 29, + 30, 30, 32, 32, 36, 36, 33, 33, 37, 37, + 41, 41, 43, 43, 40, 40, 42, 42, 42, 39, + 39, 39, 31, 31, 31, 31, 31, 31, 31, 31, + 34, 34, 34, 47, 47, 35, 35, 35, 35, 35, + 35, 35, 35, } var yyR2 = [...]int{ 0, 1, 2, 3, 0, 1, 1, 1, 1, 2, 1, 1, 3, 3, 4, 11, 8, 9, 6, 0, - 3, 0, 3, 1, 3, 8, 8, 6, 7, 1, - 3, 3, 0, 1, 1, 3, 3, 1, 3, 1, - 3, 0, 1, 1, 3, 1, 1, 1, 1, 6, - 3, 2, 1, 1, 1, 3, 5, 0, 3, 0, - 1, 0, 1, 2, 12, 0, 1, 1, 1, 2, - 4, 1, 4, 4, 1, 3, 5, 3, 4, 1, - 3, 0, 3, 0, 1, 1, 2, 6, 0, 1, - 0, 2, 0, 3, 0, 2, 0, 2, 0, 3, - 0, 4, 2, 4, 0, 1, 1, 0, 1, 2, - 1, 1, 2, 2, 4, 4, 6, 6, 1, 1, - 3, 0, 1, 3, 3, 3, 3, 3, 3, 3, - 4, + 3, 0, 3, 1, 3, 9, 8, 6, 7, 0, + 4, 1, 3, 3, 0, 1, 1, 3, 3, 1, + 3, 1, 3, 0, 1, 1, 3, 1, 1, 1, + 1, 6, 3, 2, 1, 1, 1, 3, 5, 0, + 3, 0, 1, 0, 1, 2, 12, 0, 1, 1, + 1, 2, 4, 1, 4, 4, 1, 3, 5, 3, + 4, 1, 3, 0, 3, 0, 1, 1, 2, 6, + 0, 1, 0, 2, 0, 3, 0, 2, 0, 2, + 0, 3, 0, 4, 2, 4, 0, 1, 1, 0, + 1, 2, 1, 1, 2, 2, 4, 4, 6, 6, + 1, 1, 3, 0, 1, 3, 3, 3, 3, 3, + 3, 3, 4, } var yyChk = [...]int{ -1000, -1, -2, -3, -4, -6, -5, 20, 22, 23, - 4, 5, 15, 24, 25, 28, 29, 31, -50, 74, + 4, 5, 15, 24, 25, 28, 29, 34, -51, 77, 21, 6, 11, 13, 12, 6, 7, 11, 26, 26, - 33, -24, 60, -22, 32, -2, 60, -44, 48, -44, - 13, 60, -25, 8, 60, -24, -24, -24, 30, 73, - -19, 71, -20, -18, -21, 66, 60, 60, 46, 14, - -44, -26, 34, 35, 16, 75, 75, -32, 38, -49, - -48, 60, 60, 33, 68, -39, 60, 45, 75, 73, - 75, 49, 60, 14, 35, 62, 17, -11, -9, 60, - -9, -43, 5, -31, -34, -35, 46, 70, 49, -18, - -17, 75, 62, 63, 64, 65, 55, 60, 54, 56, - 53, -32, 68, 59, -23, -24, 75, -18, 60, 71, - -21, 60, -7, -8, 60, 75, 60, 62, -8, 76, - 68, 76, -37, 41, 13, 69, 70, 72, 71, 58, - 59, 51, -47, 46, -31, -31, 75, -31, 75, 75, - 60, -43, -48, -31, -43, -26, -5, -39, 76, 76, - 73, 68, 61, -9, 75, 27, 60, 27, 62, 14, - -31, -31, -31, -31, -31, -31, 53, 46, 47, 50, - -5, 76, -31, 76, -37, -27, -28, -29, -30, 57, - -39, 76, 60, 18, -8, -38, 77, 76, -9, -13, - -14, 75, -13, -10, 60, 75, 53, -31, 75, 76, - 45, -32, -28, 36, -39, 19, -45, 52, 62, 76, - 68, -16, -15, -31, -9, -5, -15, 61, -36, 39, - -23, -10, -46, 53, 46, 78, -14, 76, 68, 76, - 76, 76, 76, -33, 37, 40, -43, 76, 53, -31, - -41, 42, -31, -12, -21, 14, -37, 40, 68, -31, - -40, -21, -21, 68, -42, 43, 44, -21, -42, + 36, -24, 63, -22, 35, -2, 63, -44, 51, -44, + 13, 63, -25, 8, 63, -24, -24, -24, 30, 76, + -19, 74, -20, -18, -21, 69, 63, 63, 49, 14, + -44, -26, 37, 38, 16, 78, 78, -32, 41, -49, + -48, 63, 63, 36, 71, -39, 63, 48, 78, 76, + 78, 52, 63, 14, 38, 65, 17, -11, -9, 63, + -9, -43, 5, -31, -34, -35, 49, 73, 52, -18, + -17, 78, 65, 66, 67, 68, 58, 63, 57, 59, + 56, -32, 71, 62, -23, -24, 78, -18, 63, 74, + -21, 63, -7, -8, 63, 78, 63, 65, -8, 79, + 71, 79, -37, 44, 13, 72, 73, 75, 74, 61, + 62, 54, -47, 49, -31, -31, 78, -31, 78, 78, + 63, -43, -48, -31, -43, -26, -5, -39, 79, 79, + 76, 71, 64, -9, 78, 27, 63, 27, 65, 14, + -31, -31, -31, -31, -31, -31, 56, 49, 50, 53, + -5, 79, -31, 79, -37, -27, -28, -29, -30, 60, + -39, 79, 63, 18, -8, -38, 80, 79, -9, -13, + -14, 78, -13, -10, 63, 78, 56, -31, 78, 79, + 48, -32, -28, 39, -39, 19, -46, 56, 49, 65, + 79, -50, 71, 14, -16, -15, -31, -9, -5, -15, + 64, -36, 42, -23, -10, -45, 55, 56, 81, -14, + 31, 79, 71, 79, 79, 79, 79, -33, 40, 43, + -43, 79, 32, -31, -41, 45, -31, -12, -21, 14, + 33, -37, 43, 71, -31, -40, -21, -21, 71, -42, + 46, 47, -21, -42, } var yyDef = [...]int{ 0, -2, 1, 4, 6, 7, 8, 0, 10, 11, - 0, 0, 0, 0, 0, 0, 0, 65, 2, 5, + 0, 0, 0, 0, 0, 0, 0, 67, 2, 5, 9, 0, 21, 21, 0, 0, 19, 0, 0, 0, - 0, 0, 79, 0, 66, 3, 12, 0, 0, 0, - 21, 13, 81, 0, 0, 0, 0, 90, 0, 0, - 0, 67, 68, 107, 71, 0, 74, 0, 0, 0, - 0, 14, 0, 0, 0, 32, 0, 100, 0, 90, - 29, 0, 80, 0, 0, 69, 108, 0, 0, 0, - 0, 22, 0, 0, 0, 20, 0, 0, 33, 37, - 0, 96, 0, 91, -2, 111, 0, 0, 0, 118, - 119, 0, 45, 46, 47, 48, 0, 74, 0, 52, - 53, 100, 0, 0, 100, 81, 0, 107, 109, 0, - 0, 75, 0, 54, 0, 0, 0, 82, 18, 0, + 0, 0, 81, 0, 68, 3, 12, 0, 0, 0, + 21, 13, 83, 0, 0, 0, 0, 92, 0, 0, + 0, 69, 70, 109, 73, 0, 76, 0, 0, 0, + 0, 14, 0, 0, 0, 34, 0, 102, 0, 92, + 31, 0, 82, 0, 0, 71, 110, 0, 0, 0, + 0, 22, 0, 0, 0, 20, 0, 0, 35, 39, + 0, 98, 0, 93, -2, 113, 0, 0, 0, 120, + 121, 0, 47, 48, 49, 50, 0, 76, 0, 54, + 55, 102, 0, 0, 102, 83, 0, 109, 111, 0, + 0, 77, 0, 56, 0, 0, 0, 84, 18, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 112, 113, 0, 0, 0, 0, - 51, 96, 30, 31, -2, 107, 0, 70, 72, 73, - 0, 0, 57, 0, 0, 0, 38, 0, 97, 0, - 123, 124, 125, 126, 127, 128, 129, 0, 0, 0, - 0, 120, 0, 50, 28, 90, 84, -2, 0, 89, - 77, 107, 76, 0, 55, 59, 0, 16, 0, 25, - 34, 41, 26, 101, 23, 0, 130, 114, 0, 115, - 0, 92, 86, 0, 78, 0, 61, 60, 0, 17, - 0, 0, 42, 43, 0, 0, 0, 0, 94, 0, - 100, 0, 56, 62, 0, 58, 35, 36, 0, 24, - 116, 117, 49, 98, 0, 0, 0, 15, 63, 44, - 96, 0, 95, 93, 39, 0, 64, 0, 0, 87, - 99, 104, 40, 0, 102, 105, 106, 104, 103, + 0, 0, 0, 124, 114, 115, 0, 0, 0, 0, + 53, 98, 32, 33, -2, 109, 0, 72, 74, 75, + 0, 0, 59, 0, 0, 0, 40, 0, 99, 0, + 125, 126, 127, 128, 129, 130, 131, 0, 0, 0, + 0, 122, 0, 52, 28, 92, 86, -2, 0, 91, + 79, 109, 78, 0, 57, 63, 0, 16, 0, 29, + 36, 43, 26, 103, 23, 0, 132, 116, 0, 117, + 0, 94, 88, 0, 80, 0, 61, 64, 0, 0, + 17, 25, 0, 0, 0, 44, 45, 0, 0, 0, + 0, 96, 0, 102, 0, 58, 62, 65, 60, 37, + 0, 38, 0, 24, 118, 119, 51, 100, 0, 0, + 0, 15, 0, 46, 98, 0, 97, 95, 41, 0, + 30, 66, 0, 0, 89, 101, 106, 42, 0, 104, + 107, 108, 106, 105, } var yyTok1 = [...]int{ @@ -396,12 +407,12 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 75, 76, 71, 69, 68, 70, 73, 72, 3, 3, + 78, 79, 74, 72, 71, 73, 76, 75, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 77, 3, 78, + 3, 80, 3, 81, } var yyTok2 = [...]int{ @@ -411,7 +422,7 @@ var yyTok2 = [...]int{ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, - 62, 63, 64, 65, 66, 67, 74, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 77, } var yyTok3 = [...]int{ @@ -428,7 +439,7 @@ var yyErrorMessages = [...]struct { var ( yyDebug = 0 - yyErrorVerbose = true + yyErrorVerbose = false ) type yyLexer interface { @@ -854,9 +865,9 @@ yydefault: yyVAL.ids = yyDollar[2].ids } case 25: - yyDollar = yyS[yypt-8 : yypt+1] + yyDollar = yyS[yypt-9 : yypt+1] { - yyVAL.stmt = &UpsertIntoStmt{isInsert: true, tableRef: yyDollar[3].tableRef, cols: yyDollar[5].ids, rows: yyDollar[8].rows} + yyVAL.stmt = &UpsertIntoStmt{isInsert: true, tableRef: yyDollar[3].tableRef, cols: yyDollar[5].ids, rows: yyDollar[8].rows, onConflict: yyDollar[9].onConflict} } case 26: yyDollar = yyS[yypt-8 : yypt+1] @@ -874,181 +885,191 @@ yydefault: yyVAL.stmt = &UpdateStmt{tableRef: yyDollar[2].tableRef, updates: yyDollar[4].updates, where: yyDollar[5].exp, indexOn: yyDollar[6].ids, limit: int(yyDollar[7].number)} } case 29: + yyDollar = yyS[yypt-0 : yypt+1] + { + yyVAL.onConflict = nil + } + case 30: + yyDollar = yyS[yypt-4 : yypt+1] + { + yyVAL.onConflict = &OnConflictDo{} + } + case 31: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.updates = []*colUpdate{yyDollar[1].update} } - case 30: + case 32: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.updates = append(yyDollar[1].updates, yyDollar[3].update) } - case 31: + case 33: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.update = &colUpdate{col: yyDollar[1].id, op: yyDollar[2].cmpOp, val: yyDollar[3].exp} } - case 32: + case 34: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.ids = nil } - case 33: + case 35: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.ids = yyDollar[1].ids } - case 34: + case 36: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.rows = []*RowSpec{yyDollar[1].row} } - case 35: + case 37: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.rows = append(yyDollar[1].rows, yyDollar[3].row) } - case 36: + case 38: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.row = &RowSpec{Values: yyDollar[2].values} } - case 37: + case 39: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.ids = []string{yyDollar[1].id} } - case 38: + case 40: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.ids = append(yyDollar[1].ids, yyDollar[3].id) } - case 39: + case 41: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.cols = []*ColSelector{yyDollar[1].col} } - case 40: + case 42: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.cols = append(yyDollar[1].cols, yyDollar[3].col) } - case 41: + case 43: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.values = nil } - case 42: + case 44: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.values = yyDollar[1].values } - case 43: + case 45: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.values = []ValueExp{yyDollar[1].exp} } - case 44: + case 46: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.values = append(yyDollar[1].values, yyDollar[3].exp) } - case 45: + case 47: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &Number{val: int64(yyDollar[1].number)} } - case 46: + case 48: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &Varchar{val: yyDollar[1].str} } - case 47: + case 49: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &Bool{val: yyDollar[1].boolean} } - case 48: + case 50: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &Blob{val: yyDollar[1].blob} } - case 49: + case 51: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.value = &Cast{val: yyDollar[3].exp, t: yyDollar[5].sqlType} } - case 50: + case 52: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.value = &SysFn{fn: yyDollar[1].id} } - case 51: + case 53: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.value = &Param{id: yyDollar[2].id} } - case 52: + case 54: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &Param{id: fmt.Sprintf("param%d", yyDollar[1].pparam), pos: yyDollar[1].pparam} } - case 53: + case 55: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.value = &NullValue{t: AnyType} } - case 54: + case 56: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.colsSpec = []*ColSpec{yyDollar[1].colSpec} } - case 55: + case 57: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.colsSpec = append(yyDollar[1].colsSpec, yyDollar[3].colSpec) } - case 56: + case 58: yyDollar = yyS[yypt-5 : yypt+1] { - yyVAL.colSpec = &ColSpec{colName: yyDollar[1].id, colType: yyDollar[2].sqlType, maxLen: int(yyDollar[3].number), autoIncrement: yyDollar[4].boolean, notNull: yyDollar[5].boolean} + yyVAL.colSpec = &ColSpec{colName: yyDollar[1].id, colType: yyDollar[2].sqlType, maxLen: int(yyDollar[3].number), notNull: yyDollar[4].boolean, autoIncrement: yyDollar[5].boolean} } - case 57: + case 59: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.number = 0 } - case 58: + case 60: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.number = yyDollar[2].number } - case 59: + case 61: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.boolean = false } - case 60: + case 62: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.boolean = true } - case 61: + case 63: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.boolean = false } - case 62: + case 64: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.boolean = false } - case 63: + case 65: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.boolean = true } - case 64: + case 66: yyDollar = yyS[yypt-12 : yypt+1] { yyVAL.stmt = &SelectStmt{ @@ -1064,337 +1085,337 @@ yydefault: limit: int(yyDollar[12].number), } } - case 65: + case 67: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.distinct = false } - case 66: + case 68: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.distinct = true } - case 67: + case 69: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.sels = nil } - case 68: + case 70: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.sels = yyDollar[1].sels } - case 69: + case 71: yyDollar = yyS[yypt-2 : yypt+1] { yyDollar[1].sel.setAlias(yyDollar[2].id) yyVAL.sels = []Selector{yyDollar[1].sel} } - case 70: + case 72: yyDollar = yyS[yypt-4 : yypt+1] { yyDollar[3].sel.setAlias(yyDollar[4].id) yyVAL.sels = append(yyDollar[1].sels, yyDollar[3].sel) } - case 71: + case 73: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.sel = yyDollar[1].col } - case 72: + case 74: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.sel = &AggColSelector{aggFn: yyDollar[1].aggFn, col: "*"} } - case 73: + case 75: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.sel = &AggColSelector{aggFn: yyDollar[1].aggFn, db: yyDollar[3].col.db, table: yyDollar[3].col.table, col: yyDollar[3].col.col} } - case 74: + case 76: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.col = &ColSelector{col: yyDollar[1].id} } - case 75: + case 77: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.col = &ColSelector{table: yyDollar[1].id, col: yyDollar[3].id} } - case 76: + case 78: yyDollar = yyS[yypt-5 : yypt+1] { yyVAL.col = &ColSelector{db: yyDollar[1].id, table: yyDollar[3].id, col: yyDollar[5].id} } - case 77: + case 79: yyDollar = yyS[yypt-3 : yypt+1] { yyDollar[1].tableRef.asBefore = yyDollar[2].number yyDollar[1].tableRef.as = yyDollar[3].id yyVAL.ds = yyDollar[1].tableRef } - case 78: + case 80: yyDollar = yyS[yypt-4 : yypt+1] { yyDollar[2].stmt.(*SelectStmt).as = yyDollar[4].id yyVAL.ds = yyDollar[2].stmt.(DataSource) } - case 79: + case 81: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.tableRef = &tableRef{table: yyDollar[1].id} } - case 80: + case 82: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.tableRef = &tableRef{db: yyDollar[1].id, table: yyDollar[3].id} } - case 81: + case 83: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.number = 0 } - case 82: + case 84: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.number = yyDollar[3].number } - case 83: + case 85: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.joins = nil } - case 84: + case 86: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.joins = yyDollar[1].joins } - case 85: + case 87: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.joins = []*JoinSpec{yyDollar[1].join} } - case 86: + case 88: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.joins = append([]*JoinSpec{yyDollar[1].join}, yyDollar[2].joins...) } - case 87: + case 89: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.join = &JoinSpec{joinType: yyDollar[1].joinType, ds: yyDollar[3].ds, indexOn: yyDollar[4].ids, cond: yyDollar[6].exp} } - case 88: + case 90: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.joinType = InnerJoin } - case 89: + case 91: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.joinType = yyDollar[1].joinType } - case 90: + case 92: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.exp = nil } - case 91: + case 93: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.exp = yyDollar[2].exp } - case 92: + case 94: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.cols = nil } - case 93: + case 95: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.cols = yyDollar[3].cols } - case 94: + case 96: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.exp = nil } - case 95: + case 97: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.exp = yyDollar[2].exp } - case 96: + case 98: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.number = 0 } - case 97: + case 99: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.number = yyDollar[2].number } - case 98: + case 100: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.ordcols = nil } - case 99: + case 101: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.ordcols = yyDollar[3].ordcols } - case 100: + case 102: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.ids = nil } - case 101: + case 103: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.ids = yyDollar[4].ids } - case 102: + case 104: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.ordcols = []*OrdCol{{sel: yyDollar[1].col, descOrder: yyDollar[2].opt_ord}} } - case 103: + case 105: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.ordcols = append(yyDollar[1].ordcols, &OrdCol{sel: yyDollar[3].col, descOrder: yyDollar[4].opt_ord}) } - case 104: + case 106: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.opt_ord = false } - case 105: + case 107: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.opt_ord = false } - case 106: + case 108: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.opt_ord = true } - case 107: + case 109: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.id = "" } - case 108: + case 110: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.id = yyDollar[1].id } - case 109: + case 111: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.id = yyDollar[2].id } - case 110: + case 112: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].exp } - case 111: + case 113: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].binExp } - case 112: + case 114: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.exp = &NotBoolExp{exp: yyDollar[2].exp} } - case 113: + case 115: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.exp = &NumExp{left: &Number{val: 0}, op: SUBSOP, right: yyDollar[2].exp} } - case 114: + case 116: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.exp = &LikeBoolExp{val: yyDollar[1].exp, notLike: yyDollar[2].boolean, pattern: yyDollar[4].exp} } - case 115: + case 117: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.exp = &ExistsBoolExp{q: (yyDollar[3].stmt).(*SelectStmt)} } - case 116: + case 118: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.exp = &InSubQueryExp{val: yyDollar[1].exp, notIn: yyDollar[2].boolean, q: yyDollar[5].stmt.(*SelectStmt)} } - case 117: + case 119: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.exp = &InListExp{val: yyDollar[1].exp, notIn: yyDollar[2].boolean, values: yyDollar[5].values} } - case 118: + case 120: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].sel } - case 119: + case 121: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].value } - case 120: + case 122: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.exp = yyDollar[2].exp } - case 121: + case 123: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.boolean = false } - case 122: + case 124: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.boolean = true } - case 123: + case 125: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: ADDOP, right: yyDollar[3].exp} } - case 124: + case 126: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: SUBSOP, right: yyDollar[3].exp} } - case 125: + case 127: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: DIVOP, right: yyDollar[3].exp} } - case 126: + case 128: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: MULTOP, right: yyDollar[3].exp} } - case 127: + case 129: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &BinBoolExp{left: yyDollar[1].exp, op: yyDollar[2].logicOp, right: yyDollar[3].exp} } - case 128: + case 130: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: yyDollar[2].cmpOp, right: yyDollar[3].exp} } - case 129: + case 131: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: EQ, right: &NullValue{t: AnyType}} } - case 130: + case 132: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: NE, right: &NullValue{t: AnyType}} diff --git a/embedded/sql/stmt.go b/embedded/sql/stmt.go index 8ce8cf28d6..bf5f1b16ad 100644 --- a/embedded/sql/stmt.go +++ b/embedded/sql/stmt.go @@ -258,8 +258,15 @@ func (stmt *CreateTableStmt) execAt(tx *SQLTx, params map[string]interface{}) (* v := make([]byte, 1+4+len(col.colName)) if col.autoIncrement { + + // in case the auto incremental key is not a primary key if len(table.primaryIndex.cols) > 1 || col.id != table.primaryIndex.cols[0].id { - return nil, ErrLimitedAutoIncrement + // creates a new unique index + createIndexStmt := &CreateIndexStmt{unique: true, table: table.name, cols: []string{col.colName}} + _, err = createIndexStmt.execAt(tx, params) + if err != nil { + return nil, err + } } v[0] = v[0] | autoIncrementFlag @@ -408,16 +415,20 @@ func (stmt *AddColumnStmt) execAt(tx *SQLTx, params map[string]interface{}) (*SQ } type UpsertIntoStmt struct { - isInsert bool - tableRef *tableRef - cols []string - rows []*RowSpec + isInsert bool + tableRef *tableRef + cols []string + rows []*RowSpec + onConflict *OnConflictDo } type RowSpec struct { Values []ValueExp } +type OnConflictDo struct { +} + func (stmt *UpsertIntoStmt) inferParameters(tx *SQLTx, params map[string]SQLValueType) error { if tx.currentDB == nil { return ErrNoDatabaseSelected @@ -491,20 +502,29 @@ func (stmt *UpsertIntoStmt) execAt(tx *SQLTx, params map[string]interface{}) (*S valuesByColID := make(map[uint32]TypedValue) + var pkMustExist bool + for colID, col := range table.colsByID { colPos, specified := selPosByColID[colID] if !specified { // TODO: Default values - if col.notNull { + if col.notNull && !col.autoIncrement { return nil, fmt.Errorf("%w (%s)", ErrNotNullableColumnCannotBeNull, col.colName) } - continue - } - if stmt.isInsert && col.autoIncrement { - return nil, fmt.Errorf("%w (%s)", ErrNoValueForAutoIncrementalColumn, col.colName) + // inject auto-incremental pk value + if stmt.isInsert && table.IsAutoIncremented() { + table.maxPK++ + + valuesByColID[table.GetAutoIncrementedColumn()[0].id] = &Number{val: table.maxPK} + + tx.lastInsertedPKs[table.name] = table.maxPK + } + + continue } + // value was specified cVal := row.Values[colPos] val, err := cVal.substitute(params) @@ -518,25 +538,26 @@ func (stmt *UpsertIntoStmt) execAt(tx *SQLTx, params map[string]interface{}) (*S } if rval.IsNull() { - if col.notNull { + if col.notNull || col.autoIncrement { return nil, fmt.Errorf("%w (%s)", ErrNotNullableColumnCannotBeNull, col.colName) } continue } - valuesByColID[colID] = rval - } - - // inject auto-incremental pk value - if stmt.isInsert && table.autoIncrementPK { - table.maxPK++ + if col.autoIncrement { + // validate specified value + nl, isNumber := rval.Value().(int64) + if !isNumber { + return nil, fmt.Errorf("%w (expecting numeric value)", ErrInvalidValue) + } - pkCol := table.primaryIndex.cols[0] + pkMustExist = nl <= table.maxPK - valuesByColID[pkCol.id] = &Number{val: table.maxPK} + tx.lastInsertedPKs[table.name] = nl + } - tx.lastInsertedPKs[table.name] = table.maxPK + valuesByColID[colID] = rval } pkEncVals, err := encodedPK(table, valuesByColID) @@ -544,7 +565,30 @@ func (stmt *UpsertIntoStmt) execAt(tx *SQLTx, params map[string]interface{}) (*S return nil, err } - err = tx.doUpsert(pkEncVals, valuesByColID, table, stmt.isInsert) + // primary index entry + mkey := mapKey(tx.sqlPrefix(), PIndexPrefix, EncodeID(table.db.id), EncodeID(table.id), EncodeID(table.primaryIndex.id), pkEncVals) + + _, err = tx.get(mkey) + if err != nil && err != store.ErrKeyNotFound { + return nil, err + } + + if err == store.ErrKeyNotFound && pkMustExist { + return nil, err + } + + if stmt.isInsert { + if err == nil && stmt.onConflict == nil { + return nil, store.ErrKeyAlreadyExists + } + + if err == nil && stmt.onConflict != nil { + // TODO: conflict resolution may be extended. Currently only supports "ON CONFLICT DO NOTHING" + return tx, nil + } + } + + err = tx.doUpsert(pkEncVals, valuesByColID, table, !stmt.isInsert) if err != nil { return nil, err } @@ -553,10 +597,10 @@ func (stmt *UpsertIntoStmt) execAt(tx *SQLTx, params map[string]interface{}) (*S return tx, nil } -func (tx *SQLTx) doUpsert(pkEncVals []byte, valuesByColID map[uint32]TypedValue, table *Table, isInsert bool) error { +func (tx *SQLTx) doUpsert(pkEncVals []byte, valuesByColID map[uint32]TypedValue, table *Table, reuseIndex bool) error { var reusableIndexEntries map[uint32]struct{} - if !isInsert && len(table.indexes) > 1 { + if reuseIndex && len(table.indexes) > 1 { currPKRow, err := tx.fetchPKRow(table, valuesByColID) if err != nil && err != ErrNoMoreRows { return err @@ -577,28 +621,9 @@ func (tx *SQLTx) doUpsert(pkEncVals []byte, valuesByColID map[uint32]TypedValue, } } - // create primary index entry + // primary index entry mkey := mapKey(tx.sqlPrefix(), PIndexPrefix, EncodeID(table.db.id), EncodeID(table.id), EncodeID(table.primaryIndex.id), pkEncVals) - if isInsert && !table.autoIncrementPK { - // mkey must not exist - _, err := tx.get(mkey) - if err == nil { - return store.ErrKeyAlreadyExists - } - if err != store.ErrKeyNotFound { - return err - } - } - - if !isInsert && table.autoIncrementPK { - // mkey must exist - _, err := tx.get(mkey) - if err != nil { - return err - } - } - valbuf := bytes.Buffer{} // null values are not serialized @@ -608,6 +633,7 @@ func (tx *SQLTx) doUpsert(pkEncVals []byte, valuesByColID map[uint32]TypedValue, encodedVals++ } } + b := make([]byte, EncLenLen) binary.BigEndian.PutUint32(b, uint32(encodedVals)) @@ -678,10 +704,6 @@ func (tx *SQLTx) doUpsert(pkEncVals []byte, valuesByColID map[uint32]TypedValue, encodedValues[2] = EncodeID(index.id) for i, col := range index.cols { - if col.MaxLen() > maxKeyLen { - return ErrMaxKeyLengthExceeded - } - rval, specified := valuesByColID[col.id] if !specified { rval = &NullValue{t: col.colType} @@ -733,10 +755,6 @@ func encodedPK(table *Table, valuesByColID map[uint32]TypedValue) ([]byte, error return nil, err } - if len(encVal) > maxKeyLen { - return nil, ErrMaxKeyLengthExceeded - } - _, err = valbuf.Write(encVal) if err != nil { return nil, err @@ -997,7 +1015,16 @@ func (stmt *UpdateStmt) execAt(tx *SQLTx, params map[string]interface{}) (*SQLTx return nil, err } - err = tx.doUpsert(pkEncVals, valuesByColID, table, false) + // primary index entry + mkey := mapKey(tx.sqlPrefix(), PIndexPrefix, EncodeID(table.db.id), EncodeID(table.id), EncodeID(table.primaryIndex.id), pkEncVals) + + // mkey must exist + _, err = tx.get(mkey) + if err != nil { + return nil, err + } + + err = tx.doUpsert(pkEncVals, valuesByColID, table, true) if err != nil { return nil, err }