Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH] sql: do not let VALUES statement treat the type definition keywords as values
@ 2019-01-25 10:50 Stanislav Zudin
  2019-01-28 17:30 ` [tarantool-patches] " n.pettik
  0 siblings, 1 reply; 2+ messages in thread
From: Stanislav Zudin @ 2019-01-25 10:50 UTC (permalink / raw)
  To: tarantool-patches, korablev; +Cc: Stanislav Zudin

The "box.sql.execute('values(blob)')" causes an accert in the expression processing, because the parser doesn't distinguish the keyword "BLOB" from the binary value (in the form X'hex').

This fix adds an additional checks in the SQL grammar. Thus the expressions such as "VALUES(BLOB)", "SELECT FLOAT" and so on are treated as a syntax errors.

Closes #3888
---
 extra/mkkeywordhash.c                        |  6 +-
 src/box/sql/parse.y                          | 11 ++--
 test/sql/gh-3888-values-blob-assert.result   | 65 ++++++++++++++++++++
 test/sql/gh-3888-values-blob-assert.test.lua | 37 +++++++++++
 4 files changed, 112 insertions(+), 7 deletions(-)
 create mode 100644 test/sql/gh-3888-values-blob-assert.result
 create mode 100644 test/sql/gh-3888-values-blob-assert.test.lua

diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
index d92b9eeb7..2b21cb863 100644
--- a/extra/mkkeywordhash.c
+++ b/extra/mkkeywordhash.c
@@ -109,7 +109,7 @@ static Keyword aKeywordTable[] = {
   { "BEFORE",                 "TK_BEFORE",      TRIGGER,          false },
   { "BEGIN",                  "TK_BEGIN",       TRIGGER,          true  },
   { "BETWEEN",                "TK_BETWEEN",     ALWAYS,           true  },
-  { "BLOB",                   "TK_BLOB",        RESERVED,         true  },
+  { "BLOB",                   "TK_BLOB_KW",     RESERVED,         true  },
   { "BY",                     "TK_BY",          ALWAYS,           true  },
   { "CASCADE",                "TK_CASCADE",     FKEY,             false },
   { "CASE",                   "TK_CASE",        ALWAYS,           true  },
@@ -235,12 +235,12 @@ static Keyword aKeywordTable[] = {
   { "DOUBLE",                 "TK_DOUBLE",      RESERVED,         true  },
   { "ELSEIF",                 "TK_STANDARD",    RESERVED,         true  },
   { "FETCH",                  "TK_STANDARD",    RESERVED,         true  },
-  { "FLOAT",                  "TK_FLOAT",       RESERVED,         true  },
+  { "FLOAT",                  "TK_FLOAT_KW",    RESERVED,         true  },
   { "FUNCTION",               "TK_STANDARD",    RESERVED,         true  },
   { "GET",                    "TK_STANDARD",    RESERVED,         true  },
   { "GRANT",                  "TK_STANDARD",    RESERVED,         true  },
   { "INT",                    "TK_INT",         RESERVED,         true  },
-  { "INTEGER",                "TK_INTEGER",     RESERVED,         true  },
+  { "INTEGER",                "TK_INTEGER_KW",  RESERVED,         true  },
   { "INOUT",                  "TK_STANDARD",    RESERVED,         true  },
   { "INSENSITIVE",            "TK_STANDARD",    RESERVED,         true  },
   { "ITERATE",                "TK_STANDARD",    RESERVED,         true  },
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 0bcf41594..73a324522 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -211,7 +211,7 @@ columnname(A) ::= nm(A) typedef(Y). {sqlite3AddColumn(pParse,&A,&Y);}
   IGNORE INITIALLY INSTEAD NO MATCH PLAN
   QUERY KEY OFFSET RAISE RELEASE REPLACE RESTRICT
 %ifdef SQLITE_OMIT_COMPOUND_SELECT
-  INTERSECT 
+  INTERSECT
 %endif SQLITE_OMIT_COMPOUND_SELECT
   RENAME CTIME_KW IF
   .
@@ -823,12 +823,15 @@ idlist(A) ::= nm(Y).
         p->affinity = AFFINITY_TEXT;
         break;
       case TK_BLOB:
+      case TK_BLOB_KW:
         p->affinity = AFFINITY_BLOB;
         break;
       case TK_INTEGER:
+      case TK_INTEGER_KW:
         p->affinity = AFFINITY_INTEGER;
         break;
       case TK_FLOAT:
+      case TK_FLOAT_KW:
         p->affinity = AFFINITY_REAL;
         break;
       }
@@ -1469,7 +1472,7 @@ wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
 /* Primitive types. */
 %type typedef {struct type_def}
 typedef(A) ::= TEXT . { A.type = AFFINITY_TEXT; }
-typedef(A) ::= BLOB . { A.type = AFFINITY_BLOB; }
+typedef(A) ::= BLOB_KW . { A.type = AFFINITY_BLOB; }
 typedef(A) ::= DATE . { A.type = AFFINITY_REAL; }
 typedef(A) ::= TIME . { A.type = AFFINITY_REAL; }
 typedef(A) ::= DATETIME . { A.type = AFFINITY_REAL; }
@@ -1496,8 +1499,8 @@ typedef(A) ::= VARCHAR char_len(B) . {
 
 %type number_typedef {struct type_def}
 typedef(A) ::= number_typedef(A) .
-number_typedef(A) ::= FLOAT|REAL|DOUBLE . { A.type = AFFINITY_REAL; }
-number_typedef(A) ::= INT|INTEGER . { A.type = AFFINITY_INTEGER; }
+number_typedef(A) ::= FLOAT_KW|REAL|DOUBLE . { A.type = AFFINITY_REAL; }
+number_typedef(A) ::= INT|INTEGER_KW . { A.type = AFFINITY_INTEGER; }
 
 %type number_len_typedef {struct type_def}
 number_typedef(A) ::= DECIMAL|NUMERIC|NUM number_len_typedef(B) . {
diff --git a/test/sql/gh-3888-values-blob-assert.result b/test/sql/gh-3888-values-blob-assert.result
new file mode 100644
index 000000000..2e025f156
--- /dev/null
+++ b/test/sql/gh-3888-values-blob-assert.result
@@ -0,0 +1,65 @@
+-- sql: assertion fault on VALUES #3888
+-- Very simple query leads to assertion fault:
+--
+-- box.cfg{}
+-- box.sql.execute("values(blob)")
+-- Assertion failed: (pExpr->u.zToken[0] == 'x' || pExpr->u.zToken[0] == 'X'),
+-- function sqlite3ExprCodeTarget,
+-- file /Users/n.pettik/tarantool/src/box/sql/expr.c, line 3759.
+-- Abort trap: 6
+--
+test_run = require('test_run').new()
+---
+...
+engine = test_run:get_cfg('engine')
+---
+...
+box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+---
+...
+-- check 'VALUES' against typedef keywords (should fail)
+box.sql.execute('VALUES(blob)')
+---
+- error: 'near "blob": syntax error'
+...
+box.sql.execute('VALUES(float)')
+---
+- error: 'near "float": syntax error'
+...
+-- check 'SELECT' against typedef keywords (should fail)
+box.sql.execute('SELECT blob')
+---
+- error: 'near "blob": syntax error'
+...
+box.sql.execute('SELECT float')
+---
+- error: 'near "float": syntax error'
+...
+-- check 'VALUES' against ID (should fail)
+box.sql.execute('VALUES(TheColumnName)')
+---
+- error: 'no such column: THECOLUMNNAME'
+...
+-- check 'SELECT' against ID (should fail)
+box.sql.execute('SELECT TheColumnName')
+---
+- error: 'no such column: THECOLUMNNAME'
+...
+-- check 'VALUES' well-formed expression  (returns value)
+box.sql.execute('VALUES(-0.5e-2)')
+---
+- - [-0.005]
+...
+box.sql.execute('SELECT X\'507265766564\'')
+---
+- - ['Preved']
+...
+-- check 'SELECT' well-formed expression  (return value)
+box.sql.execute('SELECT 3.14')
+---
+- - [3.14]
+...
+box.sql.execute('SELECT X\'4D6564766564\'')
+---
+- - ['Medved']
+...
diff --git a/test/sql/gh-3888-values-blob-assert.test.lua b/test/sql/gh-3888-values-blob-assert.test.lua
new file mode 100644
index 000000000..dafb73716
--- /dev/null
+++ b/test/sql/gh-3888-values-blob-assert.test.lua
@@ -0,0 +1,37 @@
+-- sql: assertion fault on VALUES #3888
+-- Very simple query leads to assertion fault:
+--
+-- box.cfg{}
+-- box.sql.execute("values(blob)")
+-- Assertion failed: (pExpr->u.zToken[0] == 'x' || pExpr->u.zToken[0] == 'X'),
+-- function sqlite3ExprCodeTarget,
+-- file /Users/n.pettik/tarantool/src/box/sql/expr.c, line 3759.
+-- Abort trap: 6
+--
+test_run = require('test_run').new()
+engine = test_run:get_cfg('engine')
+box.sql.execute('pragma sql_default_engine=\''..engine..'\'')
+
+-- check 'VALUES' against typedef keywords (should fail)
+box.sql.execute('VALUES(blob)')
+box.sql.execute('VALUES(float)')
+
+-- check 'SELECT' against typedef keywords (should fail)
+box.sql.execute('SELECT blob')
+box.sql.execute('SELECT float')
+
+-- check 'VALUES' against ID (should fail)
+box.sql.execute('VALUES(TheColumnName)')
+
+-- check 'SELECT' against ID (should fail)
+box.sql.execute('SELECT TheColumnName')
+
+-- check 'VALUES' well-formed expression  (returns value)
+box.sql.execute('VALUES(-0.5e-2)')
+box.sql.execute('SELECT X\'507265766564\'')
+
+-- check 'SELECT' well-formed expression  (return value)
+box.sql.execute('SELECT 3.14')
+box.sql.execute('SELECT X\'4D6564766564\'')
+
+
-- 
2.17.1

---
Branch: https://github.com/tarantool/tarantool/tree/sz/gh-3888-values-blob-assert
Issue: https://github.com/tarantool/tarantool/issues/3888

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [tarantool-patches] Re: [PATCH] sql: do not let VALUES statement treat the type definition keywords as values
  2019-01-25 10:50 [tarantool-patches] [PATCH] sql: do not let VALUES statement treat the type definition keywords as values Stanislav Zudin
@ 2019-01-28 17:30 ` n.pettik
  0 siblings, 0 replies; 2+ messages in thread
