Skip to content

Commit

Permalink
sql: rework parsing of CREATE TABLE statement
Browse files Browse the repository at this point in the history
This patch reworks CREATE TABLE statement parsing. Prior to this patch,
VDBE for CREATE TABLE statement was generated while the statement was in
the process of being parsed. After this patch, it will be generated only
after the parsing has successfully completed.

Part of tarantool#3319
Part of tarantool#4621
Part of tarantool#5485
Part of tarantool#8100

NO_DOC=Will be added later
NO_CHANGELOG=Will be added later
  • Loading branch information
ImeevMA committed Apr 12, 2023
1 parent 37b3ba6 commit 2d20c66
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 95 deletions.
84 changes: 34 additions & 50 deletions src/box/sql/build.c
Expand Up @@ -181,27 +181,8 @@ sql_space_primary_key(const struct space *space)
return space->index[0];
}

/*
* Begin constructing a new table representation in memory. This is
* the first of several action routines that get called in response
* to a CREATE TABLE statement. In particular, this routine is called
* after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
* flag is true if the table should be stored in the auxiliary database
* file instead of in the main database file. This is normally the case
* when the "TEMP" or "TEMPORARY" keyword occurs in between
* CREATE and TABLE.
*
* 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 sqlEndTable() routine
* is called to complete the construction of the new table record.
*
* @param pParse Parser context.
* @param pName1 First part of the name of the table or view.
*/
struct space *
sqlStartTable(Parse *pParse, Token *pName)
sqlStartTable(Parse *pParse, Token *pName, struct Token *engine_name)
{
char *zName = 0; /* The name of the new table */
struct space *new_space = NULL;
Expand All @@ -212,13 +193,27 @@ sqlStartTable(Parse *pParse, Token *pName)
if (sqlCheckIdentifierName(pParse, zName) != 0)
goto cleanup;

if (engine_name->n > ENGINE_NAME_MAX) {
diag_set(ClientError, ER_CREATE_SPACE, zName,
"space engine name is too long");
pParse->is_aborted = true;
goto cleanup;
}

new_space = sql_template_space_new(pParse, zName);
if (new_space == NULL)
goto cleanup;

strlcpy(new_space->def->engine_name,
sql_storage_engine_strs[current_session()->sql_default_engine],
ENGINE_NAME_MAX + 1);
const char *engine;
if (engine_name->n == 0) {
uint8_t engine_id = current_session()->sql_default_engine;
engine = sql_storage_engine_strs[engine_id];
} else {
engine = sql_normalized_name_region_new(&pParse->region,
engine_name->z,
engine_name->n);
}
strlcpy(new_space->def->engine_name, engine, ENGINE_NAME_MAX + 1);

assert(v == sqlGetVdbe(pParse));
if (!sql_get()->init.busy)
Expand Down Expand Up @@ -329,9 +324,9 @@ void
sql_create_column_start(struct Parse *parse, struct Token *name,
enum field_type type)
{
struct space *space = parse->create_table_def.new_space;
bool is_alter = space == NULL;
if (is_alter) {
struct space *space = parse->space;
if (parse->type == PARSE_TYPE_ADD_COLUMN) {
assert(space == NULL);
const char *space_name = parse->src_list->a[0].zName;
space = space_by_name(space_name);
if (space == NULL) {
Expand Down Expand Up @@ -688,8 +683,6 @@ sql_create_check_contraint(struct Parse *parser, struct sql_parse_check *cdef)
sql_expr_delete(expr_span->pExpr);

struct space *space = parser->space;
if (space == NULL)
space = parser->create_table_def.new_space;
bool is_alter_add_constr = space == NULL;
bool is_field_ck = cdef->column_name.n != 0;
uint32_t fieldno = 0;
Expand Down Expand Up @@ -908,8 +901,8 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
memcpy(raw, index_parts, index_parts_sz);
index_parts = raw;

if (parse->create_table_def.new_space != NULL ||
parse->space != NULL) {
if (parse->type == PARSE_TYPE_CREATE_TABLE ||
parse->type == PARSE_TYPE_ADD_COLUMN) {
sqlVdbeAddOp2(v, OP_SCopy, space_id_reg, entry_reg);
sqlVdbeAddOp2(v, OP_Integer, idx_def->iid, entry_reg + 1);
} else {
Expand Down Expand Up @@ -1173,9 +1166,7 @@ vdbe_emit_fk_constraint_create(struct Parse *parse_context,
* vdbe_emit_create_constraints()), but we know register
* where it will be stored.
*/
bool is_alter_add_constr =
parse_context->create_table_def.new_space == NULL &&
parse_context->space == NULL;
bool is_alter_add_constr = parse_context->space == NULL;
if (!is_alter_add_constr)
sqlVdbeAddOp2(vdbe, OP_SCopy, fk->child_id, regs);
else
Expand Down Expand Up @@ -1242,8 +1233,8 @@ vdbe_emit_create_constraints(struct Parse *parse, int reg_space_id)
return;
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
assert(reg_space_id != 0);
struct space *space = parse->create_table_def.new_space;
bool is_alter = space == NULL;
struct space *space = parse->space;
bool is_alter = parse->type == PARSE_TYPE_ADD_COLUMN;
uint32_t i = 0;
/*
* If it is an <ALTER TABLE ADD COLUMN>, then we have to
Expand All @@ -1252,10 +1243,8 @@ vdbe_emit_create_constraints(struct Parse *parse, int reg_space_id)
* (inside space object) and ending with new index_count
* (inside ephemeral space).
*/
if (is_alter) {
space = parse->space;
if (is_alter)
i = space_by_name(space->def->name)->index_count;
}
assert(space != NULL);
for (; i < space->index_count; ++i) {
struct index *idx = space->index[i];
Expand Down Expand Up @@ -1354,9 +1343,8 @@ vdbe_emit_create_constraints(struct Parse *parse, int reg_space_id)
void
sqlEndTable(struct Parse *pParse)
{
struct space *new_space = pParse->create_table_def.new_space;
if (new_space == NULL)
return;
struct space *new_space = pParse->space;
assert(new_space != NULL && pParse->type == PARSE_TYPE_CREATE_TABLE);
assert(!sql_get()->init.busy);
assert(!new_space->def->opts.is_view);

Expand Down Expand Up @@ -1391,7 +1379,7 @@ sqlEndTable(struct Parse *pParse)
sql_xstrdup(new_space->def->name), P4_DYNAMIC);
const char *error_msg = tt_sprintf(tnt_errcode_desc(ER_SPACE_EXISTS),
new_space->def->name);
bool no_err = pParse->create_table_def.base.if_not_exist;
bool no_err = pParse->create_table.if_not_exists;
vdbe_emit_halt_with_presence_test(pParse, BOX_SPACE_ID, 2, name_reg, 1,
ER_SPACE_EXISTS, error_msg,
(no_err != 0), OP_NoConflict);
Expand All @@ -1418,7 +1406,8 @@ sql_create_view(struct Parse *parse_context)
goto create_view_fail;
}
struct space *space = sqlStartTable(parse_context,
&create_entity_def->name);
&create_entity_def->name,
&Token_nil);
if (space == NULL || parse_context->is_aborted)
goto create_view_fail;
struct space *select_res_space =
Expand Down Expand Up @@ -1895,9 +1884,6 @@ sql_create_foreign_key(struct Parse *parse_context,
char *constraint_name = NULL;
bool is_self_referenced = false;
struct space *space = parse_context->space;
struct create_table_def *table_def = &parse_context->create_table_def;
if (space == NULL)
space = table_def->new_space;
/*
* Space under construction during <CREATE TABLE>
* processing or shallow copy of space during <ALTER TABLE
Expand Down Expand Up @@ -1936,7 +1922,7 @@ sql_create_foreign_key(struct Parse *parse_context,
* Child space already exists if it is
* <ALTER TABLE ADD COLUMN>.
*/
if (table_def->new_space == NULL)
if (parse_context->type == PARSE_TYPE_ADD_COLUMN)
child_space = space;
struct rlist *fkeys =
&parse_context->create_fk_constraint_parse_def.fkeys;
Expand Down Expand Up @@ -2394,9 +2380,7 @@ sql_create_index(struct Parse *parse, struct Token *index_name,
* Find the table that is to be indexed.
* Return early if not found.
*/
struct space *space = parse->create_table_def.new_space;
if (space == NULL)
space = parse->space;
struct space *space = parse->space;
bool is_create_table_or_add_col = space != NULL;
if (tbl_name != NULL) {
assert(index_name->n > 0 && index_name->z != NULL);
Expand Down
18 changes: 2 additions & 16 deletions src/box/sql/parse.y
Expand Up @@ -172,9 +172,7 @@ cmd ::= ROLLBACK TO savepoint_opt nm(X). {
//
cmd ::= create_table create_table_args with_opts.
create_table ::= createkw TABLE ifnotexists(E) nm(Y). {
create_table_def_init(&pParse->create_table_def, &Y, E);
pParse->create_table_def.new_space = sqlStartTable(pParse, &Y);
sql_parse_create_table(pParse);
sql_parse_create_table(pParse, &Y, E);
}
createkw(A) ::= CREATE(A). {disableLookaside(pParse);}

Expand All @@ -188,19 +186,7 @@ with_opts ::= WITH engine_opts.
with_opts ::= .

engine_opts ::= ENGINE EQ STRING(A). {
/* Note that specifying engine clause overwrites default engine. */
if (A.n > ENGINE_NAME_MAX) {
diag_set(ClientError, ER_CREATE_SPACE,
pParse->create_table_def.new_space->def->name,
"space engine name is too long");
pParse->is_aborted = true;
return;
}
/* Need to dequote name. */
char *normalized_name = sql_name_from_token(&A);
memcpy(pParse->create_table_def.new_space->def->engine_name, normalized_name,
strlen(normalized_name) + 1);
sql_xfree(normalized_name);
sql_parse_table_engine(pParse, &A);
}

/*
Expand Down
16 changes: 13 additions & 3 deletions src/box/sql/parse_def.c
Expand Up @@ -96,15 +96,19 @@ last_column_name(struct Parse *parse)
static const char *
new_table_name(struct Parse *parse)
{
if (parse->create_table_def.new_space == NULL)
if (parse->type != PARSE_TYPE_CREATE_TABLE)
return NULL;
return parse->create_table_def.new_space->def->name;
struct Token *name = &parse->create_table.name;
return sql_normalized_name_region_new(&parse->region, name->z, name->n);
}

void
sql_parse_create_table(struct Parse *parse)
sql_parse_create_table(struct Parse *parse, struct Token *name,
bool if_not_exists)
{
parse->type = PARSE_TYPE_CREATE_TABLE;
parse->create_table.name = *name;
parse->create_table.if_not_exists = if_not_exists;
}

/** Append a new column to column list. */
Expand Down Expand Up @@ -405,3 +409,9 @@ sql_parse_column_default(struct Parse *parse, struct ExprSpan *expr)
{
parse->column_list.a[parse->column_list.n - 1].default_expr = *expr;
}

void
sql_parse_table_engine(struct Parse *parse, struct Token *engine_name)
{
parse->create_table.engine_name = *engine_name;
}
30 changes: 16 additions & 14 deletions src/box/sql/parse_def.h
Expand Up @@ -235,6 +235,16 @@ struct sql_parse_column_list {
uint32_t n;
};

/** Description of the table being created. */
struct sql_parse_table {
/** Table name. */
struct Token name;
/** Table engine name. */
struct Token engine_name;
/** IF NOT EXISTS flag. */
bool if_not_exists;
};

/** Constant tokens for integer values. */
extern const struct Token sqlIntTokens[];

Expand Down Expand Up @@ -344,11 +354,6 @@ struct create_entity_def {
bool if_not_exist;
};

struct create_table_def {
struct create_entity_def base;
struct space *new_space;
};

struct create_ck_constraint_parse_def {
/** List of ck_constraint_parse_def objects. */
struct rlist checks;
Expand Down Expand Up @@ -523,14 +528,6 @@ create_trigger_def_init(struct create_trigger_def *trigger_def,
trigger_def->when = when;
}

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, ENTITY_TYPE_TABLE, NULL, name,
if_not_exists);
}

static inline void
create_ck_constraint_parse_def_init(struct create_ck_constraint_parse_def *def)
{
Expand Down Expand Up @@ -592,7 +589,8 @@ sql_parse_savepoint_rollback(struct Parse *parse, const struct Token *name);

/** Save parsed CREATE TABLE statement. */
void
sql_parse_create_table(struct Parse *parse);
sql_parse_create_table(struct Parse *parse, struct Token *name,
bool if_not_exists);

/** Save parsed CREATE INDEX statement. */
void
Expand Down Expand Up @@ -695,4 +693,8 @@ sql_parse_column_nullable_action(struct Parse *parse, int action,
void
sql_parse_column_default(struct Parse *parse, struct ExprSpan *expr);

/** Save parsed table engine clause. */
void
sql_parse_table_engine(struct Parse *parse, struct Token *engine_name);

#endif /* TARANTOOL_BOX_SQL_PARSE_DEF_H_INCLUDED */
4 changes: 2 additions & 2 deletions src/box/sql/resolve.c
Expand Up @@ -1529,9 +1529,9 @@ sql_resolve_self_reference(struct Parse *parser, struct space_def *def,
return;
}

/* Fake SrcList for parser->create_table_def */
/* Fake SrcList for CREATE TABLE */
SrcList sSrc;
/* Name context for parser->create_table_def */
/* Name context for CREATE TABLE */
NameContext sNC;

memset(&sNC, 0, sizeof(sNC));
Expand Down
29 changes: 20 additions & 9 deletions src/box/sql/sqlInt.h
Expand Up @@ -2038,14 +2038,6 @@ struct Parse {
struct drop_trigger_def drop_trigger_def;
struct drop_view_def drop_view_def;
};
/**
* Table def or column def is not part of union since
* information being held must survive till the end of
* parsing of whole <CREATE TABLE> or
* <ALTER TABLE ADD COLUMN> statement (to pass it to
* sqlEndTable() sql_create_column_end() function).
*/
struct create_table_def create_table_def;
/** Parsed statement type. */
enum parse_type type;
/** Savepoint description for savepoint-related statements. */
Expand All @@ -2062,6 +2054,8 @@ struct Parse {
struct sql_parse_unique primary_key;
/** Description of created index. */
struct sql_parse_index create_index;
/** Description of created table. */
struct sql_parse_table create_table;
/** Name of the column with AUTOINCREMENT. */
struct Expr *autoinc_name;
/** Source list for the statement. */
Expand Down Expand Up @@ -2689,8 +2683,25 @@ void
sqlSelectAddColumnTypeAndCollation(Parse *, struct space_def *, Select *);
struct space *sqlResultSetOfSelect(Parse *, Select *);

/*
* Begin constructing a new table representation in memory. This is
* the first of several action routines that get called in response
* to a CREATE TABLE statement. In particular, this routine is called
* after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
* flag is true if the table should be stored in the auxiliary database
* file instead of in the main database file. This is normally the case
* when the "TEMP" or "TEMPORARY" keyword occurs in between
* CREATE and TABLE.
*
* The new table record is initialized and put in pParse->space.
* 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 sqlEndTable() routine
* is called to complete the construction of the new table record.
*/
struct space *
sqlStartTable(Parse *, Token *);
sqlStartTable(struct Parse *parse, struct Token *name,
struct Token *engine_name);

/**
* Add new field to the format of ephemeral space. If it is <ALTER TABLE> create
Expand Down
6 changes: 5 additions & 1 deletion src/box/sql/tokenize.c
Expand Up @@ -567,6 +567,10 @@ sql_finish_parsing(struct Parse *parse)
sqlSavepoint(parse, SAVEPOINT_ROLLBACK, &parse->savepoint.name);
break;
case PARSE_TYPE_CREATE_TABLE:
parse->space = sqlStartTable(parse, &parse->create_table.name,
&parse->create_table.engine_name);
if (parse->is_aborted)
return;
if (sql_finish_table_properties(parse) != 0)
return;
sqlEndTable(parse);
Expand Down Expand Up @@ -624,7 +628,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
i = 0;
/* sqlParserTrace(stdout, "parser: "); */
pEngine = sqlParserAlloc(new_xmalloc);
assert(pParse->create_table_def.new_space == NULL);
assert(pParse->space == NULL);
assert(pParse->parsed_ast.trigger == NULL);
assert(pParse->nVar == 0);
assert(pParse->pVList == 0);
Expand Down

0 comments on commit 2d20c66

Please sign in to comment.