[patches] [PATCH 2/3] sql: check identifiers only for printability
Kirill Yukhin
kyukhin at tarantool.org
Tue Feb 6 08:56:56 MSK 2018
Hello Alex,
My comments are inlined.
On 05 Feb 19:51, AKhatskevich wrote:
> This patch makes identifiers in tarantool and delimited identifiers in
> SQL obey the same rules.
Extra space in beginning of the line.
> From this moment, delimited identifiers in SQL are checked only for
> printability. For more info, see #2914.
Ditto.
> There was option to check any identifier that tokenizer faces, however,
> in that case many identifiers could have been checked more than once.
Ditto.
>
> Part of #2126
> ---
> src/box/sql/alter.c | 30 ---------
> src/box/sql/build.c | 46 +++++---------
> src/box/sql/expr.c | 3 +
> src/box/sql/sqliteInt.h | 2 +-
> src/box/sql/trigger.c | 2 +-
> test/sql-tap/alter.test.lua | 14 +----
> test/sql-tap/autoinc.test.lua | 2 +-
> test/sql-tap/identifier-characters.test.lua | 97 +++++++++++++++++++++++++++++
> test/sql-tap/index1.test.lua | 51 +--------------
> test/sql-tap/suite.ini | 2 +-
> 10 files changed, 125 insertions(+), 124 deletions(-)
> create mode 100755 test/sql-tap/identifier-characters.test.lua
>
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index 293cca232..00d160a33 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -62,24 +62,6 @@ reloadTableSchema(Parse * pParse, Table * pTab, const char *zName)
> sqlite3VdbeAddRenameTableOp(v, pTab->tnum, zNewName);
> }
>
> -/*
> - * Parameter zName is the name of a table that is about to be altered
> - * (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
> - * If the table is a system table, this function leaves an error message
> - * in pParse->zErr (system tables may not be altered) and returns non-zero.
> - *
> - * Or, if zName is not a system table, zero is returned.
> - */
> -static int
> -isSystemTable(Parse * pParse, const char *zName)
> -{
> - if (0 == sqlite3StrNICmp(zName, "_", 1)) {
> - sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
> - return 1;
> - }
> - return 0;
> -}
> -
> /*
> * Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
> * command.
> @@ -124,15 +106,6 @@ sqlite3AlterRenameTable(Parse * pParse, /* Parser context. */
> goto exit_rename_table;
> }
>
> - /* Make sure it is not a system table being altered, or a reserved name
> - * that the table is being renamed to.
> - */
> - if (SQLITE_OK != isSystemTable(pParse, pTab->zName)) {
> - goto exit_rename_table;
> - }
> - if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) {
> - goto exit_rename_table;
> - }
> #ifndef SQLITE_OMIT_VIEW
> if (pTab->pSelect) {
> sqlite3ErrorMsg(pParse, "view %s may not be altered",
> @@ -318,9 +291,6 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
> sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
> goto exit_begin_add_column;
> }
> - if (SQLITE_OK != isSystemTable(pParse, pTab->zName)) {
> - goto exit_begin_add_column;
> - }
>
> assert(pTab->addColOffset > 0);
>
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index e4c1e61ce..6674e98a7 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -47,6 +47,7 @@
> #include "vdbeInt.h"
> #include "tarantoolInt.h"
> #include "box/session.h"
> +#include "box/identifier.h"
>
> /*
> * This routine is called after a single SQL statement has been
> @@ -583,18 +584,20 @@ sqlite3FindDb(sqlite3 * db, Token * pName)
>
> /*
> * This routine is used to check if the UTF-8 string zName is a legal
> - * unqualified name for a new schema object (table, index, view or
> - * trigger). All names are legal except those that begin with the string
> - * "sqlite_" (in upper, lower or mixed case). This portion of the namespace
> - * is reserved for internal use.
> + * unqualified name for an identifier.
> + * Some objects may not be checked, because they are validated in Tarantool.
> + * (e.g. table, index, column name of a real table)
> + * All names are legal except those that cantain non-printable
> + * characters or have length greater than BOX_NAME_MAX.
> */
> int
> -sqlite3CheckObjectName(Parse * pParse, char *zName)
> +sqlite3CheckIdentifierName(Parse *pParse, char *zName)
> {
> - if (!pParse->db->init.busy && pParse->nested == 0
> - && 0 == sqlite3StrNICmp(zName, "_", 1)) {
> + ssize_t len = strlen(zName);
> +
> + if (len > BOX_NAME_MAX || identifier_check(zName, len) != 0) {
> sqlite3ErrorMsg(pParse,
> - "object name reserved for internal use: %s",
> + "identifier name is invalid: %s",
> zName);
> return SQLITE_ERROR;
> }
> @@ -668,9 +671,6 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
> pParse->sNameToken = *pName;
> if (zName == 0)
> return;
> - if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) {
> - goto begin_table_error;
> - }
> #ifndef SQLITE_OMIT_AUTHORIZATION
> assert(isTemp == 0 || isTemp == 1);
> assert(isView == 0 || (isView == 1 && isTemp == 0));
> @@ -2447,11 +2447,6 @@ sqlite3DropTable(Parse * pParse, SrcList * pName, int isView, int noErr)
> }
> }
> #endif
> - if (sqlite3StrNICmp(pTab->zName, "_", 1) == 0) {
> - sqlite3ErrorMsg(pParse, "table %s may not be dropped",
> - pTab->zName);
> - goto exit_drop_table;
> - }
> #ifndef SQLITE_OMIT_VIEW
> /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
> * on a table.
> @@ -2956,16 +2951,6 @@ sqlite3CreateIndex(Parse * pParse, /* All information about this parse */
>
> assert(pTab != 0);
> assert(pParse->nErr == 0);
> - if (sqlite3CheckObjectName(pParse, pTab->zName) != 0
> - && db->init.busy == 0
> -#if SQLITE_USER_AUTHENTICATION
> - && sqlite3UserAuthTable(pTab->zName) == 0
> -#endif
> - && sqlite3StrNICmp(&pTab->zName[7], "altertab_", 9) != 0) {
> - sqlite3ErrorMsg(pParse, "table %s may not be indexed",
> - pTab->zName);
> - goto exit_create_index;
> - }
> #ifndef SQLITE_OMIT_VIEW
> if (pTab->pSelect) {
> sqlite3ErrorMsg(pParse, "views may not be indexed");
> @@ -2990,9 +2975,6 @@ sqlite3CreateIndex(Parse * pParse, /* All information about this parse */
> if (zName == 0)
> goto exit_create_index;
> assert(pName->z != 0);
> - if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) {
> - goto exit_create_index;
> - }
> if (!db->init.busy) {
> if (sqlite3FindTable(db, zName) != 0) {
> sqlite3ErrorMsg(pParse,
> @@ -3988,6 +3970,12 @@ sqlite3Savepoint(Parse * pParse, int op, Token * pName)
> sqlite3DbFree(pParse->db, zName);
> return;
> }
> + if (op == SAVEPOINT_BEGIN &&
> + sqlite3CheckIdentifierName(pParse, zName)
> + != SQLITE_OK) {
> + sqlite3ErrorMsg(pParse, "bad savepoint name");
> + return;
> + }
> sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
> }
> }
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 42e7df74d..7edb96dc8 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -1850,6 +1850,9 @@ sqlite3ExprListSetName(Parse * pParse, /* Parsing context */
> pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
> if (dequote)
> sqlite3NormalizeName(pItem->zName);
> + /* n = 0 in case of select * */
Extra star. Could you pls re-pharse the comment.
> + if (pName->n != 0)
> + sqlite3CheckIdentifierName(pParse, pItem->zName);
> }
> }
>
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 4dee24809..310a3b7dc 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -3438,7 +3438,7 @@ Expr *sqlite3ExprAddCollateToken(Parse * pParse, Expr *, const Token *, int);
> Expr *sqlite3ExprAddCollateString(Parse *, Expr *, const char *);
> Expr *sqlite3ExprSkipCollate(Expr *);
> int sqlite3CheckCollSeq(Parse *, struct coll *);
> -int sqlite3CheckObjectName(Parse *, char *);
> +int sqlite3CheckIdentifierName(Parse *, char *);
> void sqlite3VdbeSetChanges(sqlite3 *, int);
> int sqlite3AddInt64(i64 *, i64);
> int sqlite3SubInt64(i64 *, i64);
> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> index aeb80d0cf..128cc05dd 100644
> --- a/src/box/sql/trigger.c
> +++ b/src/box/sql/trigger.c
> @@ -120,7 +120,7 @@ sqlite3BeginTrigger(Parse * pParse, /* The parse context of the CREATE TRIGGER s
> * specified name exists
> */
> zName = sqlite3NameFromToken(db, pName);
> - if (!zName || SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) {
> + if (!zName || SQLITE_OK != sqlite3CheckIdentifierName(pParse, zName)) {
> goto trigger_cleanup;
> }
> if (sqlite3HashFind(&(db->mdb.pSchema->trigHash), zName)) {
> diff --git a/test/sql-tap/alter.test.lua b/test/sql-tap/alter.test.lua
> index 18de92d00..1a4ceaaf3 100755
> --- a/test/sql-tap/alter.test.lua
> +++ b/test/sql-tap/alter.test.lua
> @@ -1,6 +1,6 @@
> #!/usr/bin/env tarantool
> test = require("sqltester")
> -test:plan(42)
> +test:plan(41)
>
> test:do_execsql_test(
> "alter-1.1",
> @@ -93,17 +93,7 @@ test:do_catchsql_test(
> ALTER TABLE "_space" RENAME TO space;
> ]], {
> -- <alter-2.3>
> - 1, "table _space may not be altered"
> - -- </alter-2.3>
> - })
> -
> -test:do_catchsql_test(
> - "alter-2.4",
> - [[
> - ALTER TABLE t3 RENAME TO _test;
> - ]], {
> - -- <alter-2.3>
> - 1, "object name reserved for internal use: _TEST"
> + 1, "/logic error/"
> -- </alter-2.3>
> })
>
> diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua
> index 2fa48e657..030c12725 100755
> --- a/test/sql-tap/autoinc.test.lua
> +++ b/test/sql-tap/autoinc.test.lua
> @@ -71,7 +71,7 @@ test:do_catchsql_test(
> DROP TABLE "_sequence"
> ]], {
> -- <autoinc-1.5>
> - 1, "table _sequence may not be dropped"
> + 1, "/system space/"
> -- </autoinc-1.5>
> })
>
> diff --git a/test/sql-tap/identifier-characters.test.lua b/test/sql-tap/identifier-characters.test.lua
> new file mode 100755
> index 000000000..31b45c6e7
> --- /dev/null
> +++ b/test/sql-tap/identifier-characters.test.lua
> @@ -0,0 +1,97 @@
> +#!/usr/bin/env tarantool
> +test = require("sqltester")
> +identifier = require("identifier")
> +test:plan(9)
> +
> +local test_prefix = "identifier_char-"
> +
> +local testcases = {
> + -- table creation testcase
> + {"table",
> + -- create
> + function (id)
> + -- sql autogenerated index name rules add "sqlite_autoindex_") prefix
More than 80 chars.
> + if string.len(id) == box.schema.NAME_MAX then
> + id = string.sub(id, string.len(id))
> + end
> + test:execsql(string.format("create table \"%s\" (a primary key);", id))
More than 80 chars. We're about to start working on statuc typing. Pls, put
specific type to columns in all new test cases.
> + end,
> + -- cleanup
> + function (id)
> + if string.len(id) == box.schema.NAME_MAX then
> + id = string.sub(id, string.len(id))
> + end
> + test:execsql(string.format("drop table \"%s\";", id))
> + end},
> + {"column name",
> + function (id)
> + test:execsql(string.format("create table table1(a primary key, \"%s\");", id))
Ditto.
> + end,
> + function (id)
> + test:execsql(string.format("drop table table1;", id))
> + end},
> + {"view as select",
> + function (id)
> + test:execsql(string.format("create view \"%s\" as select * from test;", id))
More than 80 chars.
> + end,
> + function (id)
> + test:execsql(string.format("drop view \"%s\";", id))
> + end},
> + {"view column name",
> + function (id)
> + test:execsql(string.format("create view v1 (\"%s\") as select b from test;", id))
More than 80 chars.
> + end,
> + function (id)
> + test:execsql(string.format("drop view v1;", id))
> + end},
> + {"as column name",
> + function (id)
> + test:execsql(string.format("create view v1 as select b as \"%s\" from test;", id))
Ditto.
> + end,
> + function (id)
> + test:execsql(string.format("drop view v1;", id))
> + end},
> + {"index name",
> + function (id)
> + test:execsql(string.format("create index \"%s\" on test(b,c);", id))
Ditto.
> + end,
> + function (id)
> + test:execsql(string.format("drop index \"%s\" on test", id))
> + end},
> + {"savepoint name",
> + function (id)
> + test:execsql("begin")
> + local ok, res = pcall(test.execsql, test, string.format("savepoint \"%s\"", id))
Ditto.
> + test:execsql("commit")
> + if ok == false then error(res) end
> + end,
> + function (id) end},
> + {"trigger name",
> + function (id)
> + test:execsql(string.format([[
> + CREATE TRIGGER "%s" UPDATE ON test
> + BEGIN
> + SELECT RAISE(ABORT, 'newer');
> + END;]], id));
> + end,
> + function (id)
> + test:execsql(string.format("drop trigger \"%s\"", id))
> + end}
> +}
> +
> +test:do_execsql_test(
> + test_prefix.."preparition",
> + "create table test(a primary key, b, c)")
> +
> +for _, testcase in ipairs(testcases) do
> + test:do_test(
> + test_prefix..testcase[1],
> + function ()
> + return identifier.run_test(
> + testcase[2],
> + testcase[3])
> + end,
> + "All tests passed")
> +end
> +
> +test:finish_test()
> diff --git a/test/sql-tap/index1.test.lua b/test/sql-tap/index1.test.lua
> index 7bfcf77b1..cf5443e3c 100755
> --- a/test/sql-tap/index1.test.lua
> +++ b/test/sql-tap/index1.test.lua
> @@ -1,6 +1,6 @@
> #!/usr/bin/env tarantool
> test = require("sqltester")
> -test:plan(83)
> +test:plan(79)
>
> --!./tcltestrunner.lua
> -- 2001 September 15
> @@ -1051,58 +1051,11 @@ test:do_catchsql_test(
> -- </index-17.4>
> })
>
> --- The following tests ensure that it is not possible to explicitly name
> --- a schema object with a name beginning with "sqlite_". Granted that is a
> --- little outside the focus of this test scripts, but this has got to be
> --- tested somewhere.
> -test:do_catchsql_test(
> - "index-18.1",
> - [[
> - CREATE TABLE _sqlite_t1(a, b, c);
> - ]], {
> - -- <index-18.1>
> - 1, "object name reserved for internal use: _SQLITE_T1"
> - -- </index-18.1>
> - })
> -
> -test:do_catchsql_test(
> - "index-18.2",
> - [[
> - CREATE INDEX _sqlite_i1 ON t7(c);
> - ]], {
> - -- <index-18.2>
> - 1, "object name reserved for internal use: _SQLITE_I1"
> - -- </index-18.2>
> - })
> -
> -test:do_catchsql_test(
> - "index-18.3",
> - [[
> - CREATE VIEW _sqlite_v1 AS SELECT * FROM t7;
> - ]], {
> - -- <index-18.3>
> - 1, "object name reserved for internal use: _SQLITE_V1"
> - -- </index-18.3>
> - })
> -
> -test:do_catchsql_test(
> - "index-18.4",
> - [[
> - CREATE TRIGGER _sqlite_tr1 BEFORE INSERT ON t7 BEGIN SELECT 1; END;
> - ]], {
> - -- <index-18.4>
> - 1, "object name reserved for internal use: _SQLITE_TR1"
> - -- </index-18.4>
> - })
> -
> test:do_execsql_test(
> - "index-18.5",
> + "index-17.5",
> [[
> DROP TABLE t7;
> ]], {
> - -- <index-18.5>
> -
> - -- </index-18.5>
> })
>
>
> diff --git a/test/sql-tap/suite.ini b/test/sql-tap/suite.ini
> index 64713cdc3..0bc9e83dc 100644
> --- a/test/sql-tap/suite.ini
> +++ b/test/sql-tap/suite.ini
> @@ -1,5 +1,5 @@
> [default]
> core = app
> description = Database tests with #! using TAP
> -lua_libs = lua/sqltester.lua ../sql/lua/sql_tokenizer.lua
> +lua_libs = lua/sqltester.lua ../sql/lua/sql_tokenizer.lua ../box/lua/identifier.lua
> is_parallel = True
> --
> 2.14.1
>
More information about the Tarantool-patches
mailing list