From: n.pettik @ 2019-01-28 17:30 UTC (permalink / raw)
  To: tarantool-patches; +Cc: szudin

Decorations are not such vital as content of patch surely,
but still should be OK.

Firstly,  try to limit the subject line to 50 characters or so.

Then, if you send patch v2, please indicate it to format-patch
utility by passing --subject-prefix='PATCH v2’

> The "box.sql.execute('values(blob)')" causes an accert

Nit: accert -> assert.

> in the expression processing, because the parser doesn't distinguish the keyword "BLOB" from the binary value (in the form X'hex’).
> 
> This fix adds an additional checks in the SQL grammar. Thus the expressions such as "VALUES(BLOB)", "SELECT FLOAT" and so on are treated as a syntax errors.

Wrap the body of commit message to 72 characters or so.

> 
> Closes #3888
> —

Place links to your branch and issue here
(not at the very bottom of patch).

Patch itself is OK, just few nits.

> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index 0bcf41594..73a324522 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -211,7 +211,7 @@ columnname(A) ::= nm(A) typedef(Y). {sqlite3AddColumn(pParse,&A,&Y);}
>   IGNORE INITIALLY INSTEAD NO MATCH PLAN
>   QUERY KEY OFFSET RAISE RELEASE REPLACE RESTRICT
> %ifdef SQLITE_OMIT_COMPOUND_SELECT
> -  INTERSECT 
> +  INTERSECT

