diff --git a/src/box/sql.c b/src/box/sql.c index db86ae98f5158041ba286ad044b426fa753a99f7..1a3168c6ce08917f96f1d92461096f38a4849a76 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1237,20 +1237,6 @@ sql_debug_info(struct info_handler *h) info_end(h); } -struct Expr* -space_column_default_expr(uint32_t space_id, uint32_t fieldno) -{ - struct space *space; - space = space_cache_find(space_id); - assert(space != NULL); - assert(space->def != NULL); - if (space->def->opts.is_view) - return NULL; - assert(space->def->field_count > fieldno); - struct tuple_field *field = tuple_format_field(space->format, fieldno); - return field->sql_default_value_expr; -} - /** * Create and initialize a new template space_def object. * @param parser SQL Parser object. diff --git a/src/box/sql.h b/src/box/sql.h index ec2d1d2123a110d9ad6ae771e778c0563b3c65e5..5be3be425a6c1a41c313ad566647b1832eba6358 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -154,17 +154,6 @@ sql_trigger_name(struct sql_trigger *trigger); uint32_t sql_trigger_space_id(struct sql_trigger *trigger); -/** - * Given space_id and field number, return default value - * for the field. - * @param space_id Space ID. - * @param fieldno Field index. - * @retval Pointer to AST corresponding to default value. - * Can be NULL if no DEFAULT specified or it is a view. - */ -struct Expr* -space_column_default_expr(uint32_t space_id, uint32_t fieldno); - /** * Return the number of bytes required to create a duplicate of the * expression passed as the first argument. The second argument is a diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 2b93b7bcc43081e833aaba2b18f1ed3fcd472a35..23add01ab83b5f6f44f4b77802ddfbf1313d7517 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -47,6 +47,7 @@ #include "sqlInt.h" #include "mem.h" #include "vdbeInt.h" +#include "mp_decimal.h" #include "tarantoolInt.h" #include "box/sequence.h" #include "box/session.h" @@ -488,6 +489,145 @@ sqlAddDefaultValue(Parse * pParse, ExprSpan * pSpan) sql_expr_delete(pSpan->pExpr); } +/** Fetch negative integer value from the expr. */ +static int +sql_expr_nint(const struct Expr *expr, int64_t *res) +{ + assert(expr->op == TK_INTEGER); + if ((expr->flags & EP_IntValue) != 0) { + *res = -expr->u.iValue; + return 0; + } + assert(strlen(expr->u.zToken) > 2); + const char *str = tt_sprintf("-%s", expr->u.zToken); + int base = str[1] == '0' && (str[2] == 'x' || str[2] == 'X') ? 16 : 10; + errno = 0; + *res = strtoll(str, NULL, base); + return errno == 0 ? 0 : -1; +} + +/** Fetch non-negative integer value from the expr. */ +static int +sql_expr_uint(const struct Expr *expr, uint64_t *res) +{ + assert(expr->op == TK_INTEGER); + if ((expr->flags & EP_IntValue) != 0) { + *res = expr->u.iValue; + return 0; + } + assert(strlen(expr->u.zToken) > 2); + const char *str = expr->u.zToken; + int base = str[1] == '0' && (str[2] == 'x' || str[2] == 'X') ? 16 : 10; + errno = 0; + *res = strtoull(str, NULL, base); + return errno == 0 ? 0 : -1; +} + +void +sql_add_term_default(struct Parse *parser, struct ExprSpan *expr_span) +{ + assert(parser->create_column_def.space != NULL); + char *buf = NULL; + uint32_t size = 0; + struct region *region = &parser->region; + struct Expr *expr = expr_span->pExpr; + switch (sql_expr_type(expr)) { + case FIELD_TYPE_BOOLEAN: { + assert(expr->op == TK_TRUE || expr->op == TK_FALSE); + bool val = expr->op == TK_TRUE; + size = mp_sizeof_bool(val); + buf = xregion_alloc(region, size); + mp_encode_bool(buf, val); + break; + } + case FIELD_TYPE_VARBINARY: { + assert(expr->u.zToken[0] == 'x' || expr->u.zToken[0] == 'X'); + assert(expr->u.zToken[1] == '\''); + const char *val_hex = &expr->u.zToken[2]; + uint32_t len = strlen(val_hex) - 1; + assert(val_hex[len] == '\''); + size = mp_sizeof_bin(len / 2); + buf = xregion_alloc(region, size); + char *val = sqlHexToBlob(val_hex, len); + mp_encode_bin(buf, val, len / 2); + sql_xfree(val); + break; + } + case FIELD_TYPE_STRING: { + const char *val = expr->u.zToken; + uint32_t len = strlen(val); + size = mp_sizeof_str(len); + buf = xregion_alloc(region, size); + mp_encode_str(buf, val, len); + break; + } + case FIELD_TYPE_DOUBLE: { + double val; + sqlAtoF(expr_span->zStart, &val, + expr_span->zEnd - expr_span->zStart); + assert(!sqlIsNaN(val)); + size = mp_sizeof_double(val); + buf = xregion_alloc(region, size); + mp_encode_double(buf, val); + break; + } + case FIELD_TYPE_DECIMAL: { + const char *str = tt_cstr(expr_span->zStart, + expr_span->zEnd - expr_span->zStart); + decimal_t val; + decimal_from_string(&val, str); + size = mp_sizeof_decimal(&val); + buf = xregion_alloc(region, size); + mp_encode_decimal(buf, &val); + break; + } + case FIELD_TYPE_INTEGER: { + if (expr->op == TK_UMINUS) { + int64_t val; + if (sql_expr_nint(expr->pLeft, &val) == 0) { + size = mp_sizeof_int(val); + buf = xregion_alloc(region, size); + mp_encode_int(buf, val); + break; + } + int errcode = ER_INT_LITERAL_MAX; + const char *str = expr->pLeft->u.zToken; + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + errcode = ER_HEX_LITERAL_MAX; + diag_set(ClientError, errcode, "-", str); + parser->is_aborted = true; + break; + } + uint64_t val; + if (sql_expr_uint(expr, &val) == 0) { + size = mp_sizeof_uint(val); + buf = xregion_alloc(region, size); + mp_encode_uint(buf, val); + break; + } + int errcode = ER_INT_LITERAL_MAX; + const char *str = expr->u.zToken; + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + errcode = ER_HEX_LITERAL_MAX; + diag_set(ClientError, errcode, "", str); + parser->is_aborted = true; + break; + } + default: + assert(sql_expr_type(expr) == FIELD_TYPE_SCALAR); + assert(expr->op == TK_NULL || expr->op == TK_UNKNOWN); + assert(buf == NULL); + assert(size == 0); + } + sql_expr_delete(expr_span->pExpr); + if (parser->is_aborted) + return; + struct space_def *def = parser->create_column_def.space->def; + struct field_def *field = &def->fields[def->field_count - 1]; + field->default_value = buf; + field->default_value_size = size; +} + static int field_def_create_for_pk(struct Parse *parser, struct field_def *field, const char *space_name) diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index d4d4a9aa03781cf8c2c76313950541a0ea67f9e1..41e7e10243f119485ac267cab2225e59ddbd7d16 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -801,12 +801,13 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct space *space, on_conflict != ON_CONFLICT_ACTION_DEFAULT ? on_conflict : def->fields[i].nullable_action; /* ABORT is a default error action. */ - if (on_conflict_nullable == ON_CONFLICT_ACTION_DEFAULT) - on_conflict_nullable = ON_CONFLICT_ACTION_ABORT; - struct Expr *dflt = space_column_default_expr(def->id, i); - if (on_conflict_nullable == ON_CONFLICT_ACTION_REPLACE && - dflt == NULL) + if (on_conflict_nullable == ON_CONFLICT_ACTION_DEFAULT || + on_conflict_nullable == ON_CONFLICT_ACTION_REPLACE) on_conflict_nullable = ON_CONFLICT_ACTION_ABORT; + /* If default it set it will be inserted instead of NULL. */ + if (on_conflict_nullable == ON_CONFLICT_ACTION_ABORT && + def->fields[i].default_value != NULL) + on_conflict_nullable = ON_CONFLICT_ACTION_NONE; int addr; switch (on_conflict_nullable) { case ON_CONFLICT_ACTION_ABORT: @@ -826,14 +827,8 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct space *space, sqlVdbeAddOp2(v, OP_IsNull, new_tuple_reg + i, ignore_label); break; - case ON_CONFLICT_ACTION_REPLACE: - addr = sqlVdbeAddOp1(v, OP_NotNull, - new_tuple_reg + i); - sqlExprCode(parse_context, dflt, new_tuple_reg + i); - sqlVdbeJumpHere(v, addr); - break; default: - unreachable(); + assert(on_conflict_nullable == ON_CONFLICT_ACTION_NONE); } } sql_emit_table_types(v, space->def, new_tuple_reg); diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 3c024824a81f0a5024e7806a13e9bd4d91060ab1..be67cb53a2ed2b4d9ce6b637f31dc4345b217827 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -288,15 +288,20 @@ carglist ::= . %type cconsname { struct Token } cconsname(N) ::= CONSTRAINT nm(X). { N = X; } cconsname(N) ::= . { N = Token_nil; } -ccons ::= DEFAULT term(X). {sqlAddDefaultValue(pParse,&X);} -ccons ::= DEFAULT LP expr(X) RP. {sqlAddDefaultValue(pParse,&X);} -ccons ::= DEFAULT PLUS number(X). {sqlAddDefaultValue(pParse,&X);} +ccons ::= DEFAULT term(X). {sql_add_term_default(pParse, &X);} +ccons ::= DEFAULT LP expr(X) RP. { + if (sql_expr_is_term(X.pExpr)) + sql_add_term_default(pParse, &X); + else + sqlAddDefaultValue(pParse, &X); +} +ccons ::= DEFAULT PLUS number(X). {sql_add_term_default(pParse, &X);} ccons ::= DEFAULT MINUS(A) number(X). { ExprSpan v; v.pExpr = sqlPExpr(pParse, TK_UMINUS, X.pExpr, 0); v.zStart = A.z; v.zEnd = X.zEnd; - sqlAddDefaultValue(pParse,&v); + sql_add_term_default(pParse, &v); } // In addition to the type name, we also care about the primary key and diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 564cbf587708eefd8d6792ed27eba4f1ebbdcba1..4244b7a2d9028bb16ad03e077120c2fa706cbdb6 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -2611,6 +2611,16 @@ struct Expr * expr_new_variable(struct Parse *parse, const struct Token *spec, const struct Token *id); +/** Return TRUE if expression is term, FALSE otherwise. */ +static inline bool +sql_expr_is_term(const struct Expr *expr) +{ + uint8_t op = expr->op; + return op == TK_STRING || op == TK_BLOB || op == TK_INTEGER || + op == TK_FLOAT || op == TK_DECIMAL || op == TK_TRUE || + op == TK_FALSE || op == TK_UNKNOWN || op == TK_NULL; +} + /** * Set the sort order for the last element on the given ExprList. * @@ -2736,6 +2746,10 @@ sqlAddPrimaryKey(struct Parse *parse); void sql_create_check_contraint(struct Parse *parser, bool is_field_ck); +/** Add a default literal to the last created column. */ +void +sql_add_term_default(struct Parse *parser, struct ExprSpan *expr_span); + void sqlAddDefaultValue(Parse *, ExprSpan *); void sqlAddCollateType(Parse *, Token *); diff --git a/test/sql-luatest/defaults_test.lua b/test/sql-luatest/defaults_test.lua index a1a9d7b132aeb1603e5c987a14bc80bd7e13ad9c..b4e2543b3161ed9d38aea84e4fd2fa988aca24f0 100644 --- a/test/sql-luatest/defaults_test.lua +++ b/test/sql-luatest/defaults_test.lua @@ -233,3 +233,15 @@ g.test_only_numbers_with_signs = function() box.execute([[DROP TABLE t;]]) end) end + +g.test_new_defaults = function() + g.server:exec(function() + -- Make sure a literal works correctly as a default value. + local sql = [[CREATE TABLE T(I INT PRIMARY KEY, A ANY DEFAULT 123, + B STRING DEFAULT('something'));]] + box.execute(sql) + t.assert_equals(box.space.T:format()[2].default, 123) + t.assert_equals(box.space.T:format()[3].default, 'something') + box.space.T:drop() + end) +end diff --git a/test/sql-tap/default.test.lua b/test/sql-tap/default.test.lua index ff9a3520d6d7a1515572770433e595005707c1dc..381b393bdbd4df536854d54ff36d83face3c2ec7 100755 --- a/test/sql-tap/default.test.lua +++ b/test/sql-tap/default.test.lua @@ -80,7 +80,7 @@ test:do_execsql_test( PRAGMA table_info(t4); ]], { -- <default-2.1> - 0, "rowid", "integer", 1, "", 1, 1, "c", "string", 0, "'abc'", 0 + 0, "rowid", "integer", 1, "", 1, 1, "c", "string", 0, "", 0 -- </default-2.1> }) @@ -91,7 +91,7 @@ test:do_execsql_test( PRAGMA table_info(t4); ]], { -- <default-2.2> - 0, "rowid", "integer", 1, "", 1, 1, "c", "string", 0, "'abc'", 0 + 0, "rowid", "integer", 1, "", 1, 1, "c", "string", 0, "", 0 -- </default-2.2> }) diff --git a/test/sql-tap/insert3.test.lua b/test/sql-tap/insert3.test.lua index 60567483a397fd4ca7a37737f1b29f3f24c3325f..44c9bdc79f1c3377f2e0a4521ab25209fd3b9363 100755 --- a/test/sql-tap/insert3.test.lua +++ b/test/sql-tap/insert3.test.lua @@ -134,7 +134,7 @@ test:do_execsql_test( SELECT * FROM t2dup; ]], { -- <insert3-2.1> - 1, 123, "b", "c", 2, -1, "234", "c", 3, -1, "b", "345" + 1, 123, "", "", 2, -1, "234", "", 3, -1, "", "345" -- </insert3-2.1> }) @@ -148,7 +148,7 @@ test:do_execsql_test( SELECT * FROM t2dup; ]], { -- <insert3-2.2> - 4, 1, "b", "c", 5, -1, "987", "c", 6, -1, "b", "876" + 4, 1, "", "", 5, -1, "987", "", 6, -1, "", "876" -- </insert3-2.2> }) @@ -237,7 +237,7 @@ test:do_execsql_test( "insert3-3.7", [[ CREATE TABLE t6(id INTEGER PRIMARY KEY AUTOINCREMENT, x INT, - y DOUBLE DEFAULT 4.3, z TEXT DEFAULT 'hi'); + y DOUBLE DEFAULT 4.3e0, z TEXT DEFAULT 'hi'); INSERT INTO t6 DEFAULT VALUES; SELECT * FROM t6; ]], { diff --git a/test/sql-tap/triggerC.test.lua b/test/sql-tap/triggerC.test.lua index e2e6e8d2bad7259262eef0968b16ad4f6228f13d..44417797dd173767d56eedc9a3fed4d96273bbb2 100755 --- a/test/sql-tap/triggerC.test.lua +++ b/test/sql-tap/triggerC.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool local test = require("sqltester") -test:plan(43) +test:plan(41) --!./tcltestrunner.lua -- 2009 August 24 @@ -739,64 +739,6 @@ test:do_test( -- </triggerC-11.0> }) --- MUST_WORK_TEST -local -tests11 = {-- {"CREATE TABLE t1(a PRIMARY KEY, b)", {{}, {}}}, - {"CREATE TABLE t1(a INT PRIMARY KEY DEFAULT 1, b TEXT DEFAULT 'abc')", {1, "abc"}}} - ---for _ in X(0, "X!foreach", [=[["testno tbl defaults","\n 1 \"CREATE TABLE t1(a PRIMARY KEY, b)\" {{} {}}\n 2 \"CREATE TABLE t1(a PRIMARY KEY DEFAULT 1, b DEFAULT 'abc')\" {1 abc}\n 3 \"CREATE TABLE t1(a PRIMARY KEY, b DEFAULT 4.5)\" {{} 4.5}\n"]]=]) do -for testno, v in ipairs(tests11) do - test:do_test( - "triggerC-11."..testno..".1", - function() - test:catchsql " DROP TABLE t1 " - test:execsql " DELETE FROM log " - test:execsql(v[1]) - return test:execsql [[ - CREATE TRIGGER tt1 BEFORE INSERT ON t1 FOR EACH ROW BEGIN - INSERT INTO log VALUES((SELECT COALESCE(MAX(id),0) + 1 FROM log), - new.a, new.b); - END; - INSERT INTO t1 DEFAULT VALUES; - SELECT a,b FROM log; - ]] - end, v[2]) - - -- Tarantool: we're unable to do double insert of default vals - -- (PK will be not unique). Comment so far - -- test:do_test( - -- "triggerC-11."..testno..".2", - -- function() - -- test:execsql " DELETE FROM log " - -- return test:execsql [[ - -- CREATE TRIGGER tt2 AFTER INSERT ON t1 FOR EACH ROW BEGIN - -- INSERT INTO log VALUES(new.a, new.b); - -- END; - -- INSERT INTO t1 DEFAULT VALUES; - -- SELECT * FROM log; - -- ]] - -- end, { - -- -- X(891, "X!cmd", [=[["concat",["defaults"],["defaults"]]]=]) - -- }) - - -- Legacy from the original code. Must be replaced with valid value. - local defaults = nil - test:do_test( - "triggerC-11."..testno..".3", - function() - test:execsql " DROP TRIGGER tt1 " - test:execsql " DELETE FROM t1" - test:execsql " DELETE FROM log " - return test:execsql [[ - INSERT INTO t1 DEFAULT VALUES; - SELECT a,b FROM log; - ]] - end, { - defaults - }) - - -- -end test:do_test( "triggerC-11.4", function() diff --git a/test/sql/boolean.result b/test/sql/boolean.result index e5a61fc402ed30c9f2419f97e9f8f89505e8b558..338794d6de8467b8db820dba95adb5a9ee1f8d40 100644 --- a/test/sql/boolean.result +++ b/test/sql/boolean.result @@ -68,23 +68,15 @@ SELECT a FROM t WHERE a != true; | ... -- Check DEFAULT values for boolean. -CREATE TABLE t0 (i INT PRIMARY KEY, a BOOLEAN DEFAULT true); +CREATE TABLE t0def (i INT PRIMARY KEY, a BOOLEAN DEFAULT true); | --- | - row_count: 1 | ... -INSERT INTO t0 VALUES (1, false); - | --- - | - row_count: 1 - | ... -INSERT INTO t0(i) VALUES (2); +INSERT INTO t0def(i) VALUES (1); | --- | - row_count: 1 | ... -INSERT INTO t0 VALUES (3, NULL); - | --- - | - row_count: 1 - | ... -SELECT * FROM t0; +SELECT * FROM t0def; | --- | - metadata: | - name: i @@ -92,12 +84,30 @@ SELECT * FROM t0; | - name: a | type: boolean | rows: - | - [1, false] - | - [2, true] - | - [3, null] + | - [1, true] + | ... +DROP TABLE t0def; + | --- + | - row_count: 1 | ... -- Check UNKNOWN value for boolean. +CREATE TABLE t0 (i INT PRIMARY KEY, a BOOLEAN); + | --- + | - row_count: 1 + | ... +INSERT INTO t0 VALUES (1, false); + | --- + | - row_count: 1 + | ... +INSERT INTO t0 VALUES (2, true); + | --- + | - row_count: 1 + | ... +INSERT INTO t0 VALUES (3, NULL); + | --- + | - row_count: 1 + | ... INSERT INTO t0 VALUES (4, UNKNOWN); | --- | - row_count: 1 diff --git a/test/sql/boolean.test.sql b/test/sql/boolean.test.sql index 03ad01e8e51987c9917a6ae4b1188136974b1696..41aecc8934f132a53c5cc14846ea7816f85f62cb 100644 --- a/test/sql/boolean.test.sql +++ b/test/sql/boolean.test.sql @@ -26,13 +26,16 @@ SELECT a FROM t WHERE a; SELECT a FROM t WHERE a != true; -- Check DEFAULT values for boolean. -CREATE TABLE t0 (i INT PRIMARY KEY, a BOOLEAN DEFAULT true); -INSERT INTO t0 VALUES (1, false); -INSERT INTO t0(i) VALUES (2); -INSERT INTO t0 VALUES (3, NULL); -SELECT * FROM t0; +CREATE TABLE t0def (i INT PRIMARY KEY, a BOOLEAN DEFAULT true); +INSERT INTO t0def(i) VALUES (1); +SELECT * FROM t0def; +DROP TABLE t0def; -- Check UNKNOWN value for boolean. +CREATE TABLE t0 (i INT PRIMARY KEY, a BOOLEAN); +INSERT INTO t0 VALUES (1, false); +INSERT INTO t0 VALUES (2, true); +INSERT INTO t0 VALUES (3, NULL); INSERT INTO t0 VALUES (4, UNKNOWN); SELECT * FROM t0; diff --git a/test/sql/types.result b/test/sql/types.result index 17b361a185a03bc92f59ea564a0fc3e09f88ff08..3d62f90dece1e47eccd41f118b05c0d7a1ed8f08 100644 --- a/test/sql/types.result +++ b/test/sql/types.result @@ -2035,7 +2035,7 @@ box.execute("DROP TABLE t3;") box.func.check_t3_ck_unnamed_t3_d_1:drop() --- ... -box.execute("CREATE TABLE t4 (i INT PRIMARY KEY, d DOUBLE DEFAULT 1.2345);") +box.execute("CREATE TABLE t4 (i INT PRIMARY KEY, d DOUBLE DEFAULT 1.2345e0);") --- - row_count: 1 ... diff --git a/test/sql/types.test.lua b/test/sql/types.test.lua index 986e93c1715836341236f360bc5032297206931e..333d7edaa42a1608fd93be96fbd906182c69c4e1 100644 --- a/test/sql/types.test.lua +++ b/test/sql/types.test.lua @@ -465,7 +465,7 @@ box.execute("SELECT * FROM t3;") box.execute("DROP TABLE t3;") box.func.check_t3_ck_unnamed_t3_d_1:drop() -box.execute("CREATE TABLE t4 (i INT PRIMARY KEY, d DOUBLE DEFAULT 1.2345);") +box.execute("CREATE TABLE t4 (i INT PRIMARY KEY, d DOUBLE DEFAULT 1.2345e0);") box.execute("INSERT INTO t4(i) VALUES (1);") box.execute("SELECT * FROM t4;") box.execute("DROP TABLE t4;")