[tarantool-patches] Re: [PATCH v2 1/5] sql: introduce structs assembling DDL arguments during parsing

n.pettik korablev at tarantool.org
Thu Feb 28 01:56:57 MSK 2019


First of all, many thanks for providing detailed analysis,
even despite insignificance of this patch (very first version
was ready about two months ago and IMHO it was OK;
didn’t expect that suggested refactoring might take so long).

> On 15 Feb 2019, at 23:13, Vladislav Shpilevoy <v.shpilevoy at tarantool.org> wrote:
> Hi! Thanks for the fixes!
>> suggest ‘_prepare’, ‘_assebmle’, ‘_fill'
>> If you are sure about ‘_create' naming, I will fix it.
>>>> +  sqlite3StartTable(pParse);
>>>>  }
>>>>  createkw(A) ::= CREATE(A).  {disableLookaside(pParse);}
>>>>  @@ -237,8 +239,15 @@ nm(A) ::= id(A). {
>>>>  carglist ::= carglist cconsdef.
>>>>  carglist ::= .
>>>>  cconsdef ::= cconsname ccons.
>>>> -cconsname ::= CONSTRAINT nm(X).           {pParse->constraintName = X;}
>>>> -cconsname ::= .                           {pParse->constraintName.n = 0;}
>>>> +cconsname ::= cconsname_start cconsname_parse .
>>>> +cconsname_start ::= . {
>>>> +  /* Prepare base members for re-usage. */
>>>> +  memset(&pParse->create_index_def, 0, sizeof(struct create_index_def));
>>>> +}
>>>> +cconsname_parse ::= CONSTRAINT nm(X). {
>>>> +  create_entity_def_init(&pParse->create_index_def, X, false);
>>> 
>>> 7. Why in case of any constraint start, you reset only index_def? UNIQUE/PK
>>> are not the only existing constraints.
>>> Also, memset(0) is not scalable
>>> solution - you need a separate reset() function, or something like that in
>>> case if in future parse_def.h structures will grow in size and complexity,
>>> and 0 becomes not default value of some attribute.
>> Well, imho it is too much.
> I did not understand. What is too much?

Too much assuming that some day we will have to initialize
members with non-zero values. Anyway, it doesn’t really
matter now, I just passed pointer to create_constraint_def:

@@ -242,11 +241,10 @@ carglist ::= .
 cconsdef ::= cconsname ccons.
 cconsname ::= cconsname_start cconsname_parse .
 cconsname_start ::= . {
-  /* Prepare base members for re-usage. */
-  memset(&pParse->create_index_def, 0, sizeof(struct create_constraint_def));
+  memset(&pParse->create_index_def.base, 0, sizeof(struct create_constraint_def));
 }

So, we don’t need to add to union one more member. Is this way OK?

>> Actually, here we really care only about
>> zeroing several pointers and some size params and don’t care
>> about the rest.
> 
> Yes, but here you assume, that index_def always lies in the
> same memory as all other constraint objects, and we will never
> store more pointers in the rest memory > sizeof(index_def).
> 
>> This is first rule to be called when some constraint
>> is created. What is more, now terminal initialisers fill all fields, except
>> for base structs. Hence, now it is enough to reset memory layout
>> up to struct create_constraint_def:
> 
> Your code below assumes the thing I said above, so please, add a
> struct create_constraint_def to union in struct Parse and use it
> here. Like this:

See solution above, I guess it is more suitable.

> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index 22b5669d8..83c0fc465 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -244,7 +244,7 @@ cconsdef ::= cconsname ccons.
> cconsname ::= cconsname_start cconsname_parse .
> cconsname_start ::= . {
>   /* Prepare base members for re-usage. */
> -  memset(&pParse->create_index_def, 0, sizeof(struct create_constraint_def));
> +  memset(&pParse->constraint_def, 0, sizeof(pParse->constraint_def));
> }
> cconsname_parse ::= CONSTRAINT nm(X). {
>   create_entity_def_init(&pParse->create_index_def.base.base, X, false);
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 4e6806be8..8566fe404 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2731,6 +2731,7 @@ struct Parse {
> 	 * from parse.y
> 	 */
> 	union {
> +		struct create_constraint_def constraint_def;
> 		struct create_ck_def create_ck_def;
> 		struct create_fk_def create_fk_def;
> 		struct create_index_def create_index_def;
> 
>>>> diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
>>>> +enum entity_type {
>>>> +	ENTITY_TYPE_TABLE = 0,
>>>> +	ENTITY_TYPE_INDEX,
>>>> +	ENTITY_TYPE_TRIGGER,
>>>> +	ENTITY_TYPE_CK,
>>>> +	ENTITY_TYPE_FK,
>>>> +	entity_type_MAX
>>> 
>>> 9. These entity_types are useless. Even being used in asserts,
>>> they do not prevent errors, allowing to create an instance of
>>> create_entity_def, but use it as drop_entity_def, because
>>> base.entity_type in both cases can be, for example,
>>> ENTITY_TYPE_INDEX.
>>> 
>>> Please, use not object entity types, but def types:
>>> 
>>>    ALTER_DEF_CREATE_TABLE/DROP_TABLE/RENAME.../...INDEX.../...
>>> 
>>> Or split entity type and action, so as to have something like
>>> this:
>>> 
>>>    def.entity = ALTER_DEF_TABLE;
>>>    def.action = ALTER_DEF_RENAME;
>> I’d better choose this way. Sorry, I can’t spot diff
>> of particularly this fix, since it has been entangled with diffs
>> to other comments. But I will attach whole diff at the end of letter.
>>> By the way, please, rename ENTITY_TYPE_CK to ENTITY_TYPE_CHECK.
>>> Three letters 'HEC' economy is not worth the name obfuscation.
>> It is common abbreviation (the same as FK), and AFAIR was even
>> suggested by Konstantin in one of reviews (mb not to this patch).
>> It simply makes name be similar to ENTITY_TYPE_FK.
> 
> I see CK here first time. We've never used this abbreviation as I
> remember. We use FK not because it is a policy, but because it is
> just too long without contraction.
> 
> Otherwise we would have ENTITY_TYPE_IN instead of _INDEX,
> ENTITY_TYPE_TA instead of _TABLE, ENTITY_TYPE_TR instead of _TRIGGER.

Ok, re-asked Konstantin about using CK as an abbreviation for CHECK,
and he gave positive answer. 

>> @@ -264,6 +283,7 @@ create_fk_def_init(struct create_fk_def *fk_def, struct ExprList *child_cols,
>>         fk_def->parent_name = parent_name;
>>         fk_def->parent_cols = parent_cols;
>>         fk_def->actions = actions;
>> +       ((struct alter_entity_def *) fk_def)->entity_type = ENTITY_TYPE_FK;
>>  }
>> I know you asked me to choose between base struct and cast.
>> However, I think here cast is much more suitable: these structures
>> are terminated so we have to write base.base.base.
>> On the other hand, almost in all places one or two nesting
>> base usage looks better. I can revert this change with ease,
>> if you want to.
> 
> base.base.base is bad, but just one base is good, where possible.
> 
> 
> Below I listed the calls you do to create each 'terminal' structure.
> 
> 	TABLE RENAME:
> 	alter_entity_def_init
> 	rename_entity_def_init
> 
> 	TABLE CREATE
> 	alter_entity_def_init
> 	create_entity_def_init
> 	create_table_def_init
> 
> 	TABLE DROP:
> 	alter_entity_def_init
> 	drop_entity_def_init
> 
> 	TABLE ADD FK:
> 	alter_entity_def_init
> 	create_entity_def_init
> 	create_constraint_def_init
> 	create_fk_def_init
> 
> 	DROP FK:
> 	alter_entity_def_init
> 	drop_entity_def_init
> 
> 	DROP INDEX:
> 	alter_entity_def_init
> 	drop_entity_def_init
> 
> 	ADD INDEX:
> 	alter_entity_def_init
> 	create_entity_def_init
> 	create_index_def_init
> 
> 	CHECK:
>        alter_entity_def_init
>        create_entity_def_init
> 	create_ck_def_init
> 
> 	CREATE TRIGGER:
> 	alter_entity_def_init
> 	create_entity_def_init
> 	create_trigger_def_init
> 
> 	DROP TRIGGER:
> 	alter_entity_def_init
> 	drop_entity_def_init
> 
> Here are some problems with what you said earlier and with
> the whole structure.
> 
> Firstly, some of sequences are not finished with a an explicit
> terminal. For example, *all* DROPs are finished with an abstract
> drop_entity_def_init() taking an entity type explicitly. Despite
> the words you said earlier that you initialize everything in steps.
> So in the case of DROP the rule is violated already. I think, that
> you should define static inline helpers in parse_def.h with names
> drop_table_def_init(), drop_index_def_init(), drop_check_def_init()
> etc.

Drop procedure (i.e. arguments filling) is the same for all entities
(this situation is likely to remain unchanged). Does it make any
sense to duplicate code without any necessity? What these
helpers are supposed to do?

> Secondly, some words about create_entity_def_init(). It is the
> most arguable function for a public usage (out of parse_def.h)
> because hardly I can imagine a situation when you parsed a word
> CREATE, but can not parse a next one: TABLE, TRIGGER, CONSTRAINT etc.
> And we see it in the lists above. Below I listed all of this function
> usage cases and how you can replace that function here.
> 
> - create_entity_def_init() in create_table ::= rule, line 172. Here
>  it is followed by create_table_def_init() on the next line.
> 
> - create_entity_def_init() in cconsname_parse ::= rule line 250.
>  This rule is a prefix for a constraint definition, so this function
>  can be substituted with create_constraint_def_init().
> 
> - create_entity_def_init() in cmd ::= rule (about CREATE VIEW),
>  line 400. Here we see, that 1) you missed ENTITY_TYPE_VIEW,
>  2) create_view_def_init() can be called here.
> 
> - create_entity_def_init() in cmd ::= rule (about CREATE INDEX),
>  line 1246. Here it is followed by create_index_def_init() on the
>  next line.
> 
> - create_entity_def_init() in trigger_decl ::= rule, line 1367.
>  Here it is followed by create_trigger_def_init().
> 
> - create_entity_def_init() in cmd ::= rule (about ALTER TABLE ADD
>  CONSTRAINT FOREIGN KEY), line 1500. Here it is followed by
>  create_constraint_def_init().
> 
> So as you can see create_entity_def_init() is not necessary to use
> out of parse_def.h. You can put it into
> create_table/index/constraint_def().

Ok, done. See updated version at the end of letter.

> Now about create_constraint_def_init(). In your patch it is always
> followed by create_fk_def_init().

I didn’t get this point. create_constraint_def_init() may come before
create_fk_def_init() as well as before create_index_def/create_ck_def.

> But after you did the comment above
> about create_entity_def_init(), you will have create_constraint_def_init()
> in cconsname_parse ::= CONSTRAINT nm(X) rule. And I do not see any
> concrete reasons why can not you remove it from here and use only
> create_index_def_init(), create_ck_def_init() etc in ccons ::= rule.
> You said, that some of pointers should be nullifed, but what pointers
> and why? What if you do not nullify them right after CONSTRAINT word
> is met?

We had to nullify them since in previous versions members of some
structures might leave uninitialised. Now _init funs fill all members,
so there is no any problem.

> Thirdly, about alter_entity_def_init. Even now it is *always*
> followed by create_*_init(), or drop_*_init(), or rename_*_init().
> So I advise you to make this function internal for parse_def.h and
> put its calls into create/drop/rename_*_init().

That’s OK as well.

> The whole idea, as I understood it, was not to just split into a
> random steps, but so as to build an automaton:
> 
> 
>                  +- drop_check
>                  |
>                  +- drop_fk
>                  |
>                  +- drop_table
>                  |
>   drop_entity ---+- drop_index
>  /
> -- rename_entity --- rename_table
>  \
>   create_entity -+- create_index
>                  |
>                  +- create_table
>                  |
>                  +- create_fk
>                  |
>                  +- create_check
> 
> Which in future should be easily expanded to this:
> 
>                   +- drop_check
>                   |
>                   +- drop_fk
>                   |
>                   +- drop_table
>                   |
>  +- drop_entity --+- drop_index
>  |
>  |                +- index_rename
>  |                |
>  |                +- table_add_column
>  |                |
> --+- alter_entity -+- table_rename
>  |                |
>  |                +- table_drop_column
>  |                |
>  |                +- fk_rename
>  |
>  +- create_entity +- create_index
>                   |
>                   +- create_table
>                   |
>                   +- create_fk
>                   |
>                   +- create_check
> 
> So any rule can by finished in two calls. First defines an action,
> second defines an entity and action-entity details. Now some of your
> rules are finished in 2, some in 3, some in 4 steps, and at the end
> of some of them you still are not in a terminal position.

Also, I put two straight preparatory patches which simplify
view creation procedure:

sql: don't compute string for VIEW twice
sql: simplify VIEW creation procedure

Will send them separately after approval of current patch.

Diff (it may be inconsistent a bit, so I also attach whole patch):

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 5ef0f0bd8..ee2ed55ad 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -391,27 +391,26 @@ sql_table_new(Parse *parser, char *name)
  *
  * @param pParse Parser context.
  */
-void
-sqlite3StartTable(struct Parse *pParse)
+struct Table *
+sqlite3StartTable(struct Parse *pParse, struct Token name, bool if_not_exists)
 {
-       Table *pTable;
+       Table *pTable = NULL;
        char *zName = 0;        /* The name of the new table */
        sqlite3 *db = pParse->db;
        struct Vdbe *v = sqlite3GetVdbe(pParse);
        if (v == NULL)
                goto cleanup;
        sqlite3VdbeCountChanges(v);
-       struct Token name = pParse->create_table_def.base.name;
        zName = sqlite3NameFromToken(db, &name);
 
        if (zName == 0)
-               return;
+               return NULL;
        if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK)
                goto cleanup;
 
        struct space *space = space_by_name(zName);
        if (space != NULL) {
-               if (!pParse->create_table_def.base.if_not_exist) {
+               if (!if_not_exists) {
                        sqlite3ErrorMsg(pParse, "table %s already exists",
                                        zName);
                } else {
@@ -423,15 +422,12 @@ sqlite3StartTable(struct Parse *pParse)
        pTable = sql_table_new(pParse, zName);
        if (pTable == NULL)
                goto cleanup;
-
-       pParse->create_table_def.new_table = pTable;
-
        if (!db->init.busy && (v = sqlite3GetVdbe(pParse)) != 0)
                sql_set_multi_write(pParse, true);
 
  cleanup:
        sqlite3DbFree(db, zName);
-       return;
+       return pTable;
 }
 
 /**
@@ -1297,12 +1293,6 @@ sqlite3EndTable(Parse * pParse,  /* Parse context */
                vdbe_emit_create_index(pParse, p->def, idx->def,
                                       reg_space_id, idx->def->iid);
        }
-
-       for (uint32_t i = 0; i < p->space->index_count; ++i) {
-               struct index *idx = p->space->index[i];
-               vdbe_emit_create_index(pParse, p->def, idx->def, reg_space_id,
-                                      idx->def->iid);
-       }
        /*
         * Check to see if we need to create an _sequence table
         * for keeping track of autoincrement keys.
@@ -1370,9 +1360,13 @@ cleanup:
 }
 
 void
-sql_create_view(struct Parse *parse_context, struct Token *begin,
-               struct ExprList *aliases, struct Select *select)
+sql_create_view(struct Parse *parse_context)
 {
+       struct create_view_def *view_def = &parse_context->create_view_def;
+       struct create_entity_def *create_entity_def = &view_def->base;
+       struct alter_entity_def *alter_entity_def = &create_entity_def->base;
+       assert(alter_entity_def->entity_type == ENTITY_TYPE_VIEW);
+       assert(alter_entity_def->alter_action == ALTER_ACTION_CREATE);
        struct sqlite3 *db = parse_context->db;
        struct Table *sel_tab = NULL;
        if (parse_context->nVar > 0) {
@@ -1380,13 +1374,15 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
                                "parameters are not allowed in views");
                goto create_view_fail;
        }
-       sqlite3StartTable(parse_context);
-       struct Table *p = parse_context->create_table_def.new_table;
+       struct Table *p = sqlite3StartTable(parse_context,
+                                           create_entity_def->name,
+                                           create_entity_def->if_not_exist);
        if (p == NULL || parse_context->nErr != 0)
                goto create_view_fail;
-       sel_tab = sqlite3ResultSetOfSelect(parse_context, select);
+       sel_tab = sqlite3ResultSetOfSelect(parse_context, view_def->select);
        if (sel_tab == NULL)
                goto create_view_fail;
+       struct ExprList *aliases = view_def->aliases;
        if (aliases != NULL) {
                if ((int)sel_tab->def->field_count != aliases->nExpr) {
                        sqlite3ErrorMsg(parse_context, "expected %d columns "\
@@ -1397,7 +1393,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
                }
                sqlite3ColumnsFromExprList(parse_context, aliases, p);
                sqlite3SelectAddColumnTypeAndCollation(parse_context, p,
-                                                      select);
+                                                      view_def->select);
        } else {
                assert(sel_tab->def->opts.is_temporary);
                p->def->fields = sel_tab->def->fields;
@@ -1415,6 +1411,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
        if (end.z[0] != ';')
                end.z += end.n;
        end.n = 0;
+       struct Token *begin = view_def->create_start;
        int n = end.z - begin->z;
        assert(n > 0);
        const char *z = begin->z;
@@ -1429,12 +1426,13 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
                parse_context->nErr++;
                goto create_view_fail;
        }
+
        vdbe_emit_space_create(parse_context, getNewSpaceId(parse_context), p);
 
  create_view_fail:
        sqlite3DbFree(db, sel_tab);
-       sql_expr_list_delete(db, aliases);
-       sql_select_delete(db, select);
+       sql_expr_list_delete(db, view_def->aliases);
+       sql_select_delete(db, view_def->select);
        return;
 }
 
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 22b5669d8..4b13fca8b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -168,10 +168,8 @@ cmd ::= ROLLBACK TO savepoint_opt nm(X). {
 //
 cmd ::= create_table create_table_args.
 create_table ::= createkw TABLE ifnotexists(E) nm(Y). {
-  alter_entity_def_init(&pParse->create_table_def.base.base, NULL);
-  create_entity_def_init(&pParse->create_table_def.base, Y, E);
-  create_table_def_init(&pParse->create_table_def);
-  sqlite3StartTable(pParse);
+  create_table_def_init(&pParse->create_table_def, Y, E);
+  pParse->create_table_def.new_table = sqlite3StartTable(pParse, Y, E);
 }
 createkw(A) ::= CREATE(A).  {disableLookaside(pParse);}
 
@@ -243,11 +241,11 @@ carglist ::= .
 cconsdef ::= cconsname ccons.
 cconsname ::= cconsname_start cconsname_parse .
 cconsname_start ::= . {
-  /* Prepare base members for re-usage. */
-  memset(&pParse->create_index_def, 0, sizeof(struct create_constraint_def));
+  memset(&pParse->create_index_def.base, 0, sizeof(struct create_constraint_def));
 }
 cconsname_parse ::= CONSTRAINT nm(X). {
-  create_entity_def_init(&pParse->create_index_def.base.base, X, false);
+  create_constraint_def_init(&pParse->create_index_def.base, NULL, X, false,
+                             false);
 }
 cconsname_parse ::= .
 ccons ::= DEFAULT term(X).            {sqlite3AddDefaultValue(pParse,&X);}
@@ -348,7 +346,7 @@ tcons ::= UNIQUE LP sortlist(X) RP. {
 tcons ::= check_constraint_def .
 tcons ::= FOREIGN KEY LP eidlist(FA) RP
           REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). {
-  create_constraint_def_init(&pParse->create_fk_def.base, D);
+  ((struct create_constraint_def *) &pParse->create_fk_def)->is_deferred = D;
   create_fk_def_init(&pParse->create_fk_def, FA, &T, TA, R);
   sql_create_foreign_key(pParse);
 }
@@ -379,8 +377,7 @@ cmd ::= drop_start(X) drop_table . {
 }
 
 drop_table ::= ifexists(E) fullname(X) . {
-  alter_entity_def_init(&pParse->drop_entity_def.base, X);
-  drop_entity_def_init(&pParse->drop_entity_def, (struct Token) {}, E,
+  drop_entity_def_init(&pParse->drop_entity_def, X, (struct Token) {}, E,
                        ENTITY_TYPE_TABLE);
 }
 
@@ -397,8 +394,8 @@ ifexists(A) ::= .            {A = 0;}
 cmd ::= createkw(X) VIEW ifnotexists(E) nm(Y) eidlist_opt(C)
           AS select(S). {
   if (!pParse->parse_only) {
-    create_entity_def_init(&pParse->create_table_def.base, Y, E);
-    sql_create_view(pParse, &X, C, S);
+    create_view_def_init(&pParse->create_view_def, Y, &X, C, S, E);
+    sql_create_view(pParse);
   } else {
     sql_store_select(pParse, S);
   }
@@ -1241,10 +1238,9 @@ paren_exprlist(A) ::= LP exprlist(X) RP.  {A = X;}
 //
 cmd ::= createkw uniqueflag(U) INDEX ifnotexists(NE) nm(X)
         ON nm(Y) LP sortlist(Z) RP. {
-  alter_entity_def_init((struct alter_entity_def *) &pParse->create_index_def,
-                        sqlite3SrcListAppend(pParse->db, 0, &Y));
-  create_entity_def_init((struct create_entity_def *) &pParse->create_index_def,
-                         X, NE);
+  create_constraint_def_init(&pParse->create_index_def.base,
+                             sqlite3SrcListAppend(pParse->db, 0, &Y), X, NE,
+                             false);
   create_index_def_init(&pParse->create_index_def, Z, U, SORT_ORDER_ASC);
   sql_create_index(pParse);
 }
@@ -1309,8 +1305,7 @@ collate(C) ::= COLLATE id.   {C = 1;}
 ///////////////////////////// The DROP INDEX command /////////////////////////
 //
 cmd ::= DROP INDEX ifexists(E) nm(X) ON fullname(Y).   {
-  alter_entity_def_init(&pParse->drop_entity_def.base, Y);
-  drop_entity_def_init(&pParse->drop_entity_def, X, E, ENTITY_TYPE_INDEX);
+  drop_entity_def_init(&pParse->drop_entity_def, Y, X, E, ENTITY_TYPE_INDEX);
   sql_drop_index(pParse);
 }
 
@@ -1362,10 +1357,8 @@ cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
 trigger_decl(A) ::= TRIGGER ifnotexists(NOERR) nm(B)
                     trigger_time(C) trigger_event(D)
                     ON fullname(E) foreach_clause when_clause(G). {
-  struct create_trigger_def *trigger_def = &pParse->create_trigger_def;
-  alter_entity_def_init((struct alter_entity_def *) trigger_def, E);
-  create_entity_def_init((struct create_entity_def *) trigger_def, B, NOERR);
-  create_trigger_def_init(trigger_def, C, D.a, D.b, G);
+  create_trigger_def_init(&pParse->create_trigger_def, E, B, C, D.a, D.b,
+                          G, NOERR);
   sql_trigger_begin(pParse);
   A = B; /*A-overwrites-T*/
 }
@@ -1476,8 +1469,7 @@ raisetype(A) ::= FAIL.      {A = ON_CONFLICT_ACTION_FAIL;}
 
 ////////////////////////  DROP TRIGGER statement //////////////////////////////
 cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
-  alter_entity_def_init(&pParse->drop_entity_def.base, X);
-  drop_entity_def_init(&pParse->drop_entity_def, (struct Token){}, NOERR,
+  drop_entity_def_init(&pParse->drop_entity_def, X, (struct Token){}, NOERR,
                        ENTITY_TYPE_TRIGGER);
   sql_drop_trigger(pParse);
 }
@@ -1488,24 +1480,20 @@ cmd ::= ANALYZE nm(X).          {sqlite3Analyze(pParse, &X);}
 
 //////////////////////// ALTER TABLE table ... ////////////////////////////////
 cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
-  alter_entity_def_init(&pParse->rename_entity_def.base, X);
-  rename_entity_def_init(&pParse->rename_entity_def, Z);
+  rename_entity_def_init(&pParse->rename_entity_def, X, Z);
   sql_alter_table_rename(pParse);
 }
 
 cmd ::= ALTER TABLE fullname(X) ADD CONSTRAINT nm(Z) FOREIGN KEY
         LP eidlist(FA) RP REFERENCES nm(T) eidlist_opt(TA) refargs(R)
         defer_subclause_opt(D). {
-  alter_entity_def_init((struct alter_entity_def *) &pParse->create_fk_def, X);
-  create_entity_def_init(&pParse->create_fk_def.base.base, Z, false);
-  create_constraint_def_init(&pParse->create_fk_def.base, D);
+  create_constraint_def_init(&pParse->create_fk_def.base, X, Z, D, false);
   create_fk_def_init(&pParse->create_fk_def, FA, &T, TA, R);
   sql_create_foreign_key(pParse);
 }
 
 cmd ::= ALTER TABLE fullname(X) DROP CONSTRAINT nm(Z). {
-  alter_entity_def_init(&pParse->drop_entity_def.base, X);
-  drop_entity_def_init(&pParse->drop_entity_def, Z, false, ENTITY_TYPE_FK);
+  drop_entity_def_init(&pParse->drop_entity_def, X, Z, false, ENTITY_TYPE_FK);
   sql_drop_foreign_key(pParse);
 }
 
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
index 0392c0a87..c3ed2f72d 100644
--- a/src/box/sql/parse_def.h
+++ b/src/box/sql/parse_def.h
@@ -126,6 +126,7 @@ enum sql_index_type {
 
 enum entity_type {
        ENTITY_TYPE_TABLE = 0,
+       ENTITY_TYPE_VIEW,
        ENTITY_TYPE_INDEX,
        ENTITY_TYPE_TRIGGER,
        ENTITY_TYPE_CK,
@@ -176,6 +177,19 @@ struct create_table_def {
        bool has_autoinc;
 };
 
+struct create_view_def {
+       struct create_entity_def base;
+       /**
+        * Starting position of CREATE VIEW ... statement.
+        * It is used to fetch whole statement, which is
+        * saved as raw string to space options.
+        */
+       struct Token *create_start;
+       /** List of column aliases (SELECT x AS y ...). */
+       struct ExprList *aliases;
+       struct Select *select;
+};
+
 struct drop_entity_def {
        struct alter_entity_def base;
        /** Name of index/trigger/constraint to be dropped. */
@@ -239,8 +253,9 @@ alter_entity_def_init(struct alter_entity_def *alter_def,
 
 static inline void
 rename_entity_def_init(struct rename_entity_def *rename_def,
-                      struct Token new_name)
+                      struct SrcList *table_name, struct Token new_name)
 {
+       alter_entity_def_init(&rename_def->base, table_name);
        rename_def->new_name = new_name;
        struct alter_entity_def *alter_def =
                (struct alter_entity_def *) rename_def;
@@ -258,15 +273,20 @@ create_entity_def_init(struct create_entity_def *create_def, struct Token name,
 
 static inline void
 create_constraint_def_init(struct create_constraint_def *constr_def,
-                          bool is_deferred)
+                          struct SrcList *parent_name, struct Token name,
+                          bool if_not_exists, bool is_deferred)
 {
+       alter_entity_def_init(&constr_def->base.base, parent_name);
+       create_entity_def_init(&constr_def->base, name, if_not_exists);
        constr_def->is_deferred = is_deferred;
 }
 
 static inline void
-drop_entity_def_init(struct drop_entity_def *drop_def, struct Token name,
+drop_entity_def_init(struct drop_entity_def *drop_def,
+                    struct SrcList *parent_name, struct Token name,
                     bool if_exist, enum entity_type entity_type)
 {
+       alter_entity_def_init(&drop_def->base, parent_name);
        drop_def->name = name;
        drop_def->if_exist = if_exist;
        drop_def->base.entity_type = entity_type;
@@ -274,8 +294,13 @@ drop_entity_def_init(struct drop_entity_def *drop_def, struct Token name,
 }
 
 static inline void
-create_trigger_def_init(struct create_trigger_def *trigger_def, int tr_tm,
-                       int op, struct IdList *cols, struct Expr *when) {
+create_trigger_def_init(struct create_trigger_def *trigger_def,
+                       struct SrcList *table_name, struct Token name,
+                       int tr_tm, int op, struct IdList *cols,
+                       struct Expr *when, bool if_not_exists)
+{
+       alter_entity_def_init(&trigger_def->base.base, table_name);
+       create_entity_def_init(&trigger_def->base, name, if_not_exists);
        trigger_def->tr_tm = tr_tm;
        trigger_def->op = op;
        trigger_def->cols = cols;
@@ -326,8 +351,10 @@ create_fk_def_init(struct create_fk_def *fk_def, struct ExprList *child_cols,
 }
 
 static inline void
-create_table_def_init(struct create_table_def *table_def)
+create_table_def_init(struct create_table_def *table_def, struct Token name,
+                     bool if_not_exists)
 {
+       create_entity_def_init(&table_def->base, name, if_not_exists);
        rlist_create(&table_def->new_fkey);
        struct alter_entity_def *alter_def =
                (struct alter_entity_def *) table_def;
@@ -335,6 +362,21 @@ create_table_def_init(struct create_table_def *table_def)
        alter_def->alter_action = ALTER_ACTION_CREATE;
 }
 
+static inline void
+create_view_def_init(struct create_view_def *view_def, struct Token name,
+                    struct Token *create, struct ExprList *aliases,
+                    struct Select *select, bool if_not_exists)
+{
+       create_entity_def_init(&view_def->base, name, if_not_exists);
+       view_def->create_start = create;
+       view_def->select = select;
+       view_def->aliases = aliases;
+       struct alter_entity_def *alter_def =
+               (struct alter_entity_def *) view_def;
+       alter_def->entity_type = ENTITY_TYPE_VIEW;
+       alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
 static inline void
 create_table_def_destroy(struct create_table_def *table_def)
 {
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 4e6806be8..a6a4183db 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2735,6 +2735,7 @@ struct Parse {
                struct create_fk_def create_fk_def;
                struct create_index_def create_index_def;
                struct create_trigger_def create_trigger_def;
+               struct create_view_def create_view_def;
                struct rename_entity_def rename_entity_def;
                struct drop_entity_def drop_entity_def;
        };
@@ -3305,7 +3306,9 @@ Table *sqlite3ResultSetOfSelect(Parse *, Select *);
 struct index *
 sql_table_primary_key(const struct Table *tab);
 
-void sqlite3StartTable(Parse *);
+struct Table *
+sqlite3StartTable(struct Parse *parse, struct Token name, bool);
+
 void sqlite3AddColumn(Parse *, Token *, struct type_def *);
 
 /**
@@ -3379,13 +3382,9 @@ int sqlite3FaultSim(int);
  * The parser calls this routine in order to create a new VIEW.
  *
  * @param parse_context Current parsing context.
- * @param begin The CREATE token that begins the statement.
- * @param aliases Optional list of view column names.
- * @param select A SELECT statement that will become the new view.
  */
 void
-sql_create_view(struct Parse *parse_context, struct Token *begin,
-               struct ExprList *aliases, struct Select *select);
+sql_create_view(struct Parse *parse_context);
 
 /**
  * Helper to convert SQLite affinity to corresponding


Whole patch:

commit 9eb8234b22a55a2524b560b38f7a49959371ccaf
Author: Nikita Pettik <korablev at tarantool.org>
Date:   Wed Jan 9 12:28:09 2019 +0200

    sql: introduce structs assembling DDL arguments during parsing
    
    Parser's rules implementing DDL have a lot in common. For instance,
    to drop any entity it is enough to know its name and name of table it is
    related to.
    Thus, it was suggested to arrange arguments of DDL rules into
    hierarchical structure. The root of chain always includes name of table
    to be altered: all existing entities are related to some table.  Then
    comes one of drop/create/rename rules. Indeed, each DDL operation can be
    classified in these terms, at least until we introduce ALTER TABLE ALTER
    CONSTRAINT statement. Drop is represented by single structure (as it was
    mentioned); rename can be applied only to table; create can be applied
    to CONSTRAINT (indexes are considered as constraints) and TRIGGER, which
    in turn are different in arguments required to create those objects. And
    so forth.
    
    What is more, we are going to introduce ALTER TABLE ADD CONSTRAINT
    UNIQUE With new hierarchy we can extend ALTER TABLE statement with ease:
    basic structures (alter -> create entity -> create constraint) are the
    same for .. FOREIGN KEY/UNIQUE, but the last one will be different.
    
    Patch itself is made up of refactoring; no functional changes are
    provided.
    
    Needed for #3097

diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index d0ce9d893..6eb14fdb5 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -38,12 +38,15 @@
 #include "box/schema.h"
 
 void
-sql_alter_table_rename(struct Parse *parse, struct SrcList *src_tab,
-		       struct Token *new_name_tk)
+sql_alter_table_rename(struct Parse *parse)
 {
+	struct rename_entity_def *rename_def = &parse->rename_entity_def;
+	struct SrcList *src_tab = rename_def->base.entity_name;
+	assert(rename_def->base.entity_type == ENTITY_TYPE_TABLE);
+	assert(rename_def->base.alter_action == ALTER_ACTION_RENAME);
 	assert(src_tab->nSrc == 1);
 	struct sqlite3 *db = parse->db;
-	char *new_name = sqlite3NameFromToken(db, new_name_tk);
+	char *new_name = sqlite3NameFromToken(db, &rename_def->new_name);
 	if (new_name == NULL)
 		goto exit_rename_table;
 	/* Check that new name isn't occupied by another table. */
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 68e83ec8b..0bc967bf7 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -383,7 +383,7 @@ sql_table_new(Parse *parser, char *name)
  * when the "TEMP" or "TEMPORARY" keyword occurs in between
  * CREATE and TABLE.
  *
- * The new table record is initialized and put in pParse->pNewTable.
+ * The new table record is initialized and put in pParse->create_table_def.
  * As more of the CREATE TABLE statement is parsed, additional action
  * routines will be called to add more information to this record.
  * At the end of the CREATE TABLE statement, the sqlite3EndTable() routine
@@ -393,21 +393,20 @@ sql_table_new(Parse *parser, char *name)
  * @param pName1 First part of the name of the table or view.
  * @param noErr Do nothing if table already exists.
  */
-void
+struct Table *
 sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 {
-	Table *pTable;
+	Table *pTable = NULL;
 	char *zName = 0;	/* The name of the new table */
 	sqlite3 *db = pParse->db;
 	struct Vdbe *v = sqlite3GetVdbe(pParse);
 	if (v == NULL)
 		goto cleanup;
 	sqlite3VdbeCountChanges(v);
-
 	zName = sqlite3NameFromToken(db, pName);
 
 	if (zName == 0)
-		return;
+		return NULL;
 	if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK)
 		goto cleanup;
 
@@ -425,16 +424,12 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 	pTable = sql_table_new(pParse, zName);
 	if (pTable == NULL)
 		goto cleanup;
-
-	assert(pParse->pNewTable == 0);
-	pParse->pNewTable = pTable;
-
 	if (!db->init.busy && (v = sqlite3GetVdbe(pParse)) != 0)
 		sql_set_multi_write(pParse, true);
 
  cleanup:
 	sqlite3DbFree(db, zName);
-	return;
+	return pTable;
 }
 
 /**
@@ -516,7 +511,7 @@ sqlite3AddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	int i;
 	char *z;
 	sqlite3 *db = pParse->db;
-	if ((p = pParse->pNewTable) == 0)
+	if ((p = pParse->create_table_def.new_table) == NULL)
 		return;
 #if SQLITE_MAX_COLUMN
 	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
@@ -565,14 +560,13 @@ sqlite3AddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	column_def->affinity = type_def->type;
 	column_def->type = sql_affinity_to_field_type(column_def->affinity);
 	p->def->field_count++;
-	pParse->constraintName.n = 0;
 }
 
 void
 sql_column_add_nullable_action(struct Parse *parser,
 			       enum on_conflict_action nullable_action)
 {
-	struct Table *p = parser->pNewTable;
+	struct Table *p = parser->create_table_def.new_table;
 	if (p == NULL || NEVER(p->def->field_count < 1))
 		return;
 	struct field_def *field = &p->def->fields[p->def->field_count - 1];
@@ -609,7 +603,7 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 {
 	Table *p;
 	sqlite3 *db = pParse->db;
-	p = pParse->pNewTable;
+	p = pParse->create_table_def.new_table;
 	assert(p->def->opts.is_temporary);
 	if (p != 0) {
 		if (!sqlite3ExprIsConstantOrFunction
@@ -671,15 +665,12 @@ field_def_create_for_pk(struct Parse *parser, struct field_def *field,
  * index for the key.  No index is created for INTEGER PRIMARY KEYs.
  */
 void
-sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
-		     ExprList * pList,	/* List of field names to be indexed */
-		     int autoInc,	/* True if the AUTOINCREMENT keyword is present */
-		     enum sort_order sortOrder
-    )
+sqlite3AddPrimaryKey(struct Parse *pParse)
 {
-	Table *pTab = pParse->pNewTable;
+	Table *pTab = pParse->create_table_def.new_table;
 	int iCol = -1, i;
 	int nTerm;
+	struct ExprList *pList = pParse->create_index_def.cols;
 	if (pTab == 0)
 		goto primary_key_exit;
 	if (sql_table_primary_key(pTab) != NULL) {
@@ -713,10 +704,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 		}
 	}
 	if (nTerm == 1 && iCol != -1 &&
-	    pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER &&
-	    sortOrder != SORT_ORDER_DESC) {
-		assert(autoInc == 0 || autoInc == 1);
-		pParse->is_new_table_autoinc = autoInc;
+	    pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER) {
 		struct sqlite3 *db = pParse->db;
 		struct ExprList *list;
 		struct Token token;
@@ -726,18 +714,17 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 							     &token, 0));
 		if (list == NULL)
 			goto primary_key_exit;
-		sql_create_index(pParse, 0, 0, list, 0, SORT_ORDER_ASC,
-				 false, SQL_INDEX_TYPE_CONSTRAINT_PK);
+		pParse->create_index_def.cols = list;
+		sql_create_index(pParse);
 		if (db->mallocFailed)
 			goto primary_key_exit;
-	} else if (autoInc) {
+	} else if (pParse->create_table_def.has_autoinc) {
 		sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
 				"INTEGER PRIMARY KEY or INT PRIMARY KEY");
 		goto primary_key_exit;
 	} else {
-		sql_create_index(pParse, 0, 0, pList, 0, sortOrder, false,
-				 SQL_INDEX_TYPE_CONSTRAINT_PK);
-		pList = 0;
+		sql_create_index(pParse);
+		pList = NULL;
 		if (pParse->nErr > 0)
 			goto primary_key_exit;
 	}
@@ -756,14 +743,21 @@ primary_key_exit:
 }
 
 void
-sql_add_check_constraint(struct Parse *parser, struct ExprSpan *span)
-{
-	struct Expr *expr = span->pExpr;
-	struct Table *table = parser->pNewTable;
+sql_add_check_constraint(struct Parse *parser)
+{
+	struct create_ck_def *ck_def = &parser->create_ck_def;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) &parser->create_ck_def;
+	assert(alter_def->entity_type == ENTITY_TYPE_CK);
+	(void) alter_def;
+	struct Expr *expr = ck_def->expr->pExpr;
+	struct Table *table = parser->create_table_def.new_table;
 	if (table != NULL) {
 		expr->u.zToken =
-			sqlite3DbStrNDup(parser->db, (char *)span->zStart,
-					 (int)(span->zEnd - span->zStart));
+			sqlite3DbStrNDup(parser->db,
+					 (char *) ck_def->expr->zStart,
+					 (int) (ck_def->expr->zEnd -
+					        ck_def->expr->zStart));
 		if (expr->u.zToken == NULL)
 			goto release_expr;
 		table->def->opts.checks =
@@ -773,9 +767,10 @@ sql_add_check_constraint(struct Parse *parser, struct ExprSpan *span)
 			sqlite3DbFree(parser->db, expr->u.zToken);
 			goto release_expr;
 		}
-		if (parser->constraintName.n) {
+		struct create_entity_def *entity_def = &ck_def->base.base;
+		if (entity_def->name.n > 0) {
 			sqlite3ExprListSetName(parser, table->def->opts.checks,
-					       &parser->constraintName, 1);
+					       &entity_def->name, 1);
 		}
 	} else {
 release_expr:
@@ -790,7 +785,7 @@ release_expr:
 void
 sqlite3AddCollateType(Parse * pParse, Token * pToken)
 {
-	Table *p = pParse->pNewTable;
+	Table *p = pParse->create_table_def.new_table;
 	if (p == NULL)
 		return;
 	uint32_t i = p->def->field_count - 1;
@@ -923,7 +918,7 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	memcpy(raw, index_parts, index_parts_sz);
 	index_parts = raw;
 
-	if (parse->pNewTable != NULL) {
+	if (parse->create_table_def.new_table != NULL) {
 		sqlite3VdbeAddOp2(v, OP_SCopy, space_id_reg, entry_reg);
 		sqlite3VdbeAddOp2(v, OP_Integer, idx_def->iid, entry_reg + 1);
 	} else {
@@ -1116,14 +1111,15 @@ vdbe_emit_fkey_create(struct Parse *parse_context, const struct fkey_def *fk)
 	 * of <CREATE TABLE ...> statement, we don't have child
 	 * id, but we know register where it will be stored.
 	 */
-	if (parse_context->pNewTable != NULL) {
+	if (parse_context->create_table_def.new_table != NULL) {
 		sqlite3VdbeAddOp2(vdbe, OP_SCopy, fk->child_id,
 				  constr_tuple_reg + 1);
 	} else {
 		sqlite3VdbeAddOp2(vdbe, OP_Integer, fk->child_id,
 				  constr_tuple_reg + 1);
 	}
-	if (parse_context->pNewTable != NULL && fkey_is_self_referenced(fk)) {
+	if (parse_context->create_table_def.new_table != NULL &&
+	    fkey_is_self_referenced(fk)) {
 		sqlite3VdbeAddOp2(vdbe, OP_SCopy, fk->parent_id,
 				  constr_tuple_reg + 2);
 	} else {
@@ -1186,7 +1182,7 @@ vdbe_emit_fkey_create(struct Parse *parse_context, const struct fkey_def *fk)
 			  constr_tuple_reg + 9);
 	sqlite3VdbeAddOp3(vdbe, OP_SInsert, BOX_FK_CONSTRAINT_ID, 0,
 			  constr_tuple_reg + 9);
-	if (parse_context->pNewTable == NULL)
+	if (parse_context->create_table_def.new_table == NULL)
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_NCHANGE);
 	save_record(parse_context, BOX_FK_CONSTRAINT_ID, constr_tuple_reg, 2,
 		    vdbe->nOp - 1);
@@ -1260,7 +1256,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		return;
 	}
 	assert(!db->mallocFailed);
-	p = pParse->pNewTable;
+	p = pParse->create_table_def.new_table;
 	if (p == 0)
 		return;
 
@@ -1304,7 +1300,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	 * Check to see if we need to create an _sequence table
 	 * for keeping track of autoincrement keys.
 	 */
-	if (pParse->is_new_table_autoinc) {
+	if (pParse->create_table_def.has_autoinc) {
 		assert(reg_space_id != 0);
 		/* Do an insertion into _sequence. */
 		int reg_seq_id = ++pParse->nMem;
@@ -1327,7 +1323,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	}
 	/* Code creation of FK constraints, if any. */
 	struct fkey_parse *fk_parse;
-	rlist_foreach_entry(fk_parse, &pParse->new_fkey, link) {
+	rlist_foreach_entry(fk_parse, &pParse->create_table_def.new_fkey,
+			    link) {
 		struct fkey_def *fk = fk_parse->fkey;
 		if (fk_parse->selfref_cols != NULL) {
 			struct ExprList *cols = fk_parse->selfref_cols;
@@ -1366,10 +1363,14 @@ cleanup:
 }
 
 void
-sql_create_view(struct Parse *parse_context, struct Token *begin,
-		struct Token *name, struct ExprList *aliases,
-		struct Select *select, bool if_exists)
-{
+sql_create_view(struct Parse *parse_context)
+{
+	struct create_view_def *view_def = &parse_context->create_view_def;
+	struct create_entity_def *create_entity_def = &view_def->base;
+	struct alter_entity_def *alter_entity_def = &create_entity_def->base;
+	assert(alter_entity_def->entity_type == ENTITY_TYPE_VIEW);
+	assert(alter_entity_def->alter_action == ALTER_ACTION_CREATE);
+	(void) alter_entity_def;
 	struct sqlite3 *db = parse_context->db;
 	struct Table *sel_tab = NULL;
 	if (parse_context->nVar > 0) {
@@ -1377,13 +1378,15 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 				"parameters are not allowed in views");
 		goto create_view_fail;
 	}
-	sqlite3StartTable(parse_context, name, if_exists);
-	struct Table *p = parse_context->pNewTable;
+	struct Table *p = sqlite3StartTable(parse_context,
+					    &create_entity_def->name,
+					    create_entity_def->if_not_exist);
 	if (p == NULL || parse_context->nErr != 0)
 		goto create_view_fail;
-	sel_tab = sqlite3ResultSetOfSelect(parse_context, select);
+	sel_tab = sqlite3ResultSetOfSelect(parse_context, view_def->select);
 	if (sel_tab == NULL)
 		goto create_view_fail;
+	struct ExprList *aliases = view_def->aliases;
 	if (aliases != NULL) {
 		if ((int)sel_tab->def->field_count != aliases->nExpr) {
 			sqlite3ErrorMsg(parse_context, "expected %d columns "\
@@ -1394,7 +1397,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		}
 		sqlite3ColumnsFromExprList(parse_context, aliases, p);
 		sqlite3SelectAddColumnTypeAndCollation(parse_context, p,
-						       select);
+						       view_def->select);
 	} else {
 		assert(sel_tab->def->opts.is_temporary);
 		p->def->fields = sel_tab->def->fields;
@@ -1412,6 +1415,7 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	if (end.z[0] != ';')
 		end.z += end.n;
 	end.n = 0;
+	struct Token *begin = view_def->create_start;
 	int n = end.z - begin->z;
 	assert(n > 0);
 	const char *z = begin->z;
@@ -1430,8 +1434,8 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 
  create_view_fail:
 	sqlite3DbFree(db, sel_tab);
-	sql_expr_list_delete(db, aliases);
-	sql_select_delete(db, select);
+	sql_expr_list_delete(db, view_def->aliases);
+	sql_select_delete(db, view_def->select);
 	return;
 }
 
@@ -1679,14 +1683,15 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
  * This routine is called to do the work of a DROP TABLE statement.
  *
  * @param parse_context Current parsing context.
- * @param table_name_list List containing table name.
  * @param is_view True, if statement is really 'DROP VIEW'.
- * @param if_exists True, if statement contains 'IF EXISTS' clause.
  */
 void
-sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
-	       bool is_view, bool if_exists)
+sql_drop_table(struct Parse *parse_context, bool is_view)
 {
+	struct drop_entity_def drop_def = parse_context->drop_entity_def;
+	assert(drop_def.base.entity_type == ENTITY_TYPE_TABLE);
+	assert(drop_def.base.alter_action == ALTER_ACTION_DROP);
+	struct SrcList *table_name_list = drop_def.base.entity_name;
 	struct Vdbe *v = sqlite3GetVdbe(parse_context);
 	struct sqlite3 *db = parse_context->db;
 	if (v == NULL || db->mallocFailed) {
@@ -1698,10 +1703,10 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 	const char *space_name = table_name_list->a[0].zName;
 	struct space *space = space_by_name(space_name);
 	if (space == NULL) {
-		if (!is_view && !if_exists)
+		if (!is_view && !drop_def.if_exist)
 			sqlite3ErrorMsg(parse_context, "no such table: %s",
 					space_name);
-		if (is_view && !if_exists)
+		if (is_view && !drop_def.if_exist)
 			sqlite3ErrorMsg(parse_context, "no such view: %s",
 					space_name);
 		goto exit_drop_table;
@@ -1784,12 +1789,15 @@ columnno_by_name(struct Parse *parse_context, const struct space *space,
 }
 
 void
-sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
-		       struct Token *constraint, struct ExprList *child_cols,
-		       struct Token *parent, struct ExprList *parent_cols,
-		       bool is_deferred, int actions)
+sql_create_foreign_key(struct Parse *parse_context)
 {
 	struct sqlite3 *db = parse_context->db;
+	struct create_fk_def *create_fk_def = &parse_context->create_fk_def;
+	struct create_constraint_def *create_constr_def = &create_fk_def->base;
+	struct create_entity_def *create_def = &create_constr_def->base;
+	struct alter_entity_def *alter_def = &create_def->base;
+	assert(alter_def->entity_type == ENTITY_TYPE_FK);
+	assert(alter_def->alter_action == ALTER_ACTION_CREATE);
 	/*
 	 * When this function is called second time during
 	 * <CREATE TABLE ...> statement (i.e. at VDBE runtime),
@@ -1808,20 +1816,21 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	 * Table under construction during CREATE TABLE
 	 * processing. NULL for ALTER TABLE statement handling.
 	 */
-	struct Table *new_tab = parse_context->pNewTable;
+	struct create_table_def *table_def = &parse_context->create_table_def;
+	struct Table *new_tab = table_def->new_table;
 	/* Whether we are processing ALTER TABLE or CREATE TABLE. */
 	bool is_alter = new_tab == NULL;
 	uint32_t child_cols_count;
+	struct ExprList *child_cols = create_fk_def->child_cols;
 	if (child_cols == NULL) {
 		assert(!is_alter);
 		child_cols_count = 1;
 	} else {
 		child_cols_count = child_cols->nExpr;
 	}
-	assert(!is_alter || (child != NULL && child->nSrc == 1));
 	struct space *child_space = NULL;
 	if (is_alter) {
-		const char *child_name = child->a[0].zName;
+		const char *child_name = alter_def->entity_name->a[0].zName;
 		child_space = space_by_name(child_name);
 		if (child_space == NULL) {
 			diag_set(ClientError, ER_NO_SUCH_SPACE, child_name);
@@ -1836,8 +1845,9 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			goto tnt_error;
 		}
 		memset(fk, 0, sizeof(*fk));
-		rlist_add_entry(&parse_context->new_fkey, fk, link);
+		rlist_add_entry(&table_def->new_fkey, fk, link);
 	}
+	struct Token *parent = create_fk_def->parent_name;
 	assert(parent != NULL);
 	parent_name = sqlite3NameFromToken(db, parent);
 	if (parent_name == NULL)
@@ -1849,11 +1859,12 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	 */
 	is_self_referenced = !is_alter &&
 			     strcmp(parent_name, new_tab->def->name) == 0;
+	struct ExprList *parent_cols = create_fk_def->parent_cols;
 	struct space *parent_space = space_by_name(parent_name);
 	if (parent_space == NULL) {
 		if (is_self_referenced) {
 			struct fkey_parse *fk =
-				rlist_first_entry(&parse_context->new_fkey,
+				rlist_first_entry(&table_def->new_fkey,
 						  struct fkey_parse, link);
 			fk->selfref_cols = parent_cols;
 			fk->is_self_referenced = true;
@@ -1868,18 +1879,19 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			goto exit_create_fk;
 		}
 	}
-	if (constraint == NULL && !is_alter) {
-		if (parse_context->constraintName.n == 0) {
+	if (!is_alter) {
+		if (create_def->name.n == 0) {
 			constraint_name =
 				sqlite3MPrintf(db, "FK_CONSTRAINT_%d_%s",
-					       ++parse_context->fkey_count,
+					       ++table_def->fkey_count,
 					       new_tab->def->name);
 		} else {
-			struct Token *cnstr_nm = &parse_context->constraintName;
-			constraint_name = sqlite3NameFromToken(db, cnstr_nm);
+			constraint_name =
+				sqlite3NameFromToken(db, &create_def->name);
 		}
 	} else {
-		constraint_name = sqlite3NameFromToken(db, constraint);
+		constraint_name =
+			sqlite3NameFromToken(db, &create_def->name);
 	}
 	if (constraint_name == NULL)
 		goto exit_create_fk;
@@ -1912,10 +1924,11 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 		diag_set(OutOfMemory, fk_size, "region", "struct fkey");
 		goto tnt_error;
 	}
+	int actions = create_fk_def->actions;
 	fk->field_count = child_cols_count;
 	fk->child_id = child_space != NULL ? child_space->def->id : 0;
 	fk->parent_id = parent_space != NULL ? parent_space->def->id : 0;
-	fk->is_deferred = is_deferred;
+	fk->is_deferred = create_constr_def->is_deferred;
 	fk->match = (enum fkey_match) ((actions >> 16) & 0xff);
 	fk->on_update = (enum fkey_action) ((actions >> 8) & 0xff);
 	fk->on_delete = (enum fkey_action) (actions & 0xff);
@@ -1969,7 +1982,7 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	 */
 	if (!is_alter) {
 		struct fkey_parse *parse_fk =
-			rlist_first_entry(&parse_context->new_fkey,
+			rlist_first_entry(&table_def->new_fkey,
 					  struct fkey_parse, link);
 		parse_fk->fkey = fk;
 	} else {
@@ -1993,18 +2006,21 @@ void
 fkey_change_defer_mode(struct Parse *parse_context, bool is_deferred)
 {
 	if (parse_context->db->init.busy ||
-	    rlist_empty(&parse_context->new_fkey))
+	    rlist_empty(&parse_context->create_table_def.new_fkey))
 		return;
-	rlist_first_entry(&parse_context->new_fkey, struct fkey_parse,
-			  link)->fkey->is_deferred = is_deferred;
+	rlist_first_entry(&parse_context->create_table_def.new_fkey,
+			  struct fkey_parse, link)->fkey->is_deferred =
+		is_deferred;
 }
 
 void
-sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
-		     struct Token *constraint)
+sql_drop_foreign_key(struct Parse *parse_context)
 {
-	assert(table != NULL && table->nSrc == 1);
-	const char *table_name = table->a[0].zName;
+	struct drop_entity_def *drop_def = &parse_context->drop_entity_def;
+	assert(drop_def->base.entity_type == ENTITY_TYPE_FK);
+	assert(drop_def->base.alter_action == ALTER_ACTION_DROP);
+	const char *table_name = drop_def->base.entity_name->a[0].zName;
+	assert(table_name != NULL);
 	struct space *child = space_by_name(table_name);
 	if (child == NULL) {
 		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
@@ -2013,7 +2029,7 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
 		return;
 	}
 	char *constraint_name = sqlite3NameFromToken(parse_context->db,
-						     constraint);
+						     &drop_def->name);
 	if (constraint_name != NULL)
 		vdbe_emit_fkey_drop(parse_context, constraint_name,
 				    child->def->id);
@@ -2207,19 +2223,29 @@ constraint_is_named(const char *name)
 }
 
 void
-sql_create_index(struct Parse *parse, struct Token *token,
-		 struct SrcList *tbl_name, struct ExprList *col_list,
-		 MAYBE_UNUSED struct Token *start, enum sort_order sort_order,
-		 bool if_not_exist, enum sql_index_type idx_type) {
+sql_create_index(struct Parse *parse) {
 	/* The index to be created. */
 	struct index *index = NULL;
 	/* Name of the index. */
 	char *name = NULL;
 	struct sqlite3 *db = parse->db;
 	assert(!db->init.busy);
+	struct create_index_def *create_idx_def = &parse->create_index_def;
+	struct create_entity_def *create_entity_def = &create_idx_def->base.base;
+	struct alter_entity_def *alter_entity_def = &create_entity_def->base;
+	assert(alter_entity_def->entity_type == ENTITY_TYPE_INDEX);
+	assert(alter_entity_def->alter_action == ALTER_ACTION_CREATE);
+	/*
+	 * Get list of columns to be indexed. It will be NULL if
+	 * this is a primary key or unique-constraint on the most
+	 * recent column added to the table under construction.
+	 */
+	struct ExprList *col_list = create_idx_def->cols;
+	struct SrcList *tbl_name = alter_entity_def->entity_name;
 
 	if (db->mallocFailed || parse->nErr > 0)
 		goto exit_create_index;
+	enum sql_index_type idx_type = create_idx_def->idx_type;
 	if (idx_type == SQL_INDEX_TYPE_UNIQUE ||
 	    idx_type == SQL_INDEX_TYPE_NON_UNIQUE) {
 		Vdbe *v = sqlite3GetVdbe(parse);
@@ -2234,12 +2260,13 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 */
 	struct space *space = NULL;
 	struct space_def *def = NULL;
+	struct Token token = create_entity_def->name;
 	if (tbl_name != NULL) {
-		assert(token != NULL && token->z != NULL);
+		assert(token.n > 0 && token.z != NULL);
 		const char *name = tbl_name->a[0].zName;
 		space = space_by_name(name);
 		if (space == NULL) {
-			if (! if_not_exist) {
+			if (! create_entity_def->if_not_exist) {
 				diag_set(ClientError, ER_NO_SUCH_SPACE, name);
 				parse->rc = SQL_TARANTOOL_ERROR;
 				parse->nErr++;
@@ -2248,12 +2275,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		}
 		def = space->def;
 	} else {
-		if (parse->pNewTable == NULL)
+		if (parse->create_table_def.new_table == NULL)
 			goto exit_create_index;
-		assert(token == NULL);
-		assert(start == NULL);
-		space = parse->pNewTable->space;
-		def = parse->pNewTable->def;
+		space = parse->create_table_def.new_table->space;
+		def = parse->create_table_def.new_table->def;
 	}
 
 	if (def->opts.is_view) {
@@ -2280,13 +2305,13 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 * 2) UNIQUE constraint is non-named and standard
 	 *    auto-index name will be generated.
 	 */
-	if (token != NULL) {
-		assert(token->z != NULL);
-		name = sqlite3NameFromToken(db, token);
+	if (parse->create_table_def.new_table == NULL) {
+		assert(token.z != NULL);
+		name = sqlite3NameFromToken(db, &token);
 		if (name == NULL)
 			goto exit_create_index;
 		if (sql_space_index_by_name(space, name) != NULL) {
-			if (!if_not_exist) {
+			if (! create_entity_def->if_not_exist) {
 				sqlite3ErrorMsg(parse,
 						"index %s.%s already exists",
 						def->name, name);
@@ -2295,11 +2320,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		}
 	} else {
 		char *constraint_name = NULL;
-		if (parse->constraintName.z != NULL)
+		if (create_entity_def->name.n > 0)
 			constraint_name =
 				sqlite3NameFromToken(db,
-						     &parse->constraintName);
-
+						     &create_entity_def->name);
 	       /*
 		* This naming is temporary. Now it's not
 		* possible (since we implement UNIQUE
@@ -2357,7 +2381,8 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		if (col_list == NULL)
 			goto exit_create_index;
 		assert(col_list->nExpr == 1);
-		sqlite3ExprListSetSortOrder(col_list, sort_order);
+		sqlite3ExprListSetSortOrder(col_list,
+					    create_idx_def->sort_order);
 	} else {
 		sqlite3ExprListCheckLength(parse, col_list, "index");
 	}
@@ -2438,7 +2463,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 * constraint, but has different onError (behavior on
 	 * constraint violation), then an error is raised.
 	 */
-	if (parse->pNewTable != NULL) {
+	if (parse->create_table_def.new_table != NULL) {
 		for (uint32_t i = 0; i < space->index_count; ++i) {
 			struct index *existing_idx = space->index[i];
 			uint32_t iid = existing_idx->def->iid;
@@ -2505,8 +2530,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 				  (void *)space_by_id(BOX_INDEX_ID),
 				  P4_SPACEPTR);
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
-
-		assert(start != NULL);
 		int index_id = getNewIid(parse, def->id, cursor);
 		sqlite3VdbeAddOp1(vdbe, OP_Close, cursor);
 		vdbe_emit_create_index(parse, def, index->def,
@@ -2530,30 +2553,33 @@ sql_create_index(struct Parse *parse, struct Token *token,
 }
 
 void
-sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
-	       struct Token *table_token, bool if_exists)
+sql_drop_index(struct Parse *parse_context)
 {
+	struct drop_entity_def *drop_def = &parse_context->drop_entity_def;
+	assert(drop_def->base.entity_type == ENTITY_TYPE_INDEX);
+	assert(drop_def->base.alter_action == ALTER_ACTION_DROP);
 	struct Vdbe *v = sqlite3GetVdbe(parse_context);
 	assert(v != NULL);
 	struct sqlite3 *db = parse_context->db;
 	/* Never called with prior errors. */
 	assert(parse_context->nErr == 0);
-	assert(table_token != NULL);
-	const char *table_name = sqlite3NameFromToken(db, table_token);
+	struct SrcList *table_list = drop_def->base.entity_name;
+	assert(table_list->nSrc == 1);
+	char *table_name = table_list->a[0].zName;
+	const char *index_name = NULL;
 	if (db->mallocFailed) {
 		goto exit_drop_index;
 	}
 	sqlite3VdbeCountChanges(v);
-	assert(index_name_list->nSrc == 1);
-	assert(table_token->n > 0);
 	struct space *space = space_by_name(table_name);
+	bool if_exists = drop_def->if_exist;
 	if (space == NULL) {
 		if (!if_exists)
 			sqlite3ErrorMsg(parse_context, "no such space: %s",
 					table_name);
 		goto exit_drop_index;
 	}
-	const char *index_name = index_name_list->a[0].zName;
+	index_name = sqlite3NameFromToken(db, &drop_def->name);
 	uint32_t index_id = box_index_id_by_name(space->def->id, index_name,
 						 strlen(index_name));
 	if (index_id == BOX_ID_NIL) {
@@ -2579,8 +2605,8 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
 	sqlite3VdbeAddOp2(v, OP_SDelete, BOX_INDEX_ID, record_reg);
 	sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
  exit_drop_index:
-	sqlite3SrcListDelete(db, index_name_list);
-	sqlite3DbFree(db, (void *) table_name);
+	sqlite3SrcListDelete(db, table_list);
+	sqlite3DbFree(db, (void *) index_name);
 }
 
 /*
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 0bcf41594..1f7678a4b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -168,7 +168,8 @@ cmd ::= ROLLBACK TO savepoint_opt nm(X). {
 //
 cmd ::= create_table create_table_args.
 create_table ::= createkw TABLE ifnotexists(E) nm(Y). {
-   sqlite3StartTable(pParse,&Y,E);
+  create_table_def_init(&pParse->create_table_def, Y, E);
+  pParse->create_table_def.new_table = sqlite3StartTable(pParse, &Y, E);
 }
 createkw(A) ::= CREATE(A).  {disableLookaside(pParse);}
 
@@ -178,6 +179,7 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
 
 create_table_args ::= LP columnlist RP(E). {
   sqlite3EndTable(pParse,&E,0);
+  create_table_def_destroy(&pParse->create_table_def);
 }
 create_table_args ::= AS select(S). {
   sqlite3EndTable(pParse,0,S);
@@ -237,8 +239,15 @@ nm(A) ::= id(A). {
 carglist ::= carglist cconsdef.
 carglist ::= .
 cconsdef ::= cconsname ccons.
-cconsname ::= CONSTRAINT nm(X).           {pParse->constraintName = X;}
-cconsname ::= .                           {pParse->constraintName.n = 0;}
+cconsname ::= cconsname_start cconsname_parse .
+cconsname_start ::= . {
+  memset(&pParse->create_index_def.base, 0, sizeof(struct create_constraint_def));
+}
+cconsname_parse ::= CONSTRAINT nm(X). {
+  create_constraint_def_init(&pParse->create_index_def.base, NULL, X, false,
+                             false);
+}
+cconsname_parse ::= .
 ccons ::= DEFAULT term(X).            {sqlite3AddDefaultValue(pParse,&X);}
 ccons ::= DEFAULT LP expr(X) RP.      {sqlite3AddDefaultValue(pParse,&X);}
 ccons ::= DEFAULT PLUS term(X).       {sqlite3AddDefaultValue(pParse,&X);}
@@ -260,14 +269,29 @@ ccons ::= NULL onconf(R).        {
         sql_column_add_nullable_action(pParse, R);
 }
 ccons ::= NOT NULL onconf(R).    {sql_column_add_nullable_action(pParse, R);}
-ccons ::= PRIMARY KEY sortorder(Z) autoinc(I).
-                                 {sqlite3AddPrimaryKey(pParse,0,I,Z);}
-ccons ::= UNIQUE.                {sql_create_index(pParse,0,0,0,0,
-                                                   SORT_ORDER_ASC, false,
-                                                   SQL_INDEX_TYPE_CONSTRAINT_UNIQUE);}
-ccons ::= CHECK LP expr(X) RP.   {sql_add_check_constraint(pParse,&X);}
-ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R).
-                                 {sql_create_foreign_key(pParse, NULL, NULL, NULL, &T, TA, false, R);}
+ccons ::= PRIMARY KEY sortorder(Z) autoinc(I). {
+  pParse->create_table_def.has_autoinc = I;
+  create_index_def_init(&pParse->create_index_def, NULL,
+                        SQL_INDEX_TYPE_CONSTRAINT_PK, Z);
+  sqlite3AddPrimaryKey(pParse);
+}
+ccons ::= UNIQUE. {
+  create_index_def_init(&pParse->create_index_def, NULL,
+                        SQL_INDEX_TYPE_CONSTRAINT_UNIQUE, SORT_ORDER_ASC);
+  sql_create_index(pParse);
+}
+
+ccons ::= check_constraint_def .
+
+check_constraint_def ::= CHECK LP expr(X) RP. {
+  create_ck_def_init(&pParse->create_ck_def, &X);
+  sql_add_check_constraint(pParse);
+}
+
+ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R). {
+  create_fk_def_init(&pParse->create_fk_def, NULL, &T, TA, R);
+  sql_create_foreign_key(pParse);
+}
 ccons ::= defer_subclause(D).    {fkey_change_defer_mode(pParse, D);}
 ccons ::= COLLATE id(C).        {sqlite3AddCollateType(pParse, &C);}
 
@@ -307,20 +331,24 @@ init_deferred_pred_opt(A) ::= .                       {A = 0;}
 init_deferred_pred_opt(A) ::= INITIALLY DEFERRED.     {A = 1;}
 init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE.    {A = 0;}
 
-tconsdef ::= tconsname tcons.
-tconsname ::= CONSTRAINT nm(X).      {pParse->constraintName = X;}
-tconsname ::= .                      {pParse->constraintName.n = 0;}
-tcons ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP.
-                                 {sqlite3AddPrimaryKey(pParse,X,I,0);}
-tcons ::= UNIQUE LP sortlist(X) RP.
-                                 {sql_create_index(pParse,0,0,X,0,
-                                                   SORT_ORDER_ASC,false,
-                                                   SQL_INDEX_TYPE_CONSTRAINT_UNIQUE);}
-tcons ::= CHECK LP expr(E) RP onconf.
-                                 {sql_add_check_constraint(pParse,&E);}
+tconsdef ::= cconsname tcons.
+tcons ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP. {
+  pParse->create_table_def.has_autoinc = I;
+  create_index_def_init(&pParse->create_index_def, X,
+                        SQL_INDEX_TYPE_CONSTRAINT_PK, SORT_ORDER_ASC);
+  sqlite3AddPrimaryKey(pParse);
+}
+tcons ::= UNIQUE LP sortlist(X) RP. {
+  create_index_def_init(&pParse->create_index_def, X,
+                        SQL_INDEX_TYPE_CONSTRAINT_UNIQUE, SORT_ORDER_ASC);
+  sql_create_index(pParse);
+}
+tcons ::= check_constraint_def .
 tcons ::= FOREIGN KEY LP eidlist(FA) RP
           REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). {
-    sql_create_foreign_key(pParse, NULL, NULL, FA, &T, TA, D, R);
+  ((struct create_constraint_def *) &pParse->create_fk_def)->is_deferred = D;
+  create_fk_def_init(&pParse->create_fk_def, FA, &T, TA, R);
+  sql_create_foreign_key(pParse);
 }
 %type defer_subclause_opt {int}
 defer_subclause_opt(A) ::= .                    {A = 0;}
@@ -343,9 +371,20 @@ resolvetype(A) ::= REPLACE.                  {A = ON_CONFLICT_ACTION_REPLACE;}
 
 ////////////////////////// The DROP TABLE /////////////////////////////////////
 //
-cmd ::= DROP TABLE ifexists(E) fullname(X). {
-  sql_drop_table(pParse, X, 0, E);
+
+cmd ::= drop_start(X) drop_table . {
+  sql_drop_table(pParse, X);
 }
+
+drop_table ::= ifexists(E) fullname(X) . {
+  drop_entity_def_init(&pParse->drop_entity_def, X, (struct Token) {}, E,
+                       ENTITY_TYPE_TABLE);
+}
+
+%type drop_start {bool}
+drop_start(X) ::= DROP VIEW . { X = true; }
+drop_start(X) ::= DROP TABLE . { X = false; }
+
 %type ifexists {int}
 ifexists(A) ::= IF EXISTS.   {A = 1;}
 ifexists(A) ::= .            {A = 0;}
@@ -354,13 +393,12 @@ ifexists(A) ::= .            {A = 0;}
 //
 cmd ::= createkw(X) VIEW ifnotexists(E) nm(Y) eidlist_opt(C)
           AS select(S). {
-  if (!pParse->parse_only)
-    sql_create_view(pParse, &X, &Y, C, S, E);
-  else
+  if (!pParse->parse_only) {
+    create_view_def_init(&pParse->create_view_def, Y, &X, C, S, E);
+    sql_create_view(pParse);
+  } else {
     sql_store_select(pParse, S);
-}
-cmd ::= DROP VIEW ifexists(E) fullname(X). {
-  sql_drop_table(pParse, X, 1, E);
+  }
 }
 
 //////////////////////// The SELECT statement /////////////////////////////////
@@ -1198,10 +1236,13 @@ paren_exprlist(A) ::= LP exprlist(X) RP.  {A = X;}
 
 ///////////////////////////// The CREATE INDEX command ///////////////////////
 //
-cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X)
+cmd ::= createkw uniqueflag(U) INDEX ifnotexists(NE) nm(X)
         ON nm(Y) LP sortlist(Z) RP. {
-  sql_create_index(pParse, &X, sqlite3SrcListAppend(pParse->db,0,&Y), Z, &S,
-                   SORT_ORDER_ASC, NE, U);
+  create_constraint_def_init(&pParse->create_index_def.base,
+                             sqlite3SrcListAppend(pParse->db, 0, &Y), X, NE,
+                             false);
+  create_index_def_init(&pParse->create_index_def, Z, U, SORT_ORDER_ASC);
+  sql_create_index(pParse);
 }
 
 %type uniqueflag {int}
@@ -1263,8 +1304,9 @@ collate(C) ::= COLLATE id.   {C = 1;}
 
 ///////////////////////////// The DROP INDEX command /////////////////////////
 //
-cmd ::= DROP INDEX ifexists(E) fullname(X) ON nm(Y).   {
-    sql_drop_index(pParse, X, &Y, E);
+cmd ::= DROP INDEX ifexists(E) nm(X) ON fullname(Y).   {
+  drop_entity_def_init(&pParse->drop_entity_def, Y, X, E, ENTITY_TYPE_INDEX);
+  sql_drop_index(pParse);
 }
 
 ///////////////////////////// The PRAGMA command /////////////////////////////
@@ -1315,7 +1357,9 @@ cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
 trigger_decl(A) ::= TRIGGER ifnotexists(NOERR) nm(B)
                     trigger_time(C) trigger_event(D)
                     ON fullname(E) foreach_clause when_clause(G). {
-  sql_trigger_begin(pParse, &B, C, D.a, D.b, E, G, NOERR);
+  create_trigger_def_init(&pParse->create_trigger_def, E, B, C, D.a, D.b,
+                          G, NOERR);
+  sql_trigger_begin(pParse);
   A = B; /*A-overwrites-T*/
 }
 
@@ -1425,7 +1469,9 @@ raisetype(A) ::= FAIL.      {A = ON_CONFLICT_ACTION_FAIL;}
 
 ////////////////////////  DROP TRIGGER statement //////////////////////////////
 cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
-  sql_drop_trigger(pParse,X,NOERR);
+  drop_entity_def_init(&pParse->drop_entity_def, X, (struct Token){}, NOERR,
+                       ENTITY_TYPE_TRIGGER);
+  sql_drop_trigger(pParse);
 }
 
 /////////////////////////////////// ANALYZE ///////////////////////////////////
@@ -1434,17 +1480,21 @@ cmd ::= ANALYZE nm(X).          {sqlite3Analyze(pParse, &X);}
 
 //////////////////////// ALTER TABLE table ... ////////////////////////////////
 cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
-  sql_alter_table_rename(pParse,X,&Z);
+  rename_entity_def_init(&pParse->rename_entity_def, X, Z);
+  sql_alter_table_rename(pParse);
 }
 
 cmd ::= ALTER TABLE fullname(X) ADD CONSTRAINT nm(Z) FOREIGN KEY
         LP eidlist(FA) RP REFERENCES nm(T) eidlist_opt(TA) refargs(R)
         defer_subclause_opt(D). {
-    sql_create_foreign_key(pParse, X, &Z, FA, &T, TA, D, R);
+  create_constraint_def_init(&pParse->create_fk_def.base, X, Z, D, false);
+  create_fk_def_init(&pParse->create_fk_def, FA, &T, TA, R);
+  sql_create_foreign_key(pParse);
 }
 
 cmd ::= ALTER TABLE fullname(X) DROP CONSTRAINT nm(Z). {
-    sql_drop_foreign_key(pParse, X, &Z);
+  drop_entity_def_init(&pParse->drop_entity_def, X, Z, false, ENTITY_TYPE_FK);
+  sql_drop_foreign_key(pParse);
 }
 
 //////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
new file mode 100644
index 000000000..c3ed2f72d
--- /dev/null
+++ b/src/box/sql/parse_def.h
@@ -0,0 +1,388 @@
+#ifndef TARANTOOL_BOX_SQL_PARSE_DEF_H_INCLUDED
+#define TARANTOOL_BOX_SQL_PARSE_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "box/fkey.h"
+#include "box/sql.h"
+
+/**
+ * This file contains auxiliary structures and functions which
+ * are used only during parsing routine (see parse.y).
+ * Their main purpose is to assemble common parts of altered
+ * entities (such as name, or IF EXISTS clause) and pass them
+ * as a one object to further functions.
+ *
+ * Hierarchy is following:
+ *
+ * Base structure is ALTER.
+ * ALTER is omitted only for CREATE TABLE since table is filled
+ * with meta-information just-in-time of parsing:
+ * for instance, as soon as field's name and type are recognized
+ * they are added to space definition.
+ *
+ * DROP is general for all existing objects and includes
+ * name of object itself, name of parent object (table),
+ * IF EXISTS clause and may contain on-drop behaviour
+ * (CASCADE/RESTRICT, but now it is always RESTRICT).
+ * Hence, it terms of grammar - it is a terminal symbol.
+ *
+ * RENAME can be applied only to table (at least now, since it is
+ * ANSI extension), so it is also terminal symbol.
+ *
+ * CREATE in turn can be expanded to nonterminal symbol
+ * CREATE CONSTRAINT or to terminal CREATE TABLE/INDEX/TRIGGER.
+ * CREATE CONSTRAINT unfolds to FOREIGN KEY or UNIQUE/PRIMARY KEY.
+ *
+ * For instance:
+ * ALTER TABLE t ADD CONSTRAINT c FOREIGN KEY REFERENCES t2(id);
+ * ALTER *TABLE* -> CREATE ENTITY -> CREATE CONSTRAINT -> CREATE FK
+ *
+ * CREATE TRIGGER tr1 ...
+ * ALTER *TABLE* -> CREATE ENTITY -> CREATE TRIGGER
+ *
+ * All terminal symbols are stored as a union within
+ * parsing context (struct Parse).
+ */
+
+/**
+ * Each token coming out of the lexer is an instance of
+ * this structure. Tokens are also used as part of an expression.
+ */
+struct Token {
+	/** Text of the token. Not NULL-terminated! */
+	const char *z;
+	/** Number of characters in this token. */
+	unsigned int n;
+	bool isReserved;
+};
+
+/**
+ * Structure representing foreign keys constraints appeared
+ * within CREATE TABLE statement. Used only during parsing.
+ */
+struct fkey_parse {
+	/**
+	 * Foreign keys constraint declared in <CREATE TABLE ...>
+	 * statement. They must be coded after space creation.
+	 */
+	struct fkey_def *fkey;
+	/**
+	 * If inside CREATE TABLE statement we want to declare
+	 * self-referenced FK constraint, we must delay their
+	 * resolution until the end of parsing of all columns.
+	 * E.g.: CREATE TABLE t1(id REFERENCES t1(b), b);
+	 */
+	struct ExprList *selfref_cols;
+	/**
+	 * Still, self-referenced columns might be NULL, if
+	 * we declare FK constraints referencing PK:
+	 * CREATE TABLE t1(id REFERENCES t1) - it is a valid case.
+	 */
+	bool is_self_referenced;
+	/** Organize these structs into linked list. */
+	struct rlist link;
+};
+
+/**
+ * Possible SQL index types. Note that PK and UNIQUE constraints
+ * are implemented as indexes and have their own types:
+ * _CONSTRAINT_PK and _CONSTRAINT_UNIQUE.
+ */
+enum sql_index_type {
+	SQL_INDEX_TYPE_NON_UNIQUE = 0,
+	SQL_INDEX_TYPE_UNIQUE,
+	SQL_INDEX_TYPE_CONSTRAINT_UNIQUE,
+	SQL_INDEX_TYPE_CONSTRAINT_PK,
+	sql_index_type_MAX
+};
+
+enum entity_type {
+	ENTITY_TYPE_TABLE = 0,
+	ENTITY_TYPE_VIEW,
+	ENTITY_TYPE_INDEX,
+	ENTITY_TYPE_TRIGGER,
+	ENTITY_TYPE_CK,
+	ENTITY_TYPE_FK,
+	entity_type_MAX
+};
+
+enum alter_action {
+	ALTER_ACTION_CREATE = 0,
+	ALTER_ACTION_DROP,
+	ALTER_ACTION_RENAME
+};
+
+struct alter_entity_def {
+	/** Type of topmost entity. */
+	enum entity_type entity_type;
+	/** Action to be performed using current entity. */
+	enum alter_action alter_action;
+	/** As a rule it is a name of table to be altered. */
+	struct SrcList *entity_name;
+};
+
+struct rename_entity_def {
+	struct alter_entity_def base;
+	struct Token new_name;
+};
+
+struct create_entity_def {
+	struct alter_entity_def base;
+	struct Token name;
+	/** Statement comes with IF NOT EXISTS clause. */
+	bool if_not_exist;
+};
+
+struct create_table_def {
+	struct create_entity_def base;
+	struct Table *new_table;
+	/**
+	 * Number of FK constraints declared within
+	 * CREATE TABLE statement.
+	 */
+	uint32_t fkey_count;
+	/**
+	 * Foreign key constraint appeared in CREATE TABLE stmt.
+	 */
+	struct rlist new_fkey;
+	/** True, if table to be created has AUTOINCREMENT PK. */
+	bool has_autoinc;
+};
+
+struct create_view_def {
+	struct create_entity_def base;
+	/**
+	 * Starting position of CREATE VIEW ... statement.
+	 * It is used to fetch whole statement, which is
+	 * saved as raw string to space options.
+	 */
+	struct Token *create_start;
+	/** List of column aliases (SELECT x AS y ...). */
+	struct ExprList *aliases;
+	struct Select *select;
+};
+
+struct drop_entity_def {
+	struct alter_entity_def base;
+	/** Name of index/trigger/constraint to be dropped. */
+	struct Token name;
+	/** Statement comes with IF EXISTS clause. */
+	bool if_exist;
+};
+
+struct create_trigger_def {
+	struct create_entity_def base;
+	/** One of TK_BEFORE, TK_AFTER, TK_INSTEAD. */
+	int tr_tm;
+	/** One of TK_INSERT, TK_UPDATE, TK_DELETE. */
+	int op;
+	/** Column list if this is an UPDATE trigger. */
+	struct IdList *cols;
+	/** When clause. */
+	struct Expr *when;
+};
+
+struct create_constraint_def {
+	struct create_entity_def base;
+	/** One of DEFERRED, IMMEDIATE. */
+	bool is_deferred;
+};
+
+struct create_ck_def {
+	struct create_constraint_def base;
+	/** AST representing check expression. */
+	struct ExprSpan *expr;
+};
+
+struct create_fk_def {
+	struct create_constraint_def base;
+	struct ExprList *child_cols;
+	struct Token *parent_name;
+	struct ExprList *parent_cols;
+	/**
+	 * Encoded actions for MATCH, ON DELETE and
+	 * ON UPDATE clauses.
+	 */
+	int actions;
+};
+
+struct create_index_def {
+	struct create_constraint_def base;
+	/** List of indexed columns. */
+	struct ExprList *cols;
+	/** One of _PRIMARY_KEY, _UNIQUE, _NON_UNIQUE. */
+	enum sql_index_type idx_type;
+	enum sort_order sort_order;
+};
+
+/** Basic initialisers of parse structures.*/
+static inline void
+alter_entity_def_init(struct alter_entity_def *alter_def,
+		      struct SrcList *entity_name)
+{
+	alter_def->entity_name = entity_name;
+}
+
+static inline void
+rename_entity_def_init(struct rename_entity_def *rename_def,
+		       struct SrcList *table_name, struct Token new_name)
+{
+	alter_entity_def_init(&rename_def->base, table_name);
+	rename_def->new_name = new_name;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) rename_def;
+	alter_def->entity_type = ENTITY_TYPE_TABLE;
+	alter_def->alter_action = ALTER_ACTION_RENAME;
+}
+
+static inline void
+create_entity_def_init(struct create_entity_def *create_def, struct Token name,
+		       bool if_not_exist)
+{
+	create_def->name = name;
+	create_def->if_not_exist = if_not_exist;
+}
+
+static inline void
+create_constraint_def_init(struct create_constraint_def *constr_def,
+			   struct SrcList *parent_name, struct Token name,
+			   bool if_not_exists, bool is_deferred)
+{
+	alter_entity_def_init(&constr_def->base.base, parent_name);
+	create_entity_def_init(&constr_def->base, name, if_not_exists);
+	constr_def->is_deferred = is_deferred;
+}
+
+static inline void
+drop_entity_def_init(struct drop_entity_def *drop_def,
+		     struct SrcList *parent_name, struct Token name,
+		     bool if_exist, enum entity_type entity_type)
+{
+	alter_entity_def_init(&drop_def->base, parent_name);
+	drop_def->name = name;
+	drop_def->if_exist = if_exist;
+	drop_def->base.entity_type = entity_type;
+	drop_def->base.alter_action = ALTER_ACTION_DROP;
+}
+
+static inline void
+create_trigger_def_init(struct create_trigger_def *trigger_def,
+			struct SrcList *table_name, struct Token name,
+			int tr_tm, int op, struct IdList *cols,
+			struct Expr *when, bool if_not_exists)
+{
+	alter_entity_def_init(&trigger_def->base.base, table_name);
+	create_entity_def_init(&trigger_def->base, name, if_not_exists);
+	trigger_def->tr_tm = tr_tm;
+	trigger_def->op = op;
+	trigger_def->cols = cols;
+	trigger_def->when = when;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) trigger_def;
+	alter_def->entity_type = ENTITY_TYPE_TRIGGER;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_ck_def_init(struct create_ck_def *ck_def, struct ExprSpan *expr)
+{
+	ck_def->expr = expr;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) ck_def;
+	alter_def->entity_type = ENTITY_TYPE_CK;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_index_def_init(struct create_index_def *index_def, struct ExprList *cols,
+		      enum sql_index_type idx_type, enum sort_order sort_order)
+{
+	index_def->cols = cols;
+	index_def->idx_type = idx_type;
+	index_def->sort_order = sort_order;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) index_def;
+	alter_def->entity_type = ENTITY_TYPE_INDEX;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_fk_def_init(struct create_fk_def *fk_def, struct ExprList *child_cols,
+		   struct Token *parent_name, struct ExprList *parent_cols,
+		   int actions)
+{
+	assert(fk_def != NULL);
+	fk_def->child_cols = child_cols;
+	fk_def->parent_name = parent_name;
+	fk_def->parent_cols = parent_cols;
+	fk_def->actions = actions;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) fk_def;
+	alter_def->entity_type = ENTITY_TYPE_FK;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_table_def_init(struct create_table_def *table_def, struct Token name,
+		      bool if_not_exists)
+{
+	create_entity_def_init(&table_def->base, name, if_not_exists);
+	rlist_create(&table_def->new_fkey);
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) table_def;
+	alter_def->entity_type = ENTITY_TYPE_TABLE;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_view_def_init(struct create_view_def *view_def, struct Token name,
+		     struct Token *create, struct ExprList *aliases,
+		     struct Select *select, bool if_not_exists)
+{
+	create_entity_def_init(&view_def->base, name, if_not_exists);
+	view_def->create_start = create;
+	view_def->select = select;
+	view_def->aliases = aliases;
+	struct alter_entity_def *alter_def =
+		(struct alter_entity_def *) view_def;
+	alter_def->entity_type = ENTITY_TYPE_VIEW;
+	alter_def->alter_action = ALTER_ACTION_CREATE;
+}
+
+static inline void
+create_table_def_destroy(struct create_table_def *table_def)
+{
+	struct fkey_parse *fk;
+	rlist_foreach_entry(fk, &table_def->new_fkey, link)
+		sql_expr_list_delete(sql_get(), fk->selfref_cols);
+}
+
+#endif /* TARANTOOL_BOX_SQL_PARSE_DEF_H_INCLUDED */
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 824578e45..c2660eeff 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -273,7 +273,6 @@ sql_parser_create(struct Parse *parser, sqlite3 *db)
 {
 	memset(parser, 0, sizeof(struct Parse));
 	parser->db = db;
-	rlist_create(&parser->new_fkey);
 	rlist_create(&parser->record_list);
 	region_create(&parser->region, &cord()->slabc);
 }
@@ -286,9 +285,6 @@ sql_parser_destroy(Parse *parser)
 	sqlite3 *db = parser->db;
 	sqlite3DbFree(db, parser->aLabel);
 	sql_expr_list_delete(db, parser->pConstExpr);
-	struct fkey_parse *fk;
-	rlist_foreach_entry(fk, &parser->new_fkey, link)
-		sql_expr_list_delete(db, fk->selfref_cols);
 	if (db != NULL) {
 		assert(db->lookaside.bDisable >=
 		       parser->disableLookaside);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 6462467bc..50d787d11 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -1616,9 +1616,9 @@ void
 sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
 			   struct Expr *expr, struct ExprList *expr_list)
 {
-	/* Fake SrcList for parser->pNewTable */
+	/* Fake SrcList for parser->create_table_def */
 	SrcList sSrc;
-	/* Name context for parser->pNewTable */
+	/* Name context for parser->create_table_def  */
 	NameContext sNC;
 
 	assert(type == NC_IsCheck || type == NC_IdxExpr);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index bc617d7ce..139c3c763 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -67,6 +67,7 @@
 
 #include <stdbool.h>
 
+#include "parse_def.h"
 #include "box/field_def.h"
 #include "box/sql.h"
 #include "box/txn.h"
@@ -1915,19 +1916,6 @@ struct UnpackedRecord {
 				 */
 };
 
-/*
- * Possible SQL index types. Note that PK and UNIQUE constraints
- * are implemented as indexes and have their own types:
- * SQL_INDEX_TYPE_CONSTRAINT_PK and
- * SQL_INDEX_TYPE_CONSTRAINT_UNIQUE.
- */
-enum sql_index_type {
-    SQL_INDEX_TYPE_NON_UNIQUE = 0,
-    SQL_INDEX_TYPE_UNIQUE,
-    SQL_INDEX_TYPE_CONSTRAINT_UNIQUE,
-    SQL_INDEX_TYPE_CONSTRAINT_PK,
-};
-
 /**
  * Fetch statistics concerning tuples to be selected:
  * logarithm of number of tuples which has the same key as for
@@ -1957,20 +1945,6 @@ index_field_tuple_est(const struct index_def *idx, uint32_t field);
 /** [10*log_{2}(1048576)] == 200 */
 #define DEFAULT_TUPLE_LOG_COUNT 200
 
-/*
- * Each token coming out of the lexer is an instance of
- * this structure.  Tokens are also used as part of an expression.
- *
- * Note if Token.z==0 then Token.dyn and Token.n are undefined and
- * may contain random values.  Do not make any assumptions about Token.dyn
- * and Token.n when Token.z==0.
- */
-struct Token {
-	const char *z;		/* Text of the token.  Not NULL-terminated! */
-	unsigned int n;		/* Number of characters in this token */
-	bool isReserved;         /* If reserved keyword or not */
-};
-
 /*
  * An instance of this structure contains information needed to generate
  * code for a SELECT that contains aggregate functions.
@@ -2664,33 +2638,6 @@ enum ast_type {
 	ast_type_MAX
 };
 
-/**
- * Structure representing foreign keys constraints appeared
- * within CREATE TABLE statement. Used only during parsing.
- */
-struct fkey_parse {
-	/**
-	 * Foreign keys constraint declared in <CREATE TABLE ...>
-	 * statement. They must be coded after space creation.
-	 */
-	struct fkey_def *fkey;
-	/**
-	 * If inside CREATE TABLE statement we want to declare
-	 * self-referenced FK constraint, we must delay their
-	 * resolution until the end of parsing of all columns.
-	 * E.g.: CREATE TABLE t1(id REFERENCES t1(b), b);
-	 */
-	struct ExprList *selfref_cols;
-	/**
-	 * Still, self-referenced columns might be NULL, if
-	 * we declare FK constraints referencing PK:
-	 * CREATE TABLE t1(id REFERENCES t1) - it is a valid case.
-	 */
-	bool is_self_referenced;
-	/** Organize these structs into linked list. */
-	struct rlist link;
-};
-
 /*
  * An SQL parser context.  A copy of this structure is passed through
  * the parser and down into all the parser action routine in order to
@@ -2728,7 +2675,6 @@ struct Parse {
 	int nLabel;		/* Number of labels used */
 	int *aLabel;		/* Space to hold the labels */
 	ExprList *pConstExpr;	/* Constant expressions */
-	Token constraintName;	/* Name of the constraint currently being parsed */
 	int nMaxArg;		/* Max args passed to user function by sub-program */
 	int nSelect;		/* Number of SELECT statements seen */
 	int nSelectIndent;	/* How far to indent SELECTTRACE() output */
@@ -2775,28 +2721,31 @@ struct Parse {
 	VList *pVList;		/* Mapping between variable names and numbers */
 	Vdbe *pReprepare;	/* VM being reprepared (sqlite3Reprepare()) */
 	const char *zTail;	/* All SQL text past the last semicolon parsed */
-	Table *pNewTable;	/* A table being constructed by CREATE TABLE */
 	Table *pZombieTab;	/* List of Table objects to delete after code gen */
 	TriggerPrg *pTriggerPrg;	/* Linked list of coded triggers */
 	With *pWith;		/* Current WITH clause, or NULL */
 	With *pWithToFree;	/* Free this WITH object at the end of the parse */
 	/**
-	 * Number of FK constraints declared within
-	 * CREATE TABLE statement.
+	 * One of parse_def structures which are used to
+	 * assemble and carry arguments of DDL routines
+	 * from parse.y
 	 */
-	uint32_t fkey_count;
-	/**
-	 * Foreign key constraint appeared in CREATE TABLE stmt.
-	 */
-	struct rlist new_fkey;
+	union {
+		struct create_ck_def create_ck_def;
+		struct create_fk_def create_fk_def;
+		struct create_index_def create_index_def;
+		struct create_trigger_def create_trigger_def;
+		struct create_view_def create_view_def;
+		struct rename_entity_def rename_entity_def;
+		struct drop_entity_def drop_entity_def;
+	};
+	struct create_table_def create_table_def;
 	/**
 	 * List of all records that were inserted in system spaces
 	 * in current statement.
 	 */
 	struct rlist record_list;
 	bool initiateTTrans;	/* Initiate Tarantool transaction */
-	/** True, if table to be created has AUTOINCREMENT PK. */
-	bool is_new_table_autoinc;
 	/** If set - do not emit byte code at all, just parse.  */
 	bool parse_only;
 	/** Type of parsed_ast member. */
@@ -3357,7 +3306,9 @@ Table *sqlite3ResultSetOfSelect(Parse *, Select *);
 struct index *
 sql_table_primary_key(const struct Table *tab);
 
-void sqlite3StartTable(Parse *, Token *, int);
+struct Table *
+sqlite3StartTable(Parse *, Token *, int);
+
 void sqlite3AddColumn(Parse *, Token *, struct type_def *);
 
 /**
@@ -3375,16 +3326,16 @@ void
 sql_column_add_nullable_action(struct Parse *parser,
 			       enum on_conflict_action nullable_action);
 
-void sqlite3AddPrimaryKey(Parse *, ExprList *, int, enum sort_order);
+void
+sqlite3AddPrimaryKey(struct Parse *parse);
 
 /**
  * Add a new CHECK constraint to the table currently under
  * construction.
  * @param parser Parsing context.
- * @param span Expression span object.
  */
 void
-sql_add_check_constraint(Parse *parser, ExprSpan *span);
+sql_add_check_constraint(Parse *parser);
 
 void sqlite3AddDefaultValue(Parse *, ExprSpan *);
 void sqlite3AddCollateType(Parse *, Token *);
@@ -3431,16 +3382,9 @@ int sqlite3FaultSim(int);
  * The parser calls this routine in order to create a new VIEW.
  *
  * @param parse_context Current parsing context.
- * @param begin The CREATE token that begins the statement.
- * @param name The token that holds the name of the view.
- * @param aliases Optional list of view column names.
- * @param select A SELECT statement that will become the new view.
- * @param if_exists Suppress error messages if VIEW already exists.
  */
 void
-sql_create_view(struct Parse *parse_context, struct Token *begin,
-		struct Token *name, struct ExprList *aliases,
-		struct Select *select, bool if_exists);
+sql_create_view(struct Parse *parse_context);
 
 /**
  * Helper to convert SQLite affinity to corresponding
@@ -3472,7 +3416,7 @@ void
 sql_store_select(struct Parse *parse_context, struct Select *select);
 
 void
-sql_drop_table(struct Parse *, struct SrcList *, bool, bool);
+sql_drop_table(struct Parse *, bool);
 void sqlite3DeleteTable(sqlite3 *, Table *);
 void sqlite3Insert(Parse *, SrcList *, Select *, IdList *,
 		   enum on_conflict_action);
@@ -3501,36 +3445,19 @@ void sqlite3IdListDelete(sqlite3 *, IdList *);
  * parse->pNewTable is a table that is currently being
  * constructed by a CREATE TABLE statement.
  *
- * col_list is a list of columns to be indexed.  col_list will be
- * NULL if this is a primary key or unique-constraint on the most
- * recent column added to the table currently under construction.
- *
  * @param parse All information about this parse.
- * @param token Index name. May be NULL.
- * @param tbl_name Table to index. Use pParse->pNewTable ifNULL.
- * @param col_list A list of columns to be indexed.
- * @param start The CREATE token that begins this statement.
- * @param sort_order Sort order of primary key when pList==NULL.
- * @param if_not_exist Omit error if index already exists.
- * @param idx_type The index type.
  */
 void
-sql_create_index(struct Parse *parse, struct Token *token,
-		 struct SrcList *tbl_name, struct ExprList *col_list,
-		 struct Token *start, enum sort_order sort_order,
-		 bool if_not_exist, enum sql_index_type idx_type);
+sql_create_index(struct Parse *parse);
 
 /**
  * This routine will drop an existing named index.  This routine
  * implements the DROP INDEX statement.
  *
  * @param parse_context Current parsing context.
- * @param index_name_list List containing index name.
- * @param table_token Token representing table name.
- * @param if_exists True, if statement contains 'IF EXISTS' clause.
  */
 void
-sql_drop_index(struct Parse *, struct SrcList *, struct Token *, bool);
+sql_drop_index(struct Parse *parse_context);
 
 int sqlite3Select(Parse *, Select *, SelectDest *);
 Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
@@ -3932,20 +3859,9 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
  * After the trigger actions have been parsed, the
  * sql_trigger_finish() function is called to complete the trigger
  * construction process.
- *
- * @param parse The parse context of the CREATE TRIGGER statement.
- * @param name The name of the trigger.
- * @param tr_tm One of TK_BEFORE, TK_AFTER, TK_INSTEAD.
- * @param op One of TK_INSERT, TK_UPDATE, TK_DELETE.
- * @param columns column list if this is an UPDATE OF trigger.
- * @param table The name of the table/view the trigger applies to.
- * @param when  WHEN clause.
- * @param no_err Suppress errors if the trigger already exists.
  */
 void
-sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
-		  int op, struct IdList *columns, struct SrcList *table,
-		  struct Expr *when, int no_err);
+sql_trigger_begin(struct Parse *parse);
 
 /**
  * This routine is called after all of the trigger actions have
@@ -3965,11 +3881,9 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
  * VDBE code.
  *
  * @param parser Parser context.
- * @param name The name of trigger to drop.
- * @param no_err Suppress errors if the trigger already exists.
  */
 void
-sql_drop_trigger(struct Parse *parser, struct SrcList *name, bool no_err);
+sql_drop_trigger(struct Parse *parser);
 
 /**
  * Drop a trigger given a pointer to that trigger.
@@ -4149,38 +4063,18 @@ fkey_change_defer_mode(struct Parse *parse_context, bool is_deferred);
  * OR to handle <CREATE TABLE ...>
  *
  * @param parse_context Parsing context.
- * @param child Name of table to be altered. NULL on CREATE TABLE
- *              statement processing.
- * @param constraint Name of the constraint to be created. May be
- *                   NULL on CREATE TABLE statement processing.
- *                   Then, auto-generated name is used.
- * @param child_cols Columns of child table involved in FK.
- *                   May be NULL on CREATE TABLE statement processing.
- *                   If so, the last column added is used.
- * @param parent Name of referenced table.
- * @param parent_cols List of referenced columns. If NULL, columns
- *                    which make up PK of referenced table are used.
- * @param is_deferred Is FK constraint initially deferred.
- * @param actions ON DELETE, UPDATE and INSERT resolution
- *                algorithms (e.g. CASCADE, RESTRICT etc).
  */
 void
-sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
-		       struct Token *constraint, struct ExprList *child_cols,
-		       struct Token *parent, struct ExprList *parent_cols,
-		       bool is_deferred, int actions);
+sql_create_foreign_key(struct Parse *parse_context);
 
 /**
  * Function called from parser to handle
  * <ALTER TABLE table DROP CONSTRAINT constraint> SQL statement.
  *
  * @param parse_context Parsing context.
- * @param table Table to be altered.
- * @param constraint Name of constraint to be dropped.
  */
 void
-sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
-		     struct Token *constraint);
+sql_drop_foreign_key(struct Parse *parse_context);
 
 void sqlite3Detach(Parse *, Expr *);
 int sqlite3AtoF(const char *z, double *, int);
@@ -4398,12 +4292,9 @@ extern int sqlite3PendingByte;
  * command.
  *
  * @param parse Current parsing context.
- * @param src_tab The table to rename.
- * @param new_name_tk Token containing new name of the table.
  */
 void
-sql_alter_table_rename(struct Parse *parse, struct SrcList *src_tab,
-		       struct Token *new_name_tk);
+sql_alter_table_rename(struct Parse *parse);
 
 /**
  * Return the length (in bytes) of the token that begins at z[0].
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 4eebfe527..f2088f118 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -449,7 +449,7 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		sqlite3OomFault(db);
 		return SQLITE_NOMEM_BKPT;
 	}
-	assert(pParse->pNewTable == 0);
+	assert(pParse->create_table_def.new_table == NULL);
 	assert(pParse->parsed_ast.trigger == NULL);
 	assert(pParse->nVar == 0);
 	assert(pParse->pVList == 0);
@@ -529,7 +529,7 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		sqlite3VdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
 	}
-	sqlite3DeleteTable(db, pParse->pNewTable);
+	sqlite3DeleteTable(db, pParse->create_table_def.new_table);
 
 	if (pParse->pWithToFree)
 		sqlite3WithDelete(db, pParse->pWithToFree);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7d5dc9e23..3456a244d 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -62,34 +62,31 @@ sqlite3DeleteTriggerStep(sqlite3 * db, TriggerStep * pTriggerStep)
 }
 
 void
-sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
-		  int op, struct IdList *columns, struct SrcList *table,
-		  struct Expr *when, int no_err)
+sql_trigger_begin(struct Parse *parse)
 {
 	/* The new trigger. */
 	struct sql_trigger *trigger = NULL;
 	/* The database connection. */
 	struct sqlite3 *db = parse->db;
-	/* The name of the Trigger. */
-	char *trigger_name = NULL;
-
-	/* pName->z might be NULL, but not pName itself. */
-	assert(name != NULL);
-	assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE);
-	assert(op > 0 && op < 0xff);
+	struct create_trigger_def *trigger_def = &parse->create_trigger_def;
+	struct create_entity_def *create_def = &trigger_def->base;
+	struct alter_entity_def *alter_def = &create_def->base;
+	assert(alter_def->entity_type == ENTITY_TYPE_TRIGGER);
+	assert(alter_def->alter_action == ALTER_ACTION_CREATE);
 
-	if (table == NULL || db->mallocFailed)
+	char *trigger_name = NULL;
+	if (alter_def->entity_name == NULL || db->mallocFailed)
 		goto trigger_cleanup;
-	assert(table->nSrc == 1);
-
-	trigger_name = sqlite3NameFromToken(db, name);
+	assert(alter_def->entity_name->nSrc == 1);
+	assert(create_def->name.n > 0);
+	trigger_name = sqlite3NameFromToken(db, &create_def->name);
 	if (trigger_name == NULL)
 		goto trigger_cleanup;
 
 	if (sqlite3CheckIdentifierName(parse, trigger_name) != SQLITE_OK)
 		goto trigger_cleanup;
 
-	const char *table_name = table->a[0].zName;
+	const char *table_name = alter_def->entity_name->a[0].zName;
 	uint32_t space_id;
 	if (schema_find_id(BOX_SPACE_ID, 2, table_name, strlen(table_name),
 			   &space_id) != 0)
@@ -112,6 +109,7 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 		int name_reg = ++parse->nMem;
 		sqlite3VdbeAddOp4(parse->pVdbe, OP_String8, 0, name_reg, 0,
 				  name_copy, P4_DYNAMIC);
+		bool no_err = create_def->if_not_exist;
 		if (vdbe_emit_halt_with_presence_test(parse, BOX_TRIGGER_ID, 0,
 						      name_reg, 1,
 						      ER_TRIGGER_EXISTS,
@@ -129,13 +127,14 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 	trigger->space_id = space_id;
 	trigger->zName = trigger_name;
 	trigger_name = NULL;
-
-	trigger->op = (u8) op;
-	trigger->tr_tm = tr_tm;
-	trigger->pWhen = sqlite3ExprDup(db, when, EXPRDUP_REDUCE);
-	trigger->pColumns = sqlite3IdListDup(db, columns);
-	if ((when != NULL && trigger->pWhen == NULL) ||
-	    (columns != NULL && trigger->pColumns == NULL))
+	assert(trigger_def->op == TK_INSERT || trigger_def->op == TK_UPDATE ||
+	       trigger_def->op== TK_DELETE);
+	trigger->op = (u8) trigger_def->op;
+	trigger->tr_tm = trigger_def->tr_tm;
+	trigger->pWhen = sqlite3ExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
+	trigger->pColumns = sqlite3IdListDup(db, trigger_def->cols);
+	if ((trigger->pWhen != NULL && trigger->pWhen == NULL) ||
+	    (trigger->pColumns != NULL && trigger->pColumns == NULL))
 		goto trigger_cleanup;
 	assert(parse->parsed_ast.trigger == NULL);
 	parse->parsed_ast.trigger = trigger;
@@ -143,9 +142,9 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
 
  trigger_cleanup:
 	sqlite3DbFree(db, trigger_name);
-	sqlite3SrcListDelete(db, table);
-	sqlite3IdListDelete(db, columns);
-	sql_expr_delete(db, when, false);
+	sqlite3SrcListDelete(db, alter_def->entity_name);
+	sqlite3IdListDelete(db, trigger_def->cols);
+	sql_expr_delete(db, trigger_def->when, false);
 	if (parse->parsed_ast.trigger == NULL)
 		sql_trigger_delete(db, trigger);
 	else
@@ -424,9 +423,14 @@ vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
 }
 
 void
-sql_drop_trigger(struct Parse *parser, struct SrcList *name, bool no_err)
+sql_drop_trigger(struct Parse *parser)
 {
-
+	struct drop_entity_def *drop_def = &parser->drop_entity_def;
+	struct alter_entity_def *alter_def = &drop_def->base;
+	assert(alter_def->entity_type == ENTITY_TYPE_TRIGGER);
+	assert(alter_def->alter_action == ALTER_ACTION_DROP);
+	struct SrcList *name = alter_def->entity_name;
+	bool no_err = drop_def->if_exist;
 	sqlite3 *db = parser->db;
 	if (db->mallocFailed)
 		goto drop_trigger_cleanup;
diff --git a/test/sql-tap/index7.test.lua b/test/sql-tap/index7.test.lua
index a8ce37f4b..ffb42403c 100755
--- a/test/sql-tap/index7.test.lua
+++ b/test/sql-tap/index7.test.lua
@@ -397,6 +397,6 @@ test:do_catchsql_test(
                 "_index"."id" = "_space"."id" AND
                 "_space"."name"='TEST8';
         ]],
-        {0, {"pk_TEST8_2",0,"unique_C1_1",1}})
+        {0, {"pk_unnamed_TEST8_2",0,"unique_C1_1",1}})
 
 test:finish_test()






More information about the Tarantool-patches mailing list