From 44a055d733b049fc07f7ad510f698d84b278714c Mon Sep 17 00:00:00 2001 From: Mergen Imeev <imeevma@tarantool.org> Date: Tue, 3 Oct 2023 17:07:32 +0300 Subject: [PATCH] sql: second lookup for column names This patch add second lookup for column name in the following cases: - columns in tuple foreign key creation clause; - columns in field foreign key creation clause; - columns in primary key creation clause; - columns in unique constraint creation clause; - columns in expressions; - columns in UPDATE TABLE statement; - columns in INSERT INTO statement; - columns in CREATE INDEX statement; Also, second lookup for table name in expressions also now supported. Part of #4467 NO_DOC=will be added later NO_CHANGELOG=will be added later --- src/box/sql.c | 5 +- src/box/sql/build.c | 5 + src/box/sql/expr.c | 21 +- src/box/sql/resolve.c | 221 ++++++++++-------- src/box/sql/select.c | 13 +- src/box/sql/sqlInt.h | 27 ++- src/box/sql/update.c | 37 ++- ...67_sql_id_backwards_compatibility_test.lua | 50 ++-- 8 files changed, 229 insertions(+), 150 deletions(-) diff --git a/src/box/sql.c b/src/box/sql.c index df74301d2a..7918f8a05b 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1695,7 +1695,10 @@ sql_fieldno_by_token(const struct space *space, const struct Token *name) uint32_t sql_fieldno_by_id(const struct space *space, const struct IdList_item *id) { - return sql_space_fieldno(space, id->zName); + uint32_t res = sql_space_fieldno(space, id->zName); + if (res != UINT32_MAX || id->legacy_name == NULL) + return res; + return sql_space_fieldno(space, id->legacy_name); } uint32_t diff --git a/src/box/sql/build.c b/src/box/sql/build.c index bccd2567af..bd5de51a58 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2922,6 +2922,10 @@ sql_id_list_append(struct IdList *list, struct Token *name_token) list->a = sqlArrayAllocate(list->a, sizeof(list->a[0]), &list->nId, &i); assert(i >= 0); list->a[i].zName = sql_name_from_token(name_token); + if (name_token->z[0] != '"') { + list->a[i].legacy_name = sql_legacy_name_new(name_token->z, + name_token->n); + } return list; } @@ -2933,6 +2937,7 @@ sqlIdListDelete(struct IdList *pList) return; for (i = 0; i < pList->nId; i++) { sql_xfree(pList->a[i].zName); + sql_xfree(pList->a[i].legacy_name); } sql_xfree(pList->a); sql_xfree(pList); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index c95b50fa45..64b6fb0c87 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1628,6 +1628,7 @@ sql_expr_list_dup(struct ExprList *p, int flags) } pItem->zName = sql_xstrdup(pOldItem->zName); pItem->zSpan = sql_xstrdup(pOldItem->zSpan); + pItem->legacy_name = sql_xstrdup(pOldItem->legacy_name); pItem->sort_order = pOldItem->sort_order; pItem->done = 0; pItem->bSpanIsTab = pOldItem->bSpanIsTab; @@ -1702,6 +1703,7 @@ sqlIdListDup(struct IdList *p) struct IdList_item *pNewItem = &pNew->a[i]; struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = sql_xstrdup(pOldItem->zName); + pNewItem->legacy_name = sql_xstrdup(pOldItem->legacy_name); pNewItem->idx = pOldItem->idx; } return pNew; @@ -1808,7 +1810,10 @@ sqlExprListAppendVector(Parse * pParse, /* Parsing context */ pList = sql_expr_list_append(pList, pSubExpr); assert(pList->nExpr == iFirst + i + 1); pList->a[pList->nExpr - 1].zName = pColumns->a[i].zName; + pList->a[pList->nExpr - 1].legacy_name = + pColumns->a[i].legacy_name; pColumns->a[i].zName = 0; + pColumns->a[i].legacy_name = NULL; } if (pExpr->op == TK_SELECT) { @@ -1870,6 +1875,7 @@ sqlExprListSetName(Parse * pParse, /* Parsing context */ assert(item->zName == NULL); if (dequote) { item->zName = sql_name_new(pName->z, pName->n); + item->legacy_name = sql_legacy_name_new(pName->z, pName->n); } else { item->zName = sql_xstrndup(pName->z, pName->n); } @@ -1900,6 +1906,7 @@ exprListDeleteNN(struct ExprList *pList) sql_expr_delete(pItem->pExpr); sql_xfree(pItem->zName); sql_xfree(pItem->zSpan); + sql_xfree(pItem->legacy_name); } sql_xfree(pList->a); sql_xfree(pList); @@ -5439,13 +5446,23 @@ uint32_t sql_fieldno_by_expr(const struct space *space, const struct Expr *expr) { assert(expr->op == TK_ID); - return sql_space_fieldno(space, expr->u.zToken); + uint32_t res = sql_space_fieldno(space, expr->u.zToken); + if (res != UINT32_MAX || (expr->flags & EP_Lookup2) == 0) + return res; + char *old_name_str = sql_legacy_name_new0(expr->u.zToken); + res = sql_space_fieldno(space, old_name_str); + sql_xfree(old_name_str); + return res; + } uint32_t sql_fieldno_by_item(const struct space *space, const struct ExprList_item *item) { - return sql_space_fieldno(space, item->zName); + uint32_t res = sql_space_fieldno(space, item->zName); + if (res != UINT32_MAX || item->legacy_name == NULL) + return res; + return sql_space_fieldno(space, item->legacy_name); } uint32_t diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 1f62b958f6..6c7e5e5826 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -130,48 +130,67 @@ resolveAlias(struct ExprList *pEList, int iCol, struct Expr *pExpr, sql_xfree(pDup); } -/* - * Return TRUE if the name zCol occurs anywhere in the USING clause. - * - * Return FALSE if the USING clause is NULL or if it does not contain - * zCol. +/** + * Return TRUE if the name occurs anywhere in the USING clause. + * Return FALSE if the USING clause is NULL or if it does not contain the name. */ -static int -nameInUsingClause(IdList * pUsing, const char *zCol) +static bool +nameInUsingClause(struct IdList *pUsing, const char *zCol, const char *old_col) { if (pUsing) { - int k; - for (k = 0; k < pUsing->nId; k++) { + for (int k = 0; k < pUsing->nId; k++) { if (strcmp(pUsing->a[k].zName, zCol) == 0) - return 1; + return true; + } + for (int i = 0; i < pUsing->nId; i++) { + if (strcmp(pUsing->a[i].zName, old_col) == 0) + return true; } } - return 0; + return false; } -/* - * Subqueries stores the original database, table and column names for their - * result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". - * Check to see if the zSpan given to this routine matches the zTab, - * and zCol. If any of zTab, and zCol are NULL then those fields will - * match anything. - */ -int -sqlMatchSpanName(const char *zSpan, - const char *zCol, const char *zTab - ) +bool +sqlMatchSpanName(const char *span, const char *col, const char *tab, + const char *old_col, const char *old_tab) { - int n; - for (n = 0; ALWAYS(zSpan[n]) && zSpan[n] != '.'; n++) { + size_t n; + const char *name = span; + size_t len = strlen(span); + for (n = 0; n < len && span[n] != '.'; n++) { } - if (zTab && (sqlStrNICmp(zSpan, zTab, n) != 0 || zTab[n] != 0)) { - return 0; + if (tab != NULL) { + if ((strlen(tab) != n || strncmp(name, tab, n) != 0) && + (old_tab == NULL || strlen(old_tab) != n || + strncmp(name, old_tab, n) != 0)) + return false; + name += n + 1; + len -= n + 1; } - zSpan += n + 1; - if (zCol && strcmp(zSpan, zCol) != 0) { - return 0; + if (col == NULL) + return true; + if ((strlen(col) != len || strncmp(name, col, len) != 0) && + (old_col == NULL || strlen(old_col) != len || + strncmp(name, old_col, len) != 0)) + return false; + return true; +} + +/** + * Match spans for all expressions of the list with the table name and the + * column name. + */ +static int +sql_find_column_expr(const struct ExprList *list, const char *tab_name, + const char *col_name, const char *old_tab, + const char *old_col) +{ + for (int j = 0; j < list->nExpr; j++) { + if (sqlMatchSpanName(list->a[j].zSpan, col_name, tab_name, + old_tab, old_col)) + return j; } - return 1; + return -1; } /* @@ -199,13 +218,30 @@ sqlMatchSpanName(const char *zSpan, * in pParse and return WRC_Abort. Return WRC_Prune on success. */ static int -lookupName(Parse * pParse, /* The parsing context */ - const char *zTab, /* Name of table containing column, or NULL */ - const char *zCol, /* Name of the column. */ - NameContext * pNC, /* The name context used to resolve the name */ - Expr * pExpr /* Make this EXPR node point to the selected column */ - ) +lookupName(struct Parse *pParse, struct Expr *pExpr, struct NameContext *pNC) { + const char *zTab = NULL; + const char *zCol = NULL; + char *old_tab = NULL; + char *old_col = NULL; + const struct Expr *col = NULL; + if (pExpr->op == TK_DOT) { + assert(pExpr->pLeft->op == TK_ID && pExpr->pRight->op == TK_ID); + zTab = pExpr->pLeft->u.zToken; + if ((pExpr->pLeft->flags & EP_Lookup2) != 0) + old_tab = sql_legacy_name_new0(zTab); + zCol = pExpr->pRight->u.zToken; + if ((pExpr->pRight->flags & EP_Lookup2) != 0) + old_col = sql_legacy_name_new0(zCol); + col = pExpr->pRight; + } else { + assert(pExpr->op == TK_ID); + zCol = pExpr->u.zToken; + if ((pExpr->flags & EP_Lookup2) != 0) + old_col = sql_legacy_name_new0(zCol); + col = pExpr; + } + int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ int cntTab = 0; /* Number of matching table names */ @@ -241,16 +277,14 @@ lookupName(Parse * pParse, /* The parsing context */ selFlags & SF_NestedFrom) != 0) { int hit = 0; pEList = pItem->pSelect->pEList; - for (j = 0; j < pEList->nExpr; j++) { - if (sqlMatchSpanName - (pEList->a[j].zSpan, zCol, - zTab)) { - cnt++; - cntTab = 2; - pMatch = pItem; - pExpr->iColumn = j; - hit = 1; - } + j = sql_find_column_expr(pEList, zTab, + zCol, old_tab, + old_col); + if (j >= 0) { + cnt++; + cntTab = 2; + pMatch = pItem; + pExpr->iColumn = j; } if (hit || zTab == 0) continue; @@ -260,37 +294,33 @@ lookupName(Parse * pParse, /* The parsing context */ pItem->zAlias ? pItem-> zAlias : space_def->name; assert(zTabName != 0); - if (strcmp(zTabName, zTab) != 0) { + if (strcmp(zTabName, zTab) != 0 && + (old_tab == NULL || + strcmp(zTabName, old_tab) != 0)) continue; - } } if (0 == (cntTab++)) { pMatch = pItem; } - for (j = 0; j < (int)space_def->field_count; - j++) { - if (strcmp(space_def->fields[j].name, - zCol) == 0) { - /* If there has been exactly one prior match and this match - * is for the right-hand table of a NATURAL JOIN or is in a - * USING clause, then skip this match. - */ - if (cnt == 1) { - if (pItem->fg. - jointype & - JT_NATURAL) - continue; - if (nameInUsingClause - (pItem->pUsing, - zCol)) - continue; - } - cnt++; - pMatch = pItem; - pExpr->iColumn = (i16) j; - break; - } - } + uint32_t fieldno = + sql_fieldno_by_expr(pItem->space, col); + if (fieldno == UINT32_MAX) + continue; + j = fieldno; + /* + * If there has been exactly one prior match and + * this match is for the right-hand table of a + * NATURAL JOIN or is in a USING clause, then + * skip this match. + */ + if (cnt == 1 && + ((pItem->fg.jointype & JT_NATURAL) != 0 || + nameInUsingClause(pItem->pUsing, zCol, + old_col))) + continue; + cnt++; + pMatch = pItem; + pExpr->iColumn = (i16) j; } if (pMatch) { pExpr->iTable = pMatch->iCursor; @@ -326,10 +356,12 @@ lookupName(Parse * pParse, /* The parsing context */ cntTab++; for (iCol = 0; iCol < (int)space_def->field_count; iCol++) { - if (strcmp(space_def->fields[iCol].name, - zCol) == 0) { + const char *name = + space_def->fields[iCol].name; + if (strcmp(name, zCol) == 0 || + (old_col != NULL && + strcmp(name, old_col) == 0)) break; - } } if (iCol < (int)space_def->field_count) { cnt++; @@ -364,7 +396,9 @@ lookupName(Parse * pParse, /* The parsing context */ if ((pEList = pNC->pEList) != 0 && zTab == 0 && cnt == 0) { for (j = 0; j < pEList->nExpr; j++) { char *zAs = pEList->a[j].zName; - if (zAs != 0 && strcmp(zAs, zCol) == 0) { + if (zAs != 0 && (strcmp(zAs, zCol) == 0 || + (old_col != NULL && + strcmp(zAs, old_col) == 0))) { Expr *pOrig; assert(pExpr->pLeft == 0 && pExpr->pRight == 0); @@ -379,14 +413,14 @@ lookupName(Parse * pParse, /* The parsing context */ ER_SQL_PARSER_GENERIC, tt_sprintf(err, zAs)); pParse->is_aborted = true; - return WRC_Abort; + goto lookupname_end; } if (sqlExprVectorSize(pOrig) != 1) { diag_set(ClientError, ER_SQL_PARSER_GENERIC, "row value misused"); pParse->is_aborted = true; - return WRC_Abort; + goto lookupname_end; } resolveAlias(pEList, j, pExpr, "", nSubquery); @@ -457,7 +491,9 @@ lookupName(Parse * pParse, /* The parsing context */ pExpr->pRight = 0; pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN_REF); lookupname_end: - if (cnt == 1) { + sql_xfree(old_tab); + sql_xfree(old_col); + if (cnt == 1 && !pParse->is_aborted) { assert(pNC != 0); /* Increment the nRef value on all name contexts from TopNC up to * the point where the name matched. @@ -549,31 +585,12 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) case TK_ID:{ if ((pNC->ncFlags & NC_AllowAgg) != 0) pNC->ncFlags |= NC_HasUnaggregatedId; - return lookupName(pParse, 0, pExpr->u.zToken, pNC, - pExpr); + return lookupName(pParse, pExpr, pNC); } - /* A table name and column name: ID.ID - * Or a database, table and column: ID.ID.ID - */ - case TK_DOT:{ - const char *zColumn; - const char *zTable; - Expr *pRight; - - /* if( pSrcList==0 ) break; */ - pRight = pExpr->pRight; - if (pRight->op == TK_ID) { - zTable = pExpr->pLeft->u.zToken; - zColumn = pRight->u.zToken; - } else { - assert(pRight->op == TK_DOT); - zTable = pRight->pLeft->u.zToken; - zColumn = pRight->pRight->u.zToken; - } - return lookupName(pParse, zTable, zColumn, pNC, - pExpr); - } + /* A table name and column name: ID.ID. */ + case TK_DOT: + return lookupName(pParse, pExpr, pNC); /* Resolve function names */ diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 65c4a4eaa9..d2e339f6d7 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -4260,10 +4260,14 @@ flattenSubquery(Parse * pParse, /* Parsing context */ pList = pParent->pEList; for (i = 0; i < pList->nExpr; i++) { if (pList->a[i].zName == 0) { - char *str = pList->a[i].zSpan; - int len = strlen(str); + const char *str = pList->a[i].zSpan; + size_t len = strlen(str); char *name = sql_name_new(str, len); pList->a[i].zName = name; + if (pList->a[i].zSpan[0] != '"') { + pList->a[i].legacy_name = + sql_legacy_name_new(str, len); + } } } if (pSub->pOrderBy) { @@ -5004,9 +5008,11 @@ selectExpander(Walker * pWalker, Select * p) pNew = sql_expr_list_append(pNew, a[k].pExpr); pNew->a[pNew->nExpr - 1].zName = a[k].zName; pNew->a[pNew->nExpr - 1].zSpan = a[k].zSpan; + pNew->a[pNew->nExpr - 1].legacy_name = a[k].legacy_name; a[k].zName = 0; a[k].zSpan = 0; a[k].pExpr = 0; + a[k].legacy_name = NULL; continue; } /* @@ -5047,7 +5053,8 @@ selectExpander(Walker * pWalker, Select * p) assert(zName != NULL); if (zTName != NULL && pSub != NULL && sqlMatchSpanName(pSub->pEList->a[j].zSpan, - 0, zTName) == 0) + NULL, zTName, NULL, + NULL) == 0) continue; tableSeen = 1; diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 62a44e1cf6..9d36196e95 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -1495,6 +1495,8 @@ struct ExprList { Expr *pExpr; /* The list of expressions */ char *zName; /* Token associated with this expression */ char *zSpan; /* Original text of the expression */ + /** Normalized name for the second lookup. */ + char *legacy_name; enum sort_order sort_order; unsigned done:1; /* A flag to indicate when processing is finished */ unsigned bSpanIsTab:1; /* zSpan holds DB.TABLE.COLUMN */ @@ -1538,6 +1540,8 @@ struct ExprSpan { struct IdList { struct IdList_item { char *zName; /* Name of the identifier */ + /** Normalized name for the second lookup. */ + char *legacy_name; int idx; /* Index in some Table.aCol[] of a column named zName */ } *a; int nId; /* Number of identifiers on the list */ @@ -3138,21 +3142,26 @@ sql_fieldno_by_token(const struct space *space, const struct Token *name); /** * Return the fieldno of the field with the name defined by the element of - * IdList. Return UINT32_MAX if the field was not found. + * IdList. A second lookup will be performed if the field is not found on the + * first try and field legacy_name of the id is not NULL. Return UINT32_MAX if + * the field was not found. */ uint32_t sql_fieldno_by_id(const struct space *space, const struct IdList_item *id); /** - * Return the fieldno of the field with the name defined by the expression. - * Return UINT32_MAX if the field was not found. + * Return the fieldno of the field with the name defined by the expression. A + * second lookup will be performed if the field is not found on the first try + * and flag EP_Lookup2 is set. Return UINT32_MAX if the field was not found. */ uint32_t sql_fieldno_by_expr(const struct space *space, const struct Expr *expr); /** * Return the fieldno of the field with the name defined by the name of - * expression from exprList. Return UINT32_MAX if the field was not found. + * expression from exprList. A second lookup will be performed if the field is + * not found on the first try and field legacy_name of element of ExprList is + * not NULL. Return UINT32_MAX if the field was not found. */ uint32_t sql_fieldno_by_item(const struct space *space, const struct ExprList_item *it); @@ -4077,7 +4086,15 @@ void sqlSelectPrep(Parse *, Select *, NameContext *); const char * sql_select_op_name(int id); -int sqlMatchSpanName(const char *, const char *, const char *); +/** + * Subqueries stores the original table and column names for their result sets + * in ExprList.a[].zSpan, in the form "TABLE.COLUMN". Check to see if the span + * given to this routine matches given table or column names. + */ +bool +sqlMatchSpanName(const char *span, const char *col, const char *tab, + const char *old_col, const char *old_tab); + int sqlResolveExprNames(NameContext *, Expr *); int sqlResolveExprListNames(NameContext *, ExprList *); void sqlResolveSelectNames(Parse *, Select *, NameContext *); diff --git a/src/box/sql/update.c b/src/box/sql/update.c index 33306aa54e..b91e1f5b51 100644 --- a/src/box/sql/update.c +++ b/src/box/sql/update.c @@ -134,35 +134,24 @@ sqlUpdate(Parse * pParse, /* The parser context */ if (sqlResolveExprNames(&sNC, pChanges->a[i].pExpr)) { goto update_cleanup; } - for (j = 0; j < (int)def->field_count; j++) { - if (strcmp(def->fields[j].name, - pChanges->a[i].zName) == 0) { - if (pPk && - sql_space_column_is_in_pk(space, j)) - is_pk_modified = true; - if (aXRef[j] != -1) { - const char *err = - "set id list: duplicate "\ - "column name %s"; - err = tt_sprintf(err, - pChanges->a[i].zName); - diag_set(ClientError, - ER_SQL_PARSER_GENERIC, - err); - pParse->is_aborted = true; - goto update_cleanup; - } - aXRef[j] = i; - upd_cols_cnt++; - break; - } - } - if (j >= (int)def->field_count) { + uint32_t fieldno = sql_fieldno_by_item(space, &pChanges->a[i]); + if (fieldno == UINT32_MAX) { diag_set(ClientError, ER_NO_SUCH_FIELD_NAME_IN_SPACE, pChanges->a[i].zName, def->name); pParse->is_aborted = true; goto update_cleanup; } + if (pPk != NULL && sql_space_column_is_in_pk(space, fieldno)) + is_pk_modified = true; + if (aXRef[fieldno] != -1) { + diag_set(ClientError, ER_SQL_PARSER_GENERIC, + tt_sprintf("set id list: duplicate column " + "name %s", pChanges->a[i].zName)); + pParse->is_aborted = true; + goto update_cleanup; + } + aXRef[fieldno] = i; + upd_cols_cnt++; } /* * The SET expressions are not actually used inside the diff --git a/test/sql-luatest/gh_4467_sql_id_backwards_compatibility_test.lua b/test/sql-luatest/gh_4467_sql_id_backwards_compatibility_test.lua index 9ff8d22d21..60ee8b1db6 100644 --- a/test/sql-luatest/gh_4467_sql_id_backwards_compatibility_test.lua +++ b/test/sql-luatest/gh_4467_sql_id_backwards_compatibility_test.lua @@ -113,11 +113,12 @@ g.test_create_tuple_foreign_key = function() t.assert(box.space.tab.foreign_key.one ~= nil); box.space.tab:drop() - -- Make sure table name is looked up twice in tuple FK creation clause. + -- Make sure table name and column names are looked up twice in tuple FK + -- creation clause. box.execute([[CREATE TABLE ASD(QWE INT PRIMARY KEY);]]) local sql = [[CREATE TABLE ASD1(QWE1 INT PRIMARY KEY, ZXC1 INT, - CONSTRAINT f1 FOREIGN KEY (ZXC1) REFERENCES Asd(QWE), - CONSTRAINT f2 FOREIGN KEY (ZXC1) REFERENCES asD1(QWE1));]] + CONSTRAINT f1 FOREIGN KEY (zXC1) REFERENCES Asd(QwE), + CONSTRAINT f2 FOREIGN KEY (ZXc1) REFERENCES asD1(qwe1));]] t.assert_equals(box.execute(sql), {row_count = 1}) local foreign_key = box.space.ASD1.foreign_key t.assert_equals(foreign_key.f1.space, box.space.ASD.id) @@ -136,11 +137,12 @@ g.test_create_field_foreign_key = function() t.assert(box.space.tab:format()[2].foreign_key.one ~= nil); box.space.tab:drop() - -- Make sure table name is looked up twice in field FK creation clause. + -- Make sure table name and column names are looked up twice in field FK + -- creation clause. box.execute([[CREATE TABLE ASD(QWE INT PRIMARY KEY);]]) local sql = [[CREATE TABLE ASD1(QWE1 INT PRIMARY KEY, ZXC1 INT - CONSTRAINT f1 REFERENCES Asd(QWE) - CONSTRAINT f2 REFERENCES asD1(QWE1));]] + CONSTRAINT f1 REFERENCES Asd(qwE) + CONSTRAINT f2 REFERENCES asD1(Qwe1));]] t.assert_equals(box.execute(sql), {row_count = 1}) local foreign_key = box.space.ASD1:format()[2].foreign_key t.assert_equals(foreign_key.f1.space, box.space.ASD.id) @@ -164,6 +166,14 @@ g.test_expression = function() t.assert_equals(box.execute(sql).rows, {{124}}) box.space.Tab:drop() box.func.fUn:drop() + + -- Make sure table names and column names are looked up twice in + -- expressions. + box.execute([[CREATE TABLE ASD(QWE INT PRIMARY KEY, ZXC INT);]]) + box.space.ASD:insert({12, 21}) + local rows = box.execute([[SELECT AsD.qWe * 2 - zxc FROM asd;]]).rows + t.assert_equals(rows, {{3}}) + box.space.ASD:drop() end) end @@ -174,6 +184,12 @@ g.test_create_primary_key = function() CONSTRAINT Three PRIMARY KEY (Second, First));]]) t.assert_equals(box.space.Tab.index[0].name, 'Three'); box.space.Tab:drop() + + -- Make sure column names in primary key definition are looked up twice. + box.execute([[CREATE TABLE ASD(QWE INT, ZXC INT, + CONSTRAINT THREE PRIMARY KEY (Qwe, zxc));]]) + t.assert_equals(box.space.ASD.index[0].name, 'THREE'); + box.space.ASD:drop() end) end @@ -185,6 +201,13 @@ g.test_create_unique = function() CONSTRAINT Four UNIQUE (Second, First));]]) t.assert_equals(box.space.Tab.index[1].name, 'Four'); box.space.Tab:drop() + + -- Make sure column names in unique constraint definition are looked up + -- twice. + box.execute([[CREATE TABLE ASD(QWE INT PRIMARY KEY, ZXC INT, + CONSTRAINT FOUR UNIQUE (Qwe, zxc));]]) + t.assert_equals(box.space.ASD.index[1].name, 'FOUR'); + box.space.ASD:drop() end) end @@ -302,13 +325,13 @@ g.test_update = function() t.assert_equals(box.space.Tab:select(), {{3, 11, -1}}) box.space.Tab:drop() - -- Make sure table name is looked up twice in UPDATE. + -- Make sure table name and column names are looked up twice in UPDATE. box.execute([[CREATE TABLE ASD(PK INT PRIMARY KEY, QWE INT, ZXC INT);]]) box.space.ASD:insert({3, 5, 7}) - sql = [[UPDATE aSd SET QWE = 1, ZXC = 9;]] + sql = [[UPDATE aSd SET qWE = 1, ZXc = 9;]] t.assert_equals(box.execute(sql), {row_count = 1}); t.assert_equals(box.space.ASD:select(), {{3, 1, 9}}) - sql = [[UPDATE asd SET (QWE, ZXC) = (11, -1);]] + sql = [[UPDATE asd SET (qwE, Zxc) = (11, -1);]] t.assert_equals(box.execute(sql), {row_count = 1}); t.assert_equals(box.space.ASD:select(), {{3, 11, -1}}) box.space.ASD:drop() @@ -323,9 +346,9 @@ g.test_insert = function() t.assert_equals(box.space.Tab:select(), {{123, nil, 321}}) box.space.Tab:drop() - -- Make sure table name is looked up twice in INSERT. + -- Make sure table name and column names are looked up twice in INSERT. box.execute([[CREATE TABLE ASD(PK INT PRIMARY KEY, QWE INT, ZXC INT);]]) - box.execute([[INSERT INTO asd(PK, ZXC) VALUES (123, 321);]]) + box.execute([[INSERT INTO asd(pK, zxc) VALUES (123, 321);]]) t.assert_equals(box.space.ASD:select(), {{123, nil, 321}}) box.space.ASD:drop() end) @@ -341,9 +364,10 @@ g.test_create_index = function() t.assert(box.space.Tab.index.In1 ~= nil) box.space.Tab:drop() - -- Make sure table name is looked up twice in CREATE INDEX. + -- Make sure table name and column names looked up twice in + -- CREATE INDEX. box.execute([[CREATE TABLE ASD(PK INT PRIMARY KEY, QWE INT, ZXC INT);]]) - sql = [[CREATE INDEX IND ON Asd(ZXC, QWE);]] + sql = [[CREATE INDEX IND ON Asd(zxc, qWe);]] t.assert_equals(box.execute(sql), {row_count = 1}) t.assert(box.space.ASD.index.IND ~= nil) box.space.ASD:drop() -- GitLab