Extra diff.

> %endif SQLITE_OMIT_COMPOUND_SELECT
>   RENAME CTIME_KW IF
>   .
> @@ -823,12 +823,15 @@ idlist(A) ::= nm(Y).
>         p->affinity = AFFINITY_TEXT;
>         break;
>       case TK_BLOB:
> +      case TK_BLOB_KW:
>         p->affinity = AFFINITY_BLOB;

Why do we need to assign affinity to keywords?

>         break;
>       case TK_INTEGER:
> +      case TK_INTEGER_KW:
>         p->affinity = AFFINITY_INTEGER;
>         break;
>       case TK_FLOAT:
> +      case TK_FLOAT_KW:
>         p->affinity = AFFINITY_REAL;
>         break;
>       }
> diff --git a/test/sql/gh-3888-values-blob-assert.test.lua b/test/sql/gh-3888-values-blob-assert.test.lua
> new file mode 100644
> index 000000000..dafb73716
> --- /dev/null
> +++ b/test/sql/gh-3888-values-blob-assert.test.lua
> @@ -0,0 +1,37 @@
> +-- sql: assertion fault on VALUES #3888
> +-- Very simple query leads to assertion fault:
> +--
> +-- box.cfg{}
> +-- box.sql.execute("values(blob)")
> +-- Assertion failed: (pExpr->u.zToken[0] == 'x' || pExpr->u.zToken[0] == 'X'),
> +-- function sqlite3ExprCodeTarget,
> +-- file /Users/n.pettik/tarantool/src/box/sql/expr.c, line 3759.
> +-- Abort trap: 6

To be honest, I’d rather put short description of bug,
then copying content of ticket (which is available on github).

Sort of:

Make sure that tokens representing values of integer, float,
and blob constants are different from tokens representing
keywords of the same names.

> -- 
> 2.17.1
> 
> ---
> Branch: https://github.com/tarantool/tarantool/tree/sz/gh-3888-values-blob-assert
> Issue: https://github.com/tarantool/tarantool/issues/3888

Next time move these links up.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-01-28 17:30 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-25 10:50 [tarantool-patches] [PATCH] sql: do not let VALUES statement treat the type definition keywords as values Stanislav Zudin
2019-01-28 17:30 ` [tarantool-patches] " n.pettik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox