[tarantool-patches] Re: [PATCH v1 1/1] sql: move space existence check to VDBE

n.pettik korablev at tarantool.org
Sat May 11 14:56:21 MSK 2019



> On 8 May 2019, at 20:02, imeevma at tarantool.org wrote:
> 
> Before this patch, the existence of space was checked for the
> CREATE TABLE or CREATE VIEW statements during the parsing. If the
> space already exists, an error has been set and cleanup is
> performed. But, if the statement contained 'IF NOT EXIST', the
> cleanup was performed, but the error was not set.

-> Meanwhile, create_foreign_key() assumes that if
create_table_def->new_space is NULL, then we are
dealing with ALTER TABLE statement. This in turn false,
since ctd->new_space is nullified also in case of already
existing table.

> This causes an
> error when creating a foreign key during space creation.

Not error, but segmentation fault.

> This patch moves this check to VDBE. Thus, the parsing should now
> be properly closed before performing this check.

-> parsing now is always processed till the end as in case space
doesn’t exist.

> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 6051a25..a25479e 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> 
> @@ -1143,6 +1133,23 @@ sqlEndTable(struct Parse *pParse)
> 	struct Vdbe *v = sqlGetVdbe(pParse);
> 	if (NEVER(v == 0))
> 		return;
> +	const char *space_name =
> +		sql_name_from_token(db, &pParse->create_table_def.base.name);

Why not using new_space->def->name ?

> +	char *name_copy = sqlDbStrDup(db, space_name);
> +	if (name_copy == NULL)
> +		goto cleanup;
> +	int name_reg = ++pParse->nMem;
> +	sqlVdbeAddOp4(pParse->pVdbe, OP_String8, 0, name_reg, 0, name_copy,
> +		      P4_DYNAMIC);

In vdbe_emit_space_create() name of space is duplicated as well,
so to avoid processing copy twice we can pass register holding the
name of space to vdbe_emut_space_create().

Consider this diff:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a25479edc..61f4f4127 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -833,7 +833,7 @@ error:
  */
 static void
 vdbe_emit_space_create(struct Parse *pParse, int space_id_reg,
-                      struct space *space)
+                      int space_name_reg, struct space *space)
 {
        Vdbe *v = sqlGetVdbe(pParse);
        int iFirstCol = ++pParse->nMem;
@@ -862,9 +862,7 @@ vdbe_emit_space_create(struct Parse *pParse, int space_id_reg,
        sqlVdbeAddOp2(v, OP_SCopy, space_id_reg, iFirstCol /* spaceId */ );
        sqlVdbeAddOp2(v, OP_Integer, effective_user()->uid,
                          iFirstCol + 1 /* owner */ );
-       sqlVdbeAddOp4(v, OP_String8, 0, iFirstCol + 2 /* name */ , 0,
-                         sqlDbStrDup(pParse->db, space->def->name),
-                         P4_DYNAMIC);
+       sqlVdbeAddOp2(v, OP_SCopy, space_name_reg, iFirstCol + 2);
        sqlVdbeAddOp4(v, OP_String8, 0, iFirstCol + 3 /* engine */ , 0,
                          sqlDbStrDup(pParse->db, space->def->engine_name),
                          P4_DYNAMIC);
@@ -1133,16 +1131,20 @@ sqlEndTable(struct Parse *pParse)
        struct Vdbe *v = sqlGetVdbe(pParse);
        if (NEVER(v == 0))
                return;
-       const char *space_name =
-               sql_name_from_token(db, &pParse->create_table_def.base.name);
-       char *name_copy = sqlDbStrDup(db, space_name);
-       if (name_copy == NULL)
+       /*
+        * Firstly, check if space with given name already exists.
+        * In case IF NOT EXISTS clause is specified and table
+        * exists, we will silently halt VDBE execution.
+        */
+       char *space_name_copy =
+               sqlDbStrDup(db, new_space->def->name);
+       if (space_name_copy == NULL)
                goto cleanup;
        int name_reg = ++pParse->nMem;
-       sqlVdbeAddOp4(pParse->pVdbe, OP_String8, 0, name_reg, 0, name_copy,
+       sqlVdbeAddOp4(pParse->pVdbe, OP_String8, 0, name_reg, 0, space_name_copy,
                      P4_DYNAMIC);
        const char *error_msg =
-               tt_sprintf(tnt_errcode_desc(ER_SPACE_EXISTS), space_name);
+               tt_sprintf(tnt_errcode_desc(ER_SPACE_EXISTS), space_name_copy);
        bool no_err = pParse->create_table_def.base.if_not_exist;
        if (vdbe_emit_halt_with_presence_test(pParse, BOX_SPACE_ID, 2,
                                              name_reg, 1, ER_SPACE_EXISTS,
@@ -1151,7 +1153,7 @@ sqlEndTable(struct Parse *pParse)
                goto cleanup;
 
        int reg_space_id = getNewSpaceId(pParse);
-       vdbe_emit_space_create(pParse, reg_space_id, new_space);
+       vdbe_emit_space_create(pParse, reg_space_id, name_reg, new_space);
        for (uint32_t i = 0; i < new_space->index_count; ++i) {
                struct index *idx = new_space->index[i];
                vdbe_emit_create_index(pParse, new_space->def, idx->def,
@@ -1309,7 +1311,7 @@ sql_create_view(struct Parse *parse_context)
                goto create_view_fail;
 
        vdbe_emit_space_create(parse_context, getNewSpaceId(parse_context),
-                              space);
+                              name_reg, space);

> diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
> index 5b793c0..7881cff 100755
> --- a/test/sql-tap/table.test.lua
> +++ b/test/sql-tap/table.test.lua
> @@ -1,6 +1,6 @@
> #!/usr/bin/env tarantool
> test = require("sqltester")
> -test:plan(77)
> +test:plan(79)
> 
> --!./tcltestrunner.lua
> -- 2001 September 15
> 
> @@ -1442,4 +1442,30 @@ test:do_execsql_test(
> 
>         -- </check-23.cleanup>
>     })
> +
> +--
> +-- gh-4196: Error creating table twice.

-> Segmentation fault happened when IF NOT EXISTS
clause was specified during creation of space featuring 
FK constraints.

> +--
> +test:do_execsql_test(

I’d make this test be catchsql to indicate that it should
test that no error is raised.

> +	"table-24.1",
> +	[[
> +		CREATE TABLE IF NOT EXISTS a1 (i INT PRIMARY KEY);
> +		CREATE TABLE IF NOT EXISTS b1 (i INT PRIMARY KEY, j INT, CONSTRAINT aa FOREIGN KEY(j) REFERENCES a1(i));
> +		CREATE TABLE IF NOT EXISTS b1 (i INT PRIMARY KEY, j INT, CONSTRAINT aa FOREIGN KEY(j) REFERENCES a1(i));
> +	]], {
> +		-- <table-24.1>
> +		-- </table-24.1>
> +	})
> +





More information about the Tarantool-patches mailing list