* [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form @ 2019-02-27 11:13 Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag Kirill Shcherbatov ` (8 more replies) 0 siblings, 9 replies; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Perform SQL name normalization: cast name to the upper-case (via Unicode Character Folding). Casing is locale-dependent and context-sensitive. The result may be longer or shorter than the original. For example, ß is converted to SS. The result is similar to SQL UPPER function. Performed extensive code refactoring to pass parser instance in routines that use sql_normalize_name function. This makes possible to raise an error in case of normalizing failure. Changes in version 2: - Setup parser error externally Branch: http://github.com/tarantool/tarantool/tree/kshch/gh-3991-fix-names-normalization Issue: https://github.com/tarantool/tarantool/issues/3931 Kirill Shcherbatov (7): sql: refactor sql_alloc_src_list to set diag sql: rework sql_src_list_enlarge to set diag sql: refactor sql_src_list_append to set diag sql: refactor sql_name_from_token to set diag sql: refactor sql_trigger_step_allocate to set diag sql: refactor sql_expr_create to set diag sql: store regular identifiers in case-normal form src/box/lua/lua_sql.c | 11 +- src/box/sql/alter.c | 8 +- src/box/sql/analyze.c | 47 +-- src/box/sql/build.c | 517 +++++++++++++------------- src/box/sql/delete.c | 14 +- src/box/sql/expr.c | 300 +++++++-------- src/box/sql/fk_constraint.c | 202 ++++++---- src/box/sql/parse.y | 130 +++++-- src/box/sql/pragma.c | 24 +- src/box/sql/resolve.c | 47 ++- src/box/sql/select.c | 146 +++++--- src/box/sql/sqlInt.h | 301 ++++++++++++++- src/box/sql/trigger.c | 210 +++++------ src/box/sql/util.c | 42 ++- src/box/sql/wherecode.c | 9 +- src/box/sql/whereexpr.c | 22 +- test/sql-tap/identifier_case.test.lua | 12 +- 17 files changed, 1240 insertions(+), 802 deletions(-) -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 2/7] sql: rework sql_src_list_enlarge " Kirill Shcherbatov ` (7 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_alloc_src_list routine to use diag_set in case of memory allocation error. This will ensure that the sqlSrcListAppend function throws an error using diag in subsequent patches. Needed for #3931 --- src/box/sql/build.c | 30 +++++++++++++++++------------- src/box/sql/sqlInt.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index e9851d9a1..3a65eb5a1 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -1397,8 +1397,11 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, struct sql *db = parse->db; assert(!db->mallocFailed); struct SrcList *src_list = sql_alloc_src_list(db); - if (src_list != NULL) - src_list->a[0].zName = sqlDbStrDup(db, stat_table_name); + if (src_list == NULL) { + sql_parser_error(parse); + return; + } + src_list->a[0].zName = sqlDbStrDup(db, stat_table_name); struct Expr *where = NULL; if (idx_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); @@ -2666,19 +2669,20 @@ sqlSrcListEnlarge(sql * db, /* Database connection to notify of OOM errors */ return pSrc; } -SrcList * -sql_alloc_src_list(sql *db) +struct SrcList * +sql_alloc_src_list(struct sql *db) { - SrcList *pList; - - pList = sqlDbMallocRawNN(db, sizeof(SrcList)); - if (pList == 0) + struct SrcList *src_list = sqlDbMallocRawNN(db, sizeof(SrcList)); + if (src_list == NULL) { + diag_set(OutOfMemory, sizeof(SrcList), "sqlDbMallocRawNN", + "src_list"); return NULL; - pList->nAlloc = 1; - pList->nSrc = 1; - memset(&pList->a[0], 0, sizeof(pList->a[0])); - pList->a[0].iCursor = -1; - return pList; + } + src_list->nAlloc = 1; + src_list->nSrc = 1; + memset(&src_list->a[0], 0, sizeof(src_list->a[0])); + src_list->a[0].iCursor = -1; + return src_list; } /* diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index dbfdbc6f5..ce29dc0b1 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3427,8 +3427,16 @@ void *sqlArrayAllocate(sql *, void *, int, int *, int *); IdList *sqlIdListAppend(sql *, IdList *, Token *); int sqlIdListIndex(IdList *, const char *); SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); -SrcList * -sql_alloc_src_list(sql *db); + +/** + * Allocate a new empty SrcList object. + * @param db The database connection. + * @retval not NULL list pointer on success. + * @retval NULL otherwise. + */ +struct SrcList * +sql_alloc_src_list(struct sql *db); + SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, Token *, Select *, Expr *, IdList *); -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Hi! Thanks for the patch! On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_alloc_src_list routine to use diag_set in case of > memory allocation error. This will ensure that the > sqlSrcListAppend function throws an error using diag in > subsequent patches. The issue was about normalization error, but here I do not see a word about it. Only an obfuscated necessity to refactor sqlSrcListAppend. Why do you needed it? How it makes you closer to the name normalization? I spent significant time to recover the whole tree of functions, using name normalization, in order to learn what sqlSrcListAppend has to do with it. Please, write more accurate and elaborated comments to each commit in the patchset. Also, as I see, you decided to go from the bottom and firstly refactor some helper functions, after which you fixed the functions who use normalization. But the thing is that this commit and some of the others do not work because you set diag, but diag error never reaches a user. 1) In this commit you refactored sql_alloc_str_list, its usage in sqlSrcListAppend remained to be non-tarantool. It means, that if sql_alloc_str_list fails in sqlSrcListAppend and sets a diag, it is never thrown to a user. 2) About commit 'sql: rework sql_src_list_enlarge to set diag': if sql_src_list_enlarge fails in sqlSrcListAppend or in src_list_append_unique, the diag will never be thrown. 3) About commit 'sql: refactor sql_name_from_token to set diag': if sql_name_from_token fails in sql_id_list_append or sqlSrcListIndexedBy - the diag error is lost. I do not see a way how to gracefully split the patch into multiple preparation patches, and keep diag working, so I ask you to just write a comment in the commit messages, listed above, that they are not self-sufficient. > > Needed for #3931 > --- > src/box/sql/build.c | 30 +++++++++++++++++------------- > src/box/sql/sqlInt.h | 12 ++++++++++-- > 2 files changed, 27 insertions(+), 15 deletions(-) > > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index dbfdbc6f5..ce29dc0b1 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3427,8 +3427,16 @@ void *sqlArrayAllocate(sql *, void *, int, int *, int *); > IdList *sqlIdListAppend(sql *, IdList *, Token *); > int sqlIdListIndex(IdList *, const char *); > SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); > -SrcList * > -sql_alloc_src_list(sql *db); > + > +/** > + * Allocate a new empty SrcList object. > + * @param db The database connection. > + * @retval not NULL list pointer on success. > + * @retval NULL otherwise. As I've already said - please, use capital letters at the beginning of a sentence. Also, it is worth mentioning that it sets diag, and db->mallocFailed means nothing. The same about all other refactored functions in scope of this patchset. > + */ > +struct SrcList * > +sql_alloc_src_list(struct sql *db); > + > SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); > SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, > Token *, Select *, Expr *, IdList *); > -- > 2.20.1 > > ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 0 replies; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy On 07.03.2019 20:34, Vladislav Shpilevoy wrote: > Hi! Thanks for the patch! Hi! Thank you for review. ===================================================== Refactored sqlAllocSrcList routine as sql_src_list_new and reworked it to use diag_set in case of memory allocation error. This will ensure that the sqlSrcListAppend function throws an error using diag in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its usage in sqlSrcListAppend remained to be non-Tarantool (for now). It means, that if sql_src_list_new fails in sqlSrcListAppend and sets a diag, it is never thrown to a user (now). Part of #3931 --- src/box/sql/build.c | 34 +++++++++++++++++++--------------- src/box/sql/select.c | 2 +- src/box/sql/sqlInt.h | 13 +++++++++++-- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index e9851d9a1..760054552 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -1396,9 +1396,12 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, assert(idx_name != NULL || table_name != NULL); struct sql *db = parse->db; assert(!db->mallocFailed); - struct SrcList *src_list = sql_alloc_src_list(db); - if (src_list != NULL) - src_list->a[0].zName = sqlDbStrDup(db, stat_table_name); + struct SrcList *src_list = sql_src_list_new(db); + if (src_list == NULL) { + sql_parser_error(parse); + return; + } + src_list->a[0].zName = sqlDbStrDup(db, stat_table_name); struct Expr *where = NULL; if (idx_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); @@ -2666,19 +2669,20 @@ sqlSrcListEnlarge(sql * db, /* Database connection to notify of OOM errors */ return pSrc; } -SrcList * -sql_alloc_src_list(sql *db) +struct SrcList * +sql_src_list_new(struct sql *db) { - SrcList *pList; - - pList = sqlDbMallocRawNN(db, sizeof(SrcList)); - if (pList == 0) + struct SrcList *src_list = sqlDbMallocRawNN(db, sizeof(SrcList)); + if (src_list == NULL) { + diag_set(OutOfMemory, sizeof(SrcList), "sqlDbMallocRawNN", + "src_list"); return NULL; - pList->nAlloc = 1; - pList->nSrc = 1; - memset(&pList->a[0], 0, sizeof(pList->a[0])); - pList->a[0].iCursor = -1; - return pList; + } + src_list->nAlloc = 1; + src_list->nSrc = 1; + memset(&src_list->a[0], 0, sizeof(src_list->a[0])); + src_list->a[0].iCursor = -1; + return src_list; } /* @@ -2724,7 +2728,7 @@ sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ struct SrcList_item *pItem; assert(db != 0); if (pList == 0) { - pList = sql_alloc_src_list(db); + pList = sql_src_list_new(db); if (pList == 0) return 0; } else { diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 782da1f7c..16d535076 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -315,7 +315,7 @@ sql_select_expand_from_tables(struct Select *select) { assert(select != NULL); struct Walker walker; - struct SrcList *table_names = sql_alloc_src_list(sql_get()); + struct SrcList *table_names = sql_src_list_new(sql_get()); if (table_names == NULL) return NULL; memset(&walker, 0, sizeof(walker)); diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 1d8fae5b0..4a4a748ba 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3391,8 +3391,17 @@ void *sqlArrayAllocate(sql *, void *, int, int *, int *); IdList *sqlIdListAppend(sql *, IdList *, Token *); int sqlIdListIndex(IdList *, const char *); SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); -SrcList * -sql_alloc_src_list(sql *db); + +/** + * Allocate a new empty SrcList object. + * + * @param db The database connection. + * @retval Not NULL list pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct SrcList * +sql_src_list_new(struct sql *db); + SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, Token *, Select *, Expr *, IdList *); -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 2/7] sql: rework sql_src_list_enlarge to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 3/7] sql: refactor sql_src_list_append " Kirill Shcherbatov ` (6 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_src_list_enlarge routine to use diag_set in case of memory allocation error. This will ensure that the sqlSrcListAppend function throws an error using diag in subsequent patches. Needed for #3931 --- src/box/sql/build.c | 109 ++++++++++++++++--------------------------- src/box/sql/select.c | 28 ++++++----- src/box/sql/sqlInt.h | 27 ++++++++++- 3 files changed, 83 insertions(+), 81 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 3a65eb5a1..5337df450 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2597,76 +2597,45 @@ sqlIdListIndex(IdList * pList, const char *zName) return -1; } -/* - * Expand the space allocazted for the given SrcList object by - * creating nExtra new slots beginning at iStart. iStart is zero based. - * New slots are zeroed. - * - * For example, suppose a SrcList initially contains two entries: A,B. - * To append 3 new entries onto the end, do this: - * - * sqlSrcListEnlarge(db, pSrclist, 3, 2); - * - * After the call above it would contain: A, B, nil, nil, nil. - * If the iStart argument had been 1 instead of 2, then the result - * would have been: A, nil, nil, nil, B. To prepend the new slots, - * the iStart value would be 0. The result then would - * be: nil, nil, nil, A, B. - * - * If a memory allocation fails the SrcList is unchanged. The - * db->mallocFailed flag will be set to true. - */ -SrcList * -sqlSrcListEnlarge(sql * db, /* Database connection to notify of OOM errors */ - SrcList * pSrc, /* The SrcList to be enlarged */ - int nExtra, /* Number of new slots to add to pSrc->a[] */ - int iStart /* Index in pSrc->a[] of first new slot */ - ) -{ - int i; - - /* Sanity checking on calling parameters */ - assert(iStart >= 0); - assert(nExtra >= 1); - assert(pSrc != 0); - assert(iStart <= pSrc->nSrc); - - /* Allocate additional space if needed */ - if ((u32) pSrc->nSrc + nExtra > pSrc->nAlloc) { - SrcList *pNew; - int nAlloc = pSrc->nSrc * 2 + nExtra; - int nGot; - pNew = sqlDbRealloc(db, pSrc, - sizeof(*pSrc) + (nAlloc - - 1) * - sizeof(pSrc->a[0])); - if (pNew == 0) { - assert(db->mallocFailed); - return pSrc; +struct SrcList * +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, + int start_idx) +{ + assert(start_idx >= 0); + assert(new_slots >= 1); + assert(src_list != NULL); + assert(start_idx <= src_list->nSrc); + + /* Allocate additional space if needed. */ + if (src_list->nSrc + new_slots > (int)src_list->nAlloc) { + int to_alloc = src_list->nSrc * 2 + new_slots; + int size = sizeof(*src_list) + + (to_alloc - 1) * sizeof(src_list->a[0]); + src_list = sqlDbRealloc(db, src_list, size); + if (src_list == NULL) { + diag_set(OutOfMemory, size, "sqlDbRealloc", "src_list"); + return NULL; } - pSrc = pNew; - nGot = - (sqlDbMallocSize(db, pNew) - - sizeof(*pSrc)) / sizeof(pSrc->a[0]) + 1; - pSrc->nAlloc = nGot; + src_list->nAlloc = + (sqlDbMallocSize(db, src_list) - + sizeof(*src_list)) / sizeof(src_list->a[0]) + 1;; } - /* Move existing slots that come after the newly inserted slots - * out of the way + /* + * Move existing slots that come after the newly inserted + * slots out of the way. */ - for (i = pSrc->nSrc - 1; i >= iStart; i--) { - pSrc->a[i + nExtra] = pSrc->a[i]; - } - pSrc->nSrc += nExtra; + for (int i = src_list->nSrc - 1; i >= start_idx; i--) + src_list->a[i + new_slots] = src_list->a[i]; + src_list->nSrc += new_slots; - /* Zero the newly allocated slots */ - memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0]) * nExtra); - for (i = iStart; i < iStart + nExtra; i++) { - pSrc->a[i].iCursor = -1; - } + /* Zero the newly allocated slots. */ + memset(&src_list->a[start_idx], 0, sizeof(src_list->a[0]) * new_slots); + for (int i = start_idx; i < start_idx + new_slots; i++) + src_list->a[i].iCursor = -1; - /* Return a pointer to the enlarged SrcList */ - return pSrc; + /* Return a pointer to the enlarged SrcList. */ + return src_list; } struct SrcList * @@ -2732,11 +2701,13 @@ sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ if (pList == 0) return 0; } else { - pList = sqlSrcListEnlarge(db, pList, 1, pList->nSrc); - } - if (db->mallocFailed) { - sqlSrcListDelete(db, pList); - return 0; + struct SrcList *new_list = + sql_src_list_enlarge(db, pList, 1, pList->nSrc); + if (new_list == NULL) { + sqlSrcListDelete(db, pList); + return NULL; + } + pList = new_list; } pItem = &pList->a[pList->nSrc - 1]; pItem->zName = sqlNameFromToken(db, pTable); diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 6a465a616..0baf8fb57 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -279,18 +279,22 @@ src_list_append_unique(struct sql *db, struct SrcList *list, if (name != NULL && strcmp(new_name, name) == 0) return list; } - list = sqlSrcListEnlarge(db, list, 1, list->nSrc); - if (db->mallocFailed) { - sqlSrcListDelete(db, list); - return NULL; - } + struct SrcList *new_list = + sql_src_list_enlarge(db, list, 1, list->nSrc); + if (new_list == NULL) + goto error; + list = new_list; struct SrcList_item *pItem = &list->a[list->nSrc - 1]; pItem->zName = sqlDbStrNDup(db, new_name, strlen(new_name)); if (pItem->zName == NULL) { - sqlSrcListDelete(db, list); - return NULL; + diag_set(OutOfMemory, strlen(new_name), "sqlDbStrNDup", + "pItem->zName"); + goto error; } return list; +error: + sqlSrcListDelete(db, list); + return NULL; } static int @@ -4135,12 +4139,14 @@ flattenSubquery(Parse * pParse, /* Parsing context */ * for the two elements in the FROM clause of the subquery. */ if (nSubSrc > 1) { - pParent->pSrc = pSrc = - sqlSrcListEnlarge(db, pSrc, nSubSrc - 1, - iFrom + 1); - if (db->mallocFailed) { + struct SrcList *new_list = + sql_src_list_enlarge(db, pSrc, nSubSrc - 1, + iFrom + 1); + if (new_list == NULL) { + sql_parser_error(pParse); break; } + pParent->pSrc = pSrc = new_list; } /* Transfer the FROM clause terms from the subquery into the diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index ce29dc0b1..ea6476931 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3426,7 +3426,32 @@ void sqlInsert(Parse *, SrcList *, Select *, IdList *, void *sqlArrayAllocate(sql *, void *, int, int *, int *); IdList *sqlIdListAppend(sql *, IdList *, Token *); int sqlIdListIndex(IdList *, const char *); -SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); + +/** + * Expand the space allocated for the given SrcList object by + * creating new_slots new slots beginning at start_idx. + * The start_idx is zero based. New slots are zeroed. + * + * For example, suppose a SrcList initially contains two entries: + * A,B. + * To append 3 new entries onto the end, do this: + * sql_src_list_enlarge(db, pSrclist, 3, 2); + * + * After the call above it would contain: A, B, nil, nil, nil. + * If the iStart argument had been 1 instead of 2, then the result + * would have been: A, nil, nil, nil, B. To prepend the new slots, + * the iStart value would be 0. The result then would + * be: nil, nil, nil, A, B. + * @param db The database connection. + * @param src_list The SrcList to be enlarged. + * @param new_slots Number of new slots to add to src_list->a[]. + * @param start_idx Index in pSrc->a[] of first new slot. + * @retval not NULL SrcList pointer on success. + * @retval NULL otherwise. + */ +struct SrcList * +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, + int start_idx); /** * Allocate a new empty SrcList object. -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 2/7] sql: rework sql_src_list_enlarge to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 2/7] sql: rework sql_src_list_enlarge " Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov In the commit header you said: "sql: rework sql_src_list_enlarge to set diag" But before your patch symbol 'sql_src_list_enlarge' does not exist. Please, fix that, and in other commits, who change function names, too. On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_src_list_enlarge routine to use diag_set in case You've refactored sqlSrcListEnlarge, not sql_src_list_enlarge. > of memory allocation error. This will ensure that the > sqlSrcListAppend function throws an error using diag in > subsequent patches. > > Needed for #3931 See 4 comments below. > --- > src/box/sql/build.c | 109 ++++++++++++++++--------------------------- > src/box/sql/select.c | 28 ++++++----- > src/box/sql/sqlInt.h | 27 ++++++++++- > 3 files changed, 83 insertions(+), 81 deletions(-) > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 3a65eb5a1..5337df450 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2597,76 +2597,45 @@ sqlIdListIndex(IdList * pList, const char *zName) > +struct SrcList * > +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, > + int start_idx) > +{ > + assert(start_idx >= 0); > + assert(new_slots >= 1); > + assert(src_list != NULL); > + assert(start_idx <= src_list->nSrc); > + > + /* Allocate additional space if needed. */ > + if (src_list->nSrc + new_slots > (int)src_list->nAlloc) { > + int to_alloc = src_list->nSrc * 2 + new_slots; > + int size = sizeof(*src_list) + > + (to_alloc - 1) * sizeof(src_list->a[0]); > + src_list = sqlDbRealloc(db, src_list, size); > + if (src_list == NULL) { > + diag_set(OutOfMemory, size, "sqlDbRealloc", "src_list"); > + return NULL; > } > - pSrc = pNew; > - nGot = > - (sqlDbMallocSize(db, pNew) - > - sizeof(*pSrc)) / sizeof(pSrc->a[0]) + 1; > - pSrc->nAlloc = nGot; > + src_list->nAlloc = > + (sqlDbMallocSize(db, src_list) - > + sizeof(*src_list)) / sizeof(src_list->a[0]) + 1;; 1. Double semicolon. 2. Not sure if it is worth using sqlDbMallocSize() here. Looks like double reserve. Firstly, we already increased size twice + new_slots (look at to_alloc). Secondly, we hope, that sqlDbRealloc will probably realloc more than necessary, but as I see, sqlDbRealloc will reserve at most +15 bytes. Please, just use src_list->nAlloc = to_alloc. > } > > - /* Move existing slots that come after the newly inserted slots > - * out of the way > + /* > + * Move existing slots that come after the newly inserted > + * slots out of the way. > */ > - for (i = pSrc->nSrc - 1; i >= iStart; i--) { > - pSrc->a[i + nExtra] = pSrc->a[i]; > - } > - pSrc->nSrc += nExtra; > + for (int i = src_list->nSrc - 1; i >= start_idx; i--) > + src_list->a[i + new_slots] = src_list->a[i]; 3. It is just memmove. Please use memmove() for that. > + src_list->nSrc += new_slots; > > - /* Zero the newly allocated slots */ > - memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0]) * nExtra); > - for (i = iStart; i < iStart + nExtra; i++) { > - pSrc->a[i].iCursor = -1; > - } > + /* Zero the newly allocated slots. */ > + memset(&src_list->a[start_idx], 0, sizeof(src_list->a[0]) * new_slots); > + for (int i = start_idx; i < start_idx + new_slots; i++) > + src_list->a[i].iCursor = -1; > > - /* Return a pointer to the enlarged SrcList */ > - return pSrc; > + /* Return a pointer to the enlarged SrcList. */ > + return src_list; > } > > struct SrcList * > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index ce29dc0b1..ea6476931 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3426,7 +3426,32 @@ void sqlInsert(Parse *, SrcList *, Select *, IdList *, > void *sqlArrayAllocate(sql *, void *, int, int *, int *); > IdList *sqlIdListAppend(sql *, IdList *, Token *); > int sqlIdListIndex(IdList *, const char *); > -SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); > + > +/** > + * Expand the space allocated for the given SrcList object by > + * creating new_slots new slots beginning at start_idx. > + * The start_idx is zero based. New slots are zeroed. > + * > + * For example, suppose a SrcList initially contains two entries: > + * A,B. > + * To append 3 new entries onto the end, do this: > + * sql_src_list_enlarge(db, pSrclist, 3, 2); > + * > + * After the call above it would contain: A, B, nil, nil, nil. > + * If the iStart argument had been 1 instead of 2, then the result 4.1. You have no iStart. > + * would have been: A, nil, nil, nil, B. To prepend the new slots, > + * the iStart value would be 0. The result then would 4.2. The same. > + * be: nil, nil, nil, A, B. > + * @param db The database connection. > + * @param src_list The SrcList to be enlarged. > + * @param new_slots Number of new slots to add to src_list->a[]. > + * @param start_idx Index in pSrc->a[] of first new slot. 4.3. pSrc does not exist. > + * @retval not NULL SrcList pointer on success. > + * @retval NULL otherwise. 4.4. Please, start sentences with capital letters. > + */ > +struct SrcList * > +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, > + int start_idx); > > /** > * Allocate a new empty SrcList object. > -- > 2.20.1 > > ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 2/7] sql: rework sql_src_list_enlarge to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 0 replies; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > You've refactored sqlSrcListEnlarge, not sql_src_list_enlarge. I've reword all commit messages including this one. > 1. Double semicolon. Fixed > 2. Not sure if it is worth using sqlDbMallocSize() here. Looks like > double reserve. Firstly, we already increased size twice + new_slots > (look at to_alloc). Secondly, we hope, that sqlDbRealloc will probably > realloc more than necessary, but as I see, sqlDbRealloc will reserve > at most +15 bytes. Please, just use src_list->nAlloc = to_alloc. - nGot = - (sqlDbMallocSize(db, pNew) - - sizeof(*pSrc)) / sizeof(pSrc->a[0]) + 1; - pSrc->nAlloc = nGot; + src_list->nAlloc = to_alloc; > 3. It is just memmove. Please use memmove() for that. - for (i = pSrc->nSrc - 1; i >= iStart; i--) { - pSrc->a[i + nExtra] = pSrc->a[i]; - } - pSrc->nSrc += nExtra; + memmove(&src_list->a[start_idx + new_slots], &src_list->a[start_idx], + (src_list->nSrc - start_idx) * sizeof(src_list->a[0])); + src_list->nSrc += new_slots; > 4.1. You have no iStart. > 4.2. The same. > 4.3. pSrc does not exist. > 4.4. Please, start sentences with capital letters. Hope I've fixed them all. ===================================================== Refactored sqlSrcListEnlarge routine as sql_src_list_enlarge and reworked to use diag_set in case of memory allocation error. This will ensure that the sqlSrcListAppend function throws an error using diag in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its usage in sqlSrcListAppend remained to be non-Tarantool (for now). It means, that if sql_src_list_enlarge fails in sqlSrcListAppend the diag will never be thrown. Part of #3931 --- src/box/sql/build.c | 107 +++++++++++++++---------------------------- src/box/sql/select.c | 18 +++++--- src/box/sql/sqlInt.h | 28 ++++++++++- 3 files changed, 77 insertions(+), 76 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 760054552..f53b2561c 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2597,76 +2597,43 @@ sqlIdListIndex(IdList * pList, const char *zName) return -1; } -/* - * Expand the space allocazted for the given SrcList object by - * creating nExtra new slots beginning at iStart. iStart is zero based. - * New slots are zeroed. - * - * For example, suppose a SrcList initially contains two entries: A,B. - * To append 3 new entries onto the end, do this: - * - * sqlSrcListEnlarge(db, pSrclist, 3, 2); - * - * After the call above it would contain: A, B, nil, nil, nil. - * If the iStart argument had been 1 instead of 2, then the result - * would have been: A, nil, nil, nil, B. To prepend the new slots, - * the iStart value would be 0. The result then would - * be: nil, nil, nil, A, B. - * - * If a memory allocation fails the SrcList is unchanged. The - * db->mallocFailed flag will be set to true. - */ -SrcList * -sqlSrcListEnlarge(sql * db, /* Database connection to notify of OOM errors */ - SrcList * pSrc, /* The SrcList to be enlarged */ - int nExtra, /* Number of new slots to add to pSrc->a[] */ - int iStart /* Index in pSrc->a[] of first new slot */ - ) -{ - int i; - - /* Sanity checking on calling parameters */ - assert(iStart >= 0); - assert(nExtra >= 1); - assert(pSrc != 0); - assert(iStart <= pSrc->nSrc); - - /* Allocate additional space if needed */ - if ((u32) pSrc->nSrc + nExtra > pSrc->nAlloc) { - SrcList *pNew; - int nAlloc = pSrc->nSrc * 2 + nExtra; - int nGot; - pNew = sqlDbRealloc(db, pSrc, - sizeof(*pSrc) + (nAlloc - - 1) * - sizeof(pSrc->a[0])); - if (pNew == 0) { - assert(db->mallocFailed); - return pSrc; +struct SrcList * +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, + int start_idx) +{ + assert(start_idx >= 0); + assert(new_slots >= 1); + assert(src_list != NULL); + assert(start_idx <= src_list->nSrc); + + /* Allocate additional space if needed. */ + if (src_list->nSrc + new_slots > (int)src_list->nAlloc) { + int to_alloc = src_list->nSrc * 2 + new_slots; + int size = sizeof(*src_list) + + (to_alloc - 1) * sizeof(src_list->a[0]); + src_list = sqlDbRealloc(db, src_list, size); + if (src_list == NULL) { + diag_set(OutOfMemory, size, "sqlDbRealloc", "src_list"); + return NULL; } - pSrc = pNew; - nGot = - (sqlDbMallocSize(db, pNew) - - sizeof(*pSrc)) / sizeof(pSrc->a[0]) + 1; - pSrc->nAlloc = nGot; + src_list->nAlloc = to_alloc; } - /* Move existing slots that come after the newly inserted slots - * out of the way + /* + * Move existing slots that come after the newly inserted + * slots out of the way. */ - for (i = pSrc->nSrc - 1; i >= iStart; i--) { - pSrc->a[i + nExtra] = pSrc->a[i]; - } - pSrc->nSrc += nExtra; + memmove(&src_list->a[start_idx + new_slots], &src_list->a[start_idx], + (src_list->nSrc - start_idx) * sizeof(src_list->a[0])); + src_list->nSrc += new_slots; - /* Zero the newly allocated slots */ - memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0]) * nExtra); - for (i = iStart; i < iStart + nExtra; i++) { - pSrc->a[i].iCursor = -1; - } + /* Zero the newly allocated slots. */ + memset(&src_list->a[start_idx], 0, sizeof(src_list->a[0]) * new_slots); + for (int i = start_idx; i < start_idx + new_slots; i++) + src_list->a[i].iCursor = -1; - /* Return a pointer to the enlarged SrcList */ - return pSrc; + /* Return a pointer to the enlarged SrcList. */ + return src_list; } struct SrcList * @@ -2732,11 +2699,13 @@ sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ if (pList == 0) return 0; } else { - pList = sqlSrcListEnlarge(db, pList, 1, pList->nSrc); - } - if (db->mallocFailed) { - sqlSrcListDelete(db, pList); - return 0; + struct SrcList *new_list = + sql_src_list_enlarge(db, pList, 1, pList->nSrc); + if (new_list == NULL) { + sqlSrcListDelete(db, pList); + return NULL; + } + pList = new_list; } pItem = &pList->a[pList->nSrc - 1]; pItem->zName = sqlNameFromToken(db, pTable); diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 16d535076..840bca556 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -279,14 +279,18 @@ src_list_append_unique(struct sql *db, struct SrcList *list, if (name != NULL && strcmp(new_name, name) == 0) return list; } - list = sqlSrcListEnlarge(db, list, 1, list->nSrc); - if (db->mallocFailed) { + struct SrcList *new_list = + sql_src_list_enlarge(db, list, 1, list->nSrc); + if (new_list == NULL) { sqlSrcListDelete(db, list); return NULL; } + list = new_list; struct SrcList_item *pItem = &list->a[list->nSrc - 1]; pItem->zName = sqlDbStrNDup(db, new_name, strlen(new_name)); if (pItem->zName == NULL) { + diag_set(OutOfMemory, strlen(new_name), "sqlDbStrNDup", + "pItem->zName"); sqlSrcListDelete(db, list); return NULL; } @@ -4135,12 +4139,14 @@ flattenSubquery(Parse * pParse, /* Parsing context */ * for the two elements in the FROM clause of the subquery. */ if (nSubSrc > 1) { - pParent->pSrc = pSrc = - sqlSrcListEnlarge(db, pSrc, nSubSrc - 1, - iFrom + 1); - if (db->mallocFailed) { + struct SrcList *new_list = + sql_src_list_enlarge(db, pSrc, nSubSrc - 1, + iFrom + 1); + if (new_list == NULL) { + sql_parser_error(pParse); break; } + pParent->pSrc = pSrc = new_list; } /* Transfer the FROM clause terms from the subquery into the diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 4a4a748ba..0895f7de1 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3390,7 +3390,33 @@ void sqlInsert(Parse *, SrcList *, Select *, IdList *, void *sqlArrayAllocate(sql *, void *, int, int *, int *); IdList *sqlIdListAppend(sql *, IdList *, Token *); int sqlIdListIndex(IdList *, const char *); -SrcList *sqlSrcListEnlarge(sql *, SrcList *, int, int); + +/** + * Expand the space allocated for the given SrcList object by + * creating new_slots new slots beginning at start_idx. + * The start_idx is zero based. New slots are zeroed. + * + * For example, suppose a SrcList initially contains two entries: + * A,B. + * To append 3 new entries onto the end, do this: + * sql_src_list_enlarge(db, src_list, 3, 2); + * + * After the call above it would contain: A, B, nil, nil, nil. + * If the start_idx argument had been 1 instead of 2, then the + * result would have been: A, nil, nil, nil, B. To prepend the + * new slots, the start_idx value would be 0. The result then + * would be: nil, nil, nil, A, B. + * + * @param db The database connection. + * @param src_list The SrcList to be enlarged. + * @param new_slots Number of new slots to add to src_list->a[]. + * @param start_idx Index in src_list->a[] of first new slot. + * @retval Not NULL SrcList pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct SrcList * +sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, + int start_idx); /** * Allocate a new empty SrcList object. -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 2/7] sql: rework sql_src_list_enlarge " Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 4/7] sql: refactor sql_name_from_token " Kirill Shcherbatov ` (5 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_src_list_append routine to use diag_set in case of memory allocation error. This change is necessary because the sql_src_list_append body has a sqlNameFromToken call that will be changed in subsequent patches. Needed for #3931 --- src/box/sql/build.c | 83 +++++++++++-------------------------- src/box/sql/delete.c | 14 ++++--- src/box/sql/fk_constraint.c | 17 ++++---- src/box/sql/parse.y | 24 ++++++++--- src/box/sql/select.c | 8 ++-- src/box/sql/sqlInt.h | 48 ++++++++++++++++++++- src/box/sql/trigger.c | 11 ++--- 7 files changed, 119 insertions(+), 86 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 5337df450..4fe838608 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2654,64 +2654,28 @@ sql_alloc_src_list(struct sql *db) return src_list; } -/* - * Append a new table name to the given SrcList. Create a new SrcList if - * need be. A new entry is created in the SrcList even if pTable is NULL. - * - * A SrcList is returned, or NULL if there is an OOM error. The returned - * SrcList might be the same as the SrcList that was input or it might be - * a new one. If an OOM error does occurs, then the prior value of pList - * that is input to this routine is automatically freed. - * - * If pDatabase is not null, it means that the table has an optional - * database name prefix. Like this: "database.table". The pDatabase - * points to the table name and the pTable points to the database name. - * The SrcList.a[].zName field is filled with the table name which might - * come from pTable (if pDatabase is NULL) or from pDatabase. - * SrcList.a[].zDatabase is filled with the database name from pTable, - * or with NULL if no database is specified. - * - * In other words, if call like this: - * - * sqlSrcListAppend(D,A,B,0); - * - * Then B is a table name and the database name is unspecified. If called - * like this: - * - * sqlSrcListAppend(D,A,B,C); - * - * Then C is the table name and B is the database name. If C is defined - * then so is B. In other words, we never have a case where: - * - * sqlSrcListAppend(D,A,0,C); - * - * Both pTable and pDatabase are assumed to be quoted. They are dequoted - * before being added to the SrcList. - */ -SrcList * -sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ - SrcList * pList, /* Append to this SrcList. NULL creates a new SrcList */ - Token * pTable /* Table to append */ - ) +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token) { struct SrcList_item *pItem; - assert(db != 0); - if (pList == 0) { - pList = sql_alloc_src_list(db); - if (pList == 0) - return 0; + if (list == NULL) { + list = sql_alloc_src_list(db); + if (list == NULL) + return NULL; } else { struct SrcList *new_list = - sql_src_list_enlarge(db, pList, 1, pList->nSrc); - if (new_list == NULL) { - sqlSrcListDelete(db, pList); - return NULL; - } - pList = new_list; - } - pItem = &pList->a[pList->nSrc - 1]; - pItem->zName = sqlNameFromToken(db, pTable); - return pList; + sql_src_list_enlarge(db, list, 1, list->nSrc); + if (new_list == NULL) + goto error; + list = new_list; + } + pItem = &list->a[list->nSrc - 1]; + pItem->zName = sqlNameFromToken(db, name_token); + return list; +error: + sqlSrcListDelete(db, list); + return NULL; } /* @@ -2803,10 +2767,10 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ ); goto append_from_error; } - p = sqlSrcListAppend(db, p, pTable); - if (p == 0 || NEVER(p->nSrc == 0)) { - goto append_from_error; - } + p = sql_src_list_append(db, p, pTable); + if (p == NULL) + goto tnt_error; + assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); if (pAlias->n) { @@ -2823,6 +2787,9 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ sqlIdListDelete(db, pUsing); sql_select_delete(db, pSubquery); return 0; +tnt_error: + sql_parser_error(pParse); + goto append_from_error; } /* diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 5170c7f59..26023554d 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -66,13 +66,15 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where, { struct sql *db = parse->db; where = sqlExprDup(db, where, 0); - struct SrcList *from = sqlSrcListAppend(db, NULL, NULL); - if (from != NULL) { - assert(from->nSrc == 1); - from->a[0].zName = sqlDbStrDup(db, name); - assert(from->a[0].pOn == NULL); - assert(from->a[0].pUsing == NULL); + struct SrcList *from = sql_src_list_append(db, NULL, NULL); + if (from == NULL) { + sql_parser_error(parse); + return; } + assert(from->nSrc == 1); + from->a[0].zName = sqlDbStrDup(db, name); + assert(from->a[0].pOn == NULL); + assert(from->a[0].pUsing == NULL); struct Select *select = sqlSelectNew(parse, NULL, from, where, NULL, NULL, NULL, 0, NULL, NULL); struct SelectDest dest; diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index 93c01a0e7..4f761d610 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -618,9 +618,11 @@ fk_constraint_emit_check(struct Parse *parser, struct space *space, int reg_old, * table. We need the child table as a SrcList for * sqlWhereBegin(). */ - struct SrcList *src = sqlSrcListAppend(db, NULL, NULL); - if (src == NULL) + struct SrcList *src = sql_src_list_append(db, NULL, NULL); + if (src == NULL) { + sql_parser_error(parser); continue; + } struct SrcList_item *item = src->a; struct space *child = space_by_id(fk->def->child_id); assert(child != NULL); @@ -866,11 +868,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, "constraint failed"); if (r != NULL) r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; - select = sqlSelectNew(pParse, - sql_expr_list_append(db, NULL, r), - sqlSrcListAppend(db, NULL, &err), - where, NULL, NULL, NULL, 0, NULL, - NULL); + struct SrcList *src_list = sql_src_list_append(db, NULL, &err); + if (src_list == NULL) + sql_parser_error(pParse); + select = sqlSelectNew(pParse, sql_expr_list_append(db, NULL, r), + src_list, where, NULL, NULL, NULL, 0, + NULL, NULL); where = NULL; } diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 32db6850d..4ae9eff83 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -609,8 +609,14 @@ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP %type fullname {SrcList*} %destructor fullname {sqlSrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X). - {A = sqlSrcListAppend(pParse->db,0,&X); /*A-overwrites-X*/} +fullname(A) ::= nm(X). { + /*A-overwrites-X. */ + A = sql_src_list_append(pParse->db,0,&X); + if (A == NULL) { + sql_parser_error(pParse); + return; + } +} %type joinop {int} join_nm(A) ::= id(A). @@ -1146,7 +1152,11 @@ expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] { A.zEnd = &E.z[E.n]; } expr(A) ::= expr(A) in_op(N) nm(Y) paren_exprlist(E). [IN] { - SrcList *pSrc = sqlSrcListAppend(pParse->db, 0,&Y); + struct SrcList *pSrc = sql_src_list_append(pParse->db, 0,&Y); + if (pSrc == NULL) { + sql_parser_error(pParse); + return; + } Select *pSelect = sqlSelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); if( E ) sqlSrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A.pExpr = sqlPExpr(pParse, TK_IN, A.pExpr, 0); @@ -1216,8 +1226,12 @@ paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;} // cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) ON nm(Y) LP sortlist(Z) RP. { - sql_create_index(pParse, &X, sqlSrcListAppend(pParse->db,0,&Y), Z, &S, - SORT_ORDER_ASC, NE, U); + struct SrcList *src_list = sql_src_list_append(pParse->db,0,&Y); + if (src_list == NULL) { + sql_parser_error(pParse); + return; + } + sql_create_index(pParse, &X, src_list, Z, &S, SORT_ORDER_ASC, NE, U); } %type uniqueflag {int} diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 0baf8fb57..7eea45dd7 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -256,7 +256,7 @@ findRightmost(Select * p) /** - * Work the same as sqlSrcListAppend(), but before adding to + * Work the same as sql_src_list_append(), but before adding to * list provide check on name duplicates: only values with unique * names are appended. Moreover, names of tables are not * normalized: it is parser's business and in struct Select they @@ -4116,9 +4116,9 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } else { assert(pParent != p); /* 2nd and subsequent times through the loop */ pSrc = pParent->pSrc = - sqlSrcListAppend(db, 0, 0); - if (pSrc == 0) { - assert(db->mallocFailed); + sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + sql_parser_error(pParse); break; } } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index ea6476931..eae2ec8e8 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3462,7 +3462,53 @@ sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, struct SrcList * sql_alloc_src_list(struct sql *db); -SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); +/** + * Append a new table name to the given list. Create a new + * SrcList if need be. A new entry is created in the list even + * if name_token is NULL. + * + * A SrcList is returned, or NULL if there is an OOM error. + * The returned SrcList might be the same as the SrcList that was + * input or it might be a new one. If an OOM error does occurs, + * then the prior value of list that is input to this routine is + * automatically freed. + * + * If pDatabase is not null, it means that the table has an + * optional database name prefix. Like this: "database.table". + * The pDatabase points to the table name and the pTable points + * to the database name. The SrcList.a[].zName field is filled + * with the table name which might come from pTable (if pDatabase + * is NULL) or from pDatabase. + * SrcList.a[].zDatabase is filled with the database name from + * name_token, or with NULL if no database is specified. + * + * In other words, if call like this: + * + * sql_src_list_append(D,A,B,0); + * + * Then B is a table name and the database name is unspecified. + * If called like this: + * + * sql_src_list_append(D,A,B,C); + * + * Then C is the table name and B is the database name. If C is + * defined then so is B. In other words, we never have a case + * where: + * + * sql_src_list_append(D,A,0,C); + * + * Both pTable and pDatabase are assumed to be quoted. They are + * dequoted before being added to the SrcList. + * @param db The database connection. + * @param list Append to this SrcList. NULL creates a new SrcList. + * @param name_token Token representing table name. + * @retval not NULL SrcList pointer on success. + * @retval NULL otherwise. + */ +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token); + SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, Token *, Select *, Expr *, IdList *); void sqlSrcListIndexedBy(Parse *, SrcList *, Token *); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index f7e6189de..20d41e6a1 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -588,12 +588,13 @@ targetSrcList(Parse * pParse, /* The parsing context */ sql *db = pParse->db; SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlSrcListAppend(db, 0, 0); - if (pSrc) { - assert(pSrc->nSrc > 0); - pSrc->a[pSrc->nSrc - 1].zName = - sqlDbStrDup(db, pStep->zTarget); + pSrc = sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + sql_parser_error(pParse); + return NULL; } + assert(pSrc->nSrc > 0); + pSrc->a[pSrc->nSrc - 1].zName = sqlDbStrDup(db, pStep->zTarget); return pSrc; } -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 3/7] sql: refactor sql_src_list_append " Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov See 4 comments below. On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_src_list_append routine to use diag_set in case > of memory allocation error. > This change is necessary because the sql_src_list_append body has > a sqlNameFromToken call that will be changed in subsequent > patches. > > Needed for #3931 > --- > src/box/sql/build.c | 83 +++++++++++-------------------------- > src/box/sql/delete.c | 14 ++++--- > src/box/sql/fk_constraint.c | 17 ++++---- > src/box/sql/parse.y | 24 ++++++++--- > src/box/sql/select.c | 8 ++-- > src/box/sql/sqlInt.h | 48 ++++++++++++++++++++- > src/box/sql/trigger.c | 11 ++--- > 7 files changed, 119 insertions(+), 86 deletions(-) > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 5337df450..4fe838608 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2654,64 +2654,28 @@ sql_alloc_src_list(struct sql *db) > return src_list; > } > > -/* > - * Append a new table name to the given SrcList. Create a new SrcList if > - * need be. A new entry is created in the SrcList even if pTable is NULL. > - * > - * A SrcList is returned, or NULL if there is an OOM error. The returned > - * SrcList might be the same as the SrcList that was input or it might be > - * a new one. If an OOM error does occurs, then the prior value of pList > - * that is input to this routine is automatically freed. > - * > - * If pDatabase is not null, it means that the table has an optional > - * database name prefix. Like this: "database.table". The pDatabase > - * points to the table name and the pTable points to the database name. > - * The SrcList.a[].zName field is filled with the table name which might > - * come from pTable (if pDatabase is NULL) or from pDatabase. > - * SrcList.a[].zDatabase is filled with the database name from pTable, > - * or with NULL if no database is specified. > - * > - * In other words, if call like this: > - * > - * sqlSrcListAppend(D,A,B,0); > - * > - * Then B is a table name and the database name is unspecified. If called > - * like this: > - * > - * sqlSrcListAppend(D,A,B,C); > - * > - * Then C is the table name and B is the database name. If C is defined > - * then so is B. In other words, we never have a case where: > - * > - * sqlSrcListAppend(D,A,0,C); > - * > - * Both pTable and pDatabase are assumed to be quoted. They are dequoted > - * before being added to the SrcList. > - */ > -SrcList * > -sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ > - SrcList * pList, /* Append to this SrcList. NULL creates a new SrcList */ > - Token * pTable /* Table to append */ > - ) > +struct SrcList * > +sql_src_list_append(struct sql *db, struct SrcList *list, > + struct Token *name_token) > { > struct SrcList_item *pItem; > - assert(db != 0); > - if (pList == 0) { > - pList = sql_alloc_src_list(db); > - if (pList == 0) > - return 0; > + if (list == NULL) { > + list = sql_alloc_src_list(db); > + if (list == NULL) > + return NULL; > } else { > struct SrcList *new_list = > - sql_src_list_enlarge(db, pList, 1, pList->nSrc); > - if (new_list == NULL) { > - sqlSrcListDelete(db, pList); > - return NULL; > - } > - pList = new_list; > - } > - pItem = &pList->a[pList->nSrc - 1]; > - pItem->zName = sqlNameFromToken(db, pTable); > - return pList; > + sql_src_list_enlarge(db, list, 1, list->nSrc); > + if (new_list == NULL) > + goto error; 1. When you have a single goto error, usually you can just inline it. Besides, you do not goto error above on sql_alloc_src_list() fail. I understand why, but then it is not a common error. In other words, just inline error processing here. > + list = new_list; > + } > + pItem = &list->a[list->nSrc - 1]; > + pItem->zName = sqlNameFromToken(db, name_token); 2. sqlNameFromToken can fail even now, before next patches, and here it does not set diag, as I see, but the commit message says, that sql_src_list_append is now on diag. > + return list; > +error: > + sqlSrcListDelete(db, list); > + return NULL; > } > > /* > @@ -2803,10 +2767,10 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ > ); > goto append_from_error; > } > - p = sqlSrcListAppend(db, p, pTable); > - if (p == 0 || NEVER(p->nSrc == 0)) { > - goto append_from_error; > - } > + p = sql_src_list_append(db, p, pTable); > + if (p == NULL) > + goto tnt_error; 3. The same as 1. > + assert(p->nSrc != 0); > pItem = &p->a[p->nSrc - 1]; > assert(pAlias != 0); > if (pAlias->n) { > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index ea6476931..eae2ec8e8 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3462,7 +3462,53 @@ sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, > struct SrcList * > sql_alloc_src_list(struct sql *db); > > -SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); > +/** > + * Append a new table name to the given list. Create a new > + * SrcList if need be. A new entry is created in the list even > + * if name_token is NULL. > + * > + * A SrcList is returned, or NULL if there is an OOM error. > + * The returned SrcList might be the same as the SrcList that was > + * input or it might be a new one. If an OOM error does occurs, > + * then the prior value of list that is input to this routine is > + * automatically freed. > + * > + * If pDatabase is not null, it means that the table has an > + * optional database name prefix. Like this: "database.table". > + * The pDatabase points to the table name and the pTable points > + * to the database name. The SrcList.a[].zName field is filled > + * with the table name which might come from pTable (if pDatabase > + * is NULL) or from pDatabase. > + * SrcList.a[].zDatabase is filled with the database name from > + * name_token, or with NULL if no database is specified. > + * > + * In other words, if call like this: > + * > + * sql_src_list_append(D,A,B,0); > + * > + * Then B is a table name and the database name is unspecified. > + * If called like this: > + * > + * sql_src_list_append(D,A,B,C); > + * > + * Then C is the table name and B is the database name. If C is > + * defined then so is B. In other words, we never have a case > + * where: > + * > + * sql_src_list_append(D,A,0,C); > + * > + * Both pTable and pDatabase are assumed to be quoted. They are > + * dequoted before being added to the SrcList. > + * @param db The database connection. > + * @param list Append to this SrcList. NULL creates a new SrcList. > + * @param name_token Token representing table name. > + * @retval not NULL SrcList pointer on success. > + * @retval NULL otherwise. 4.1 The comment is obsolete in terms of variable names. Please, update it. You have no parameters 'pDatabase', 'pTable'. 4.2. Sentences and capital letters. > + */ > +struct SrcList * > +sql_src_list_append(struct sql *db, struct SrcList *list, > + struct Token *name_token); > + > SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, > Token *, Select *, Expr *, IdList *); > void sqlSrcListIndexedBy(Parse *, SrcList *, Token *); ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > 1. When you have a single goto error, usually you can > just inline it. Besides, you do not goto error above on > sql_alloc_src_list() fail. I understand why, but then it > is not a common error. > > In other words, just inline error processing here. Done. > 2. sqlNameFromToken can fail even now, before next patches, > and here it does not set diag, as I see, but the commit > message says, that sql_src_list_append is now on diag. I've described this in the commit message. > 3. The same as 1. Done. > 4.1 The comment is obsolete in terms of variable names. Please, > update it. You have no parameters 'pDatabase', 'pTable'. > 4.2. Sentences and capital letters. Done. ====================================================== Refactored sqlSrcListAppend routine as sql_src_list_append and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_src_list_append body has a sqlNameFromToken call that will be changed in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its sqlNameFromToken call remained to be non-Tarantool (for now). It means, that if sqlNameFromToken fails in sql_src_list_append there is no diag message created. Part of #3931 --- src/box/sql/build.c | 71 ++++++++++--------------------------- src/box/sql/delete.c | 14 ++++---- src/box/sql/fk_constraint.c | 17 +++++---- src/box/sql/parse.y | 24 ++++++++++--- src/box/sql/select.c | 8 ++--- src/box/sql/sqlInt.h | 22 +++++++++++- src/box/sql/trigger.c | 11 +++--- 7 files changed, 86 insertions(+), 81 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index f53b2561c..04f65f165 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2652,64 +2652,26 @@ sql_src_list_new(struct sql *db) return src_list; } -/* - * Append a new table name to the given SrcList. Create a new SrcList if - * need be. A new entry is created in the SrcList even if pTable is NULL. - * - * A SrcList is returned, or NULL if there is an OOM error. The returned - * SrcList might be the same as the SrcList that was input or it might be - * a new one. If an OOM error does occurs, then the prior value of pList - * that is input to this routine is automatically freed. - * - * If pDatabase is not null, it means that the table has an optional - * database name prefix. Like this: "database.table". The pDatabase - * points to the table name and the pTable points to the database name. - * The SrcList.a[].zName field is filled with the table name which might - * come from pTable (if pDatabase is NULL) or from pDatabase. - * SrcList.a[].zDatabase is filled with the database name from pTable, - * or with NULL if no database is specified. - * - * In other words, if call like this: - * - * sqlSrcListAppend(D,A,B,0); - * - * Then B is a table name and the database name is unspecified. If called - * like this: - * - * sqlSrcListAppend(D,A,B,C); - * - * Then C is the table name and B is the database name. If C is defined - * then so is B. In other words, we never have a case where: - * - * sqlSrcListAppend(D,A,0,C); - * - * Both pTable and pDatabase are assumed to be quoted. They are dequoted - * before being added to the SrcList. - */ -SrcList * -sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ - SrcList * pList, /* Append to this SrcList. NULL creates a new SrcList */ - Token * pTable /* Table to append */ - ) +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token) { - struct SrcList_item *pItem; - assert(db != 0); - if (pList == 0) { - pList = sql_src_list_new(db); - if (pList == 0) - return 0; + if (list == NULL) { + list = sql_src_list_new(db); + if (list == NULL) + return NULL; } else { struct SrcList *new_list = - sql_src_list_enlarge(db, pList, 1, pList->nSrc); + sql_src_list_enlarge(db, list, 1, list->nSrc); if (new_list == NULL) { - sqlSrcListDelete(db, pList); + sqlSrcListDelete(db, list); return NULL; } - pList = new_list; + list = new_list; } - pItem = &pList->a[pList->nSrc - 1]; - pItem->zName = sqlNameFromToken(db, pTable); - return pList; + struct SrcList_item *item = &list->a[list->nSrc - 1]; + item->zName = sqlNameFromToken(db, name_token); + return list; } /* @@ -2801,10 +2763,12 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ ); goto append_from_error; } - p = sqlSrcListAppend(db, p, pTable); - if (p == 0 || NEVER(p->nSrc == 0)) { + p = sql_src_list_append(db, p, pTable); + if (p == NULL) { + sql_parser_error(pParse); goto append_from_error; } + assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); if (pAlias->n) { @@ -2821,6 +2785,7 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ sqlIdListDelete(db, pUsing); sql_select_delete(db, pSubquery); return 0; + } /* diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 5170c7f59..26023554d 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -66,13 +66,15 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where, { struct sql *db = parse->db; where = sqlExprDup(db, where, 0); - struct SrcList *from = sqlSrcListAppend(db, NULL, NULL); - if (from != NULL) { - assert(from->nSrc == 1); - from->a[0].zName = sqlDbStrDup(db, name); - assert(from->a[0].pOn == NULL); - assert(from->a[0].pUsing == NULL); + struct SrcList *from = sql_src_list_append(db, NULL, NULL); + if (from == NULL) { + sql_parser_error(parse); + return; } + assert(from->nSrc == 1); + from->a[0].zName = sqlDbStrDup(db, name); + assert(from->a[0].pOn == NULL); + assert(from->a[0].pUsing == NULL); struct Select *select = sqlSelectNew(parse, NULL, from, where, NULL, NULL, NULL, 0, NULL, NULL); struct SelectDest dest; diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index 4066b1cf1..d1eb6670a 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -618,9 +618,11 @@ fk_constraint_emit_check(struct Parse *parser, struct space *space, int reg_old, * table. We need the child table as a SrcList for * sqlWhereBegin(). */ - struct SrcList *src = sqlSrcListAppend(db, NULL, NULL); - if (src == NULL) + struct SrcList *src = sql_src_list_append(db, NULL, NULL); + if (src == NULL) { + sql_parser_error(parser); continue; + } struct SrcList_item *item = src->a; struct space *child = space_by_id(fk->def->child_id); assert(child != NULL); @@ -866,11 +868,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, "constraint failed"); if (r != NULL) r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; - select = sqlSelectNew(pParse, - sql_expr_list_append(db, NULL, r), - sqlSrcListAppend(db, NULL, &err), - where, NULL, NULL, NULL, 0, NULL, - NULL); + struct SrcList *src_list = sql_src_list_append(db, NULL, &err); + if (src_list == NULL) + sql_parser_error(pParse); + select = sqlSelectNew(pParse, sql_expr_list_append(db, NULL, r), + src_list, where, NULL, NULL, NULL, 0, + NULL, NULL); where = NULL; } diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 996f55d37..122fda2f0 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -609,8 +609,14 @@ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP %type fullname {SrcList*} %destructor fullname {sqlSrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X). - {A = sqlSrcListAppend(pParse->db,0,&X); /*A-overwrites-X*/} +fullname(A) ::= nm(X). { + /*A-overwrites-X. */ + A = sql_src_list_append(pParse->db,0,&X); + if (A == NULL) { + sql_parser_error(pParse); + return; + } +} %type joinop {int} join_nm(A) ::= id(A). @@ -1150,7 +1156,11 @@ expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] { A.zEnd = &E.z[E.n]; } expr(A) ::= expr(A) in_op(N) nm(Y) paren_exprlist(E). [IN] { - SrcList *pSrc = sqlSrcListAppend(pParse->db, 0,&Y); + struct SrcList *pSrc = sql_src_list_append(pParse->db, 0,&Y); + if (pSrc == NULL) { + sql_parser_error(pParse); + return; + } Select *pSelect = sqlSelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); if( E ) sqlSrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A.pExpr = sqlPExpr(pParse, TK_IN, A.pExpr, 0); @@ -1220,8 +1230,12 @@ paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;} // cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) ON nm(Y) LP sortlist(Z) RP. { - sql_create_index(pParse, &X, sqlSrcListAppend(pParse->db,0,&Y), Z, &S, - SORT_ORDER_ASC, NE, U); + struct SrcList *src_list = sql_src_list_append(pParse->db,0,&Y); + if (src_list == NULL) { + sql_parser_error(pParse); + return; + } + sql_create_index(pParse, &X, src_list, Z, &S, SORT_ORDER_ASC, NE, U); } %type uniqueflag {int} diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 840bca556..fda4296cc 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -256,7 +256,7 @@ findRightmost(Select * p) /** - * Work the same as sqlSrcListAppend(), but before adding to + * Work the same as sql_src_list_append(), but before adding to * list provide check on name duplicates: only values with unique * names are appended. Moreover, names of tables are not * normalized: it is parser's business and in struct Select they @@ -4116,9 +4116,9 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } else { assert(pParent != p); /* 2nd and subsequent times through the loop */ pSrc = pParent->pSrc = - sqlSrcListAppend(db, 0, 0); - if (pSrc == 0) { - assert(db->mallocFailed); + sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + sql_parser_error(pParse); break; } } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 0895f7de1..2df1830d6 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3428,7 +3428,27 @@ sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, struct SrcList * sql_src_list_new(struct sql *db); -SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); +/** + * Append a new table name to the given list. Create a new + * SrcList if need be. A new entry is created in the list even + * if name_token is NULL. + * + * A SrcList is returned, or NULL if there is an OOM error. + * The returned SrcList might be the same as the list that was + * input or it might be a new one. If an OOM error does occurs, + * then the prior value of list that is input to this routine is + * automatically freed. + * + * @param db The database connection. + * @param list Append to this SrcList. NULL creates a new SrcList. + * @param name_token Token representing table name. + * @retval Not NULL SrcList pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token); + SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, Token *, Select *, Expr *, IdList *); void sqlSrcListIndexedBy(Parse *, SrcList *, Token *); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index f7e6189de..20d41e6a1 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -588,12 +588,13 @@ targetSrcList(Parse * pParse, /* The parsing context */ sql *db = pParse->db; SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlSrcListAppend(db, 0, 0); - if (pSrc) { - assert(pSrc->nSrc > 0); - pSrc->a[pSrc->nSrc - 1].zName = - sqlDbStrDup(db, pStep->zTarget); + pSrc = sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + sql_parser_error(pParse); + return NULL; } + assert(pSrc->nSrc > 0); + pSrc->a[pSrc->nSrc - 1].zName = sqlDbStrDup(db, pStep->zTarget); return pSrc; } -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-03-11 15:04 ` Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Hi! Thanks for the fixes! See 2 comments below. > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index f53b2561c..04f65f165 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2821,6 +2785,7 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ > sqlIdListDelete(db, pUsing); > sql_select_delete(db, pSubquery); > return 0; > + > } 1. Stray empty line. Please, do self-review before sending a patch. Such a stray change says that you did not scan the patch before a send. > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index 0895f7de1..2df1830d6 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3428,7 +3428,27 @@ sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, > struct SrcList * > sql_src_list_new(struct sql *db); > > -SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); > +/** > + * Append a new table name to the given list. Create a new > + * SrcList if need be. A new entry is created in the list even > + * if name_token is NULL. > + * > + * A SrcList is returned, or NULL if there is an OOM error. 2. This should be said in @retval sections. > + * The returned SrcList might be the same as the list that was > + * input or it might be a new one. If an OOM error does occurs, > + * then the prior value of list that is input to this routine is > + * automatically freed. > + * > + * @param db The database connection. > + * @param list Append to this SrcList. NULL creates a new SrcList. > + * @param name_token Token representing table name. > + * @retval Not NULL SrcList pointer on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct SrcList * > +sql_src_list_append(struct sql *db, struct SrcList *list, > + struct Token *name_token); > + > SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, > Token *, Select *, Expr *, IdList *); > void sqlSrcListIndexedBy(Parse *, SrcList *, Token *); ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-03-18 19:33 ` Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > 1. Stray empty line. Please, do self-review before sending a patch. > Such a stray change says that you did not scan the patch before a > send. Fixed. > 2. This should be said in @retval sections. > >> + * The returned SrcList might be the same as the list that was >> + * input or it might be a new one. If an OOM error does occurs, >> + * then the prior value of list that is input to this routine is >> + * automatically freed. >> + * >> + * @param db The database connection. >> + * @param list Append to this SrcList. NULL creates a new SrcList. >> + * @param name_token Token representing table name. >> + * @retval Not NULL SrcList pointer on success. >> + * @retval NULL Otherwise. The diag message is set. >> + */ >> +struct SrcList * >> +sql_src_list_append(struct sql *db, struct SrcList *list, >> + struct Token *name_token); /** * Append a new table name to the given list. Create a new * SrcList if need be. A new entry is created in the list even * if name_token is NULL. * * @param db The database connection. * @param list Append to this SrcList. NULL creates a new SrcList. * @param name_token Token representing table name. * @retval Not NULL SrcList pointer is returned. The returned * SrcList might be the same as the list that was input * or it might be a new one. * @retval NULL Otherwise. The diag message is set. The prior * value of list that is input to this routine is * automatically freed. */ =================================================== Refactored sqlIdListAppend routine as sql_id_list_append and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_id_list_append body has a sqlNameFromToken call that will be changed in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its sqlNameFromToken call remained to be non-Tarantool (for now). It means, that if sqlNameFromToken fails in sql_id_list_append there is no diag message created. Part of #3931 --- src/box/sql/build.c | 44 +++++++++++++++++++++----------------------- src/box/sql/expr.c | 8 +++++--- src/box/sql/parse.y | 19 +++++++++++++++---- src/box/sql/sqlInt.h | 16 +++++++++++++++- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index ef38503da..dae582d1f 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2516,30 +2516,28 @@ sqlArrayAllocate(sql * db, /* Connection to notify of malloc failures */ return pArray; } -/* - * Append a new element to the given IdList. Create a new IdList if - * need be. - * - * A new IdList is returned, or NULL if malloc() fails. - */ -IdList * -sqlIdListAppend(sql * db, IdList * pList, Token * pToken) -{ - int i; - if (pList == 0) { - pList = sqlDbMallocZero(db, sizeof(IdList)); - if (pList == 0) - return 0; - } - pList->a = sqlArrayAllocate(db, - pList->a, - sizeof(pList->a[0]), &pList->nId, &i); - if (i < 0) { - sqlIdListDelete(db, pList); - return 0; +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *list, + struct Token *name_token) +{ + if (list == NULL && + (list = sqlDbMallocZero(db, sizeof(struct IdList))) == NULL) { + diag_set(OutOfMemory, sizeof(struct IdList), "sqlDbMallocZero", + "list"); + return NULL; } - pList->a[i].zName = sqlNameFromToken(db, pToken); - return pList; + int i; + list->a = sqlArrayAllocate(db, list->a, sizeof(list->a[0]), + &list->nId, &i); + if (i < 0) + goto error; + list->a[i].zName = sqlNameFromToken(db, name_token); + if (list->a[i].zName == NULL) + goto error; + return list; +error: + sqlIdListDelete(db, list); + return NULL; } /* diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index a2c70935e..0457ff833 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1658,9 +1658,11 @@ sqlIdListDup(sql * db, IdList * p) sqlDbFree(db, pNew); return 0; } - /* Note that because the size of the allocation for p->a[] is not - * necessarily a power of two, sqlIdListAppend() may not be called - * on the duplicate created by this function. + /* + * Note that because the size of the allocation for p->a[] + * is not necessarily a power of two, sql_id_list_append() + * may not be called on the duplicate created by this + * function. */ for (i = 0; i < p->nId; i++) { struct IdList_item *pNewItem = &pNew->a[i]; diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index ead71dfc0..daeb6e84b 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -804,10 +804,21 @@ insert_cmd(A) ::= REPLACE. {A = ON_CONFLICT_ACTION_REPLACE;} idlist_opt(A) ::= . {A = 0;} idlist_opt(A) ::= LP idlist(X) RP. {A = X;} -idlist(A) ::= idlist(A) COMMA nm(Y). - {A = sqlIdListAppend(pParse->db,A,&Y);} -idlist(A) ::= nm(Y). - {A = sqlIdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} +idlist(A) ::= idlist(A) COMMA nm(Y). { + A = sql_id_list_append(pParse->db,A,&Y); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} +idlist(A) ::= nm(Y). { + /* A-overwrites-Y. */ + A = sql_id_list_append(pParse->db,0,&Y); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} /////////////////////////// Expression Processing ///////////////////////////// // diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index b6c89893a..8f56f3e63 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3411,7 +3411,21 @@ sql_drop_table(struct Parse *, struct SrcList *, bool, bool); void sqlInsert(Parse *, SrcList *, Select *, IdList *, enum on_conflict_action); void *sqlArrayAllocate(sql *, void *, int, int *, int *); -IdList *sqlIdListAppend(sql *, IdList *, Token *); + +/** + * Append a new element to the given IdList. Create a new IdList + * if need be. + * + * @param db The database connection. + * @param list The pointer to existent Id list if exists. + * @param name_token The token containing name. + * @retval Not NULL IdList pointer is returned on success. + * @retval NULL Otherwise. Diag message is set. + */ +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *list, + struct Token *name_token); + int sqlIdListIndex(IdList *, const char *); /** -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-03-20 11:02 ` Kirill Shcherbatov @ 2019-03-26 17:08 ` Vladislav Shpilevoy 2019-03-26 18:07 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 17:08 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Hi! Thanks for the fixes! Please, look at the diff below and on the branch. ============================================================= commit 5edf345d82d7954944a1d7c7464b724821829388 Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Mon Mar 25 15:09:13 2019 +0300 Review fixes diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index ead71dfc0..9bd948bb4 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -617,7 +617,7 @@ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP %type fullname {SrcList*} %destructor fullname {sqlSrcListDelete(pParse->db, $$);} fullname(A) ::= nm(X). { - /*A-overwrites-X. */ + /* A-overwrites-X. */ A = sql_src_list_append(pParse->db,0,&X); if (A == NULL) { pParse->is_aborted = true; diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index b6c89893a..6dd1c58a7 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3459,12 +3459,8 @@ sql_src_list_new(struct sql *db); * @param db The database connection. * @param list Append to this SrcList. NULL creates a new SrcList. * @param name_token Token representing table name. - * @retval Not NULL SrcList pointer is returned. The returned - * SrcList might be the same as the list that was input - * or it might be a new one. - * @retval NULL Otherwise. The diag message is set. The prior - * value of list that is input to this routine is - * automatically freed. + * @retval Not NULL A new SrcList or updated @a list. + * @retval NULL Error. A diag message is set. @A list is deleted. */ struct SrcList * sql_src_list_append(struct sql *db, struct SrcList *list, ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/7] sql: refactor sql_src_list_append to set diag 2019-03-26 17:08 ` Vladislav Shpilevoy @ 2019-03-26 18:07 ` Vladislav Shpilevoy 0 siblings, 0 replies; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 18:07 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Sorry. Fixes here and in other emails are bad formatted. My email client has updated and dropped some settings. On the branch everything is fine (in fixes commits). On 26/03/2019 20:08, Vladislav Shpilevoy wrote: > Hi! Thanks for the fixes! > > Please, look at the diff below and on the branch. > > ============================================================= > > commit 5edf345d82d7954944a1d7c7464b724821829388 > Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> > Date: Mon Mar 25 15:09:13 2019 +0300 > > Review fixes > > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > index ead71dfc0..9bd948bb4 100644 > --- a/src/box/sql/parse.y > +++ b/src/box/sql/parse.y > @@ -617,7 +617,7 @@ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP > %type fullname {SrcList*} > %destructor fullname {sqlSrcListDelete(pParse->db, $$);} > fullname(A) ::= nm(X). { > - /*A-overwrites-X. */ > + /* A-overwrites-X. */ > A = sql_src_list_append(pParse->db,0,&X); > if (A == NULL) { > pParse->is_aborted = true; > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index b6c89893a..6dd1c58a7 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3459,12 +3459,8 @@ sql_src_list_new(struct sql *db); > * @param db The database connection. > * @param list Append to this SrcList. NULL creates a new SrcList. > * @param name_token Token representing table name. > - * @retval Not NULL SrcList pointer is returned. The returned > - * SrcList might be the same as the list that was input > - * or it might be a new one. > - * @retval NULL Otherwise. The diag message is set. The prior > - * value of list that is input to this routine is > - * automatically freed. > + * @retval Not NULL A new SrcList or updated @a list. > + * @retval NULL Error. A diag message is set. @A list is deleted. > */ > struct SrcList * > sql_src_list_append(struct sql *db, struct SrcList *list, > ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 4/7] sql: refactor sql_name_from_token to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (2 preceding siblings ...) 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 3/7] sql: refactor sql_src_list_append " Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate " Kirill Shcherbatov ` (4 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_name_from_token routine to use diag_set in case of memory allocation error. This change is necessary because the sql_name_from_token body has a sqlNameFromToken call that will be changed in subsequent patches. Needed for #3931 --- src/box/sql/alter.c | 8 +- src/box/sql/analyze.c | 47 ++++---- src/box/sql/build.c | 241 +++++++++++++++++++++++------------------- src/box/sql/expr.c | 2 +- src/box/sql/parse.y | 15 ++- src/box/sql/pragma.c | 24 +++-- src/box/sql/sqlInt.h | 35 +++++- src/box/sql/trigger.c | 4 +- 8 files changed, 228 insertions(+), 148 deletions(-) diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c index d49ebb8df..0c2f799b9 100644 --- a/src/box/sql/alter.c +++ b/src/box/sql/alter.c @@ -43,9 +43,10 @@ sql_alter_table_rename(struct Parse *parse, struct SrcList *src_tab, { assert(src_tab->nSrc == 1); struct sql *db = parse->db; - char *new_name = sqlNameFromToken(db, new_name_tk); + assert(new_name_tk != NULL); + char *new_name = sql_name_from_token(db, new_name_tk); if (new_name == NULL) - goto exit_rename_table; + goto tnt_error; /* Check that new name isn't occupied by another table. */ if (space_by_name(new_name) != NULL) { diag_set(ClientError, ER_SPACE_EXISTS, new_name); @@ -72,8 +73,7 @@ exit_rename_table: return; tnt_error: sqlDbFree(db, new_name); - parse->rc = SQL_TARANTOOL_ERROR; - parse->nErr++; + sql_parser_error(parse); goto exit_rename_table; } diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 8c83288e6..cf5af7a8b 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -1111,34 +1111,39 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space) void sqlAnalyze(Parse * pParse, Token * pName) { - sql *db = pParse->db; + char *name = NULL; + struct sql *db = pParse->db; + struct Vdbe *v = sqlGetVdbe(pParse); + if (v == NULL) + return; if (pName == NULL) { /* Form 1: Analyze everything */ sql_analyze_database(pParse); } else { /* Form 2: Analyze table named */ - char *z = sqlNameFromToken(db, pName); - if (z != NULL) { - struct space *sp = space_by_name(z); - if (sp != NULL) { - if (sp->def->opts.is_view) { - sqlErrorMsg(pParse, "VIEW isn't "\ - "allowed to be "\ - "analyzed"); - } else { - vdbe_emit_analyze_table(pParse, sp); - } - } else { - diag_set(ClientError, ER_NO_SUCH_SPACE, z); - pParse->rc = SQL_TARANTOOL_ERROR; - pParse->nErr++; - } - sqlDbFree(db, z); + name = sql_name_from_token(db, pName); + if (name == NULL) + goto tnt_error; + struct space *sp = space_by_name(name); + if (sp == NULL) { + diag_set(ClientError, ER_NO_SUCH_SPACE, name); + goto tnt_error; + } + if (sp->def->opts.is_view) { + sqlErrorMsg(pParse, + "VIEW isn't allowed to be analyzed"); + goto cleanup; + } else { + vdbe_emit_analyze_table(pParse, sp); } } - Vdbe *v = sqlGetVdbe(pParse); - if (v != NULL) - sqlVdbeAddOp0(v, OP_Expire); + sqlVdbeAddOp0(v, OP_Expire); +cleanup: + sqlDbFree(db, name); + return; +tnt_error: + sql_parser_error(pParse); + goto cleanup; } ssize_t diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 4fe838608..2084dbfeb 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -232,30 +232,18 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column) return false; } -/* - * Given a token, return a string that consists of the text of that - * token. Space to hold the returned string - * is obtained from sqlMalloc() and must be freed by the calling - * function. - * - * Any quotation marks (ex: "name", 'name', [name], or `name`) that - * surround the body of the token are removed. - * - * Tokens are often just pointers into the original SQL text and so - * are not \000 terminated and are not persistent. The returned string - * is \000 terminated and is persistent. - */ char * -sqlNameFromToken(sql * db, Token * pName) +sql_name_from_token(struct sql *db, struct Token *name_token) { - char *zName; - if (pName) { - zName = sqlDbStrNDup(db, (char *)pName->z, pName->n); - sqlNormalizeName(zName); - } else { - zName = 0; + assert(name_token != NULL && name_token->z != NULL); + char *name = sqlDbStrNDup(db, (char *)name_token->z, name_token->n); + if (name == NULL) { + diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", + "name"); + return NULL; } - return zName; + sqlNormalizeName(name); + return name; } /* @@ -332,7 +320,10 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) goto cleanup; sqlVdbeCountChanges(v); - zName = sqlNameFromToken(db, pName); + assert(pName != NULL); + zName = sql_name_from_token(db, pName); + if (zName == NULL) + goto tnt_error; pParse->sNameToken = *pName; if (zName == 0) @@ -344,10 +335,9 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) if (space != NULL) { if (!noErr) { diag_set(ClientError, ER_SPACE_EXISTS, zName); - sql_parser_error(pParse); - } else { - assert(!db->init.busy || CORRUPT_DB); + goto tnt_error; } + assert(!db->init.busy || CORRUPT_DB); goto cleanup; } @@ -367,6 +357,9 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) cleanup: sqlDbFree(db, zName); return; +tnt_error: + sql_parser_error(pParse); + goto cleanup; } /** @@ -708,11 +701,13 @@ sqlAddCollateType(Parse * pParse, Token * pToken) struct space *space = pParse->new_space; uint32_t i = space->def->field_count - 1; sql *db = pParse->db; - char *zColl = sqlNameFromToken(db, pToken); - if (!zColl) + char *coll_name = sql_name_from_token(db, pToken); + if (coll_name == NULL) { + sql_parser_error(pParse); return; + } uint32_t *coll_id = &space->def->fields[i].coll_id; - if (sql_get_coll_seq(pParse, zColl, coll_id) != NULL) { + if (sql_get_coll_seq(pParse, coll_name, coll_id) != NULL) { /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>", * then an index may have been created on this column before the * collation type was added. Correct this if it is the case. @@ -726,7 +721,7 @@ sqlAddCollateType(Parse * pParse, Token * pToken) } } } - sqlDbFree(db, zColl); + sqlDbFree(db, coll_name); } struct coll * @@ -1750,9 +1745,9 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, rlist_add_entry(&parse_context->new_fk_constraint, fk_parse, link); } assert(parent != NULL); - parent_name = sqlNameFromToken(db, parent); + parent_name = sql_name_from_token(db, parent); if (parent_name == NULL) - goto exit_create_fk; + goto tnt_error; /* * Within ALTER TABLE ADD CONSTRAINT FK also can be * self-referenced, but in this case parent (which is @@ -1785,15 +1780,20 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, sqlMPrintf(db, "FK_CONSTRAINT_%d_%s", ++parse_context->fk_constraint_count, space->def->name); + if (constraint_name == NULL) + goto exit_create_fk; } else { struct Token *cnstr_nm = &parse_context->constraintName; - constraint_name = sqlNameFromToken(db, cnstr_nm); + constraint_name = sql_name_from_token(db, cnstr_nm); + if (constraint_name == NULL) + goto tnt_error; } } else { - constraint_name = sqlNameFromToken(db, constraint); + assert(constraint != NULL); + constraint_name = sql_name_from_token(db, constraint); + if (constraint_name == NULL) + goto tnt_error; } - if (constraint_name == NULL) - goto exit_create_fk; const char *error_msg = "number of columns in foreign key does not " "match the number of columns in the primary " "index of referenced table"; @@ -1922,15 +1922,18 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table, struct space *child = space_by_name(table_name); if (child == NULL) { diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); - parse_context->rc = SQL_TARANTOOL_ERROR; - parse_context->nErr++; + sql_parser_error(parse_context); return; } - char *constraint_name = sqlNameFromToken(parse_context->db, - constraint); - if (constraint_name != NULL) - vdbe_emit_fk_constraint_drop(parse_context, constraint_name, - child->def->id); + assert(constraint != NULL); + char *constraint_name = + sql_name_from_token(parse_context->db, constraint); + if (constraint_name == NULL) { + sql_parser_error(parse_context); + return; + } + vdbe_emit_fk_constraint_drop(parse_context, constraint_name, + child->def->id); /* * We account changes to row count only if drop of * foreign keys take place in a separate @@ -2192,9 +2195,9 @@ sql_create_index(struct Parse *parse, struct Token *token, */ if (token != NULL) { assert(token->z != NULL); - name = sqlNameFromToken(db, token); + name = sql_name_from_token(db, token); if (name == NULL) - goto exit_create_index; + goto tnt_error; if (sql_space_index_by_name(space, name) != NULL) { if (!if_not_exist) { diag_set(ClientError, ER_INDEX_EXISTS_IN_SPACE, @@ -2205,10 +2208,13 @@ sql_create_index(struct Parse *parse, struct Token *token, } } else { char *constraint_name = NULL; - if (parse->constraintName.z != NULL) + if (parse->constraintName.z != NULL) { constraint_name = - sqlNameFromToken(db, - &parse->constraintName); + sql_name_from_token(db, + &parse->constraintName); + if (constraint_name == NULL) + goto tnt_error; + } /* * This naming is temporary. Now it's not @@ -2246,9 +2252,7 @@ sql_create_index(struct Parse *parse, struct Token *token, if (tbl_name != NULL && space_is_system(space)) { diag_set(ClientError, ER_MODIFY_INDEX, name, def->name, "can't create index on system space"); - parse->nErr++; - parse->rc = SQL_TARANTOOL_ERROR; - goto exit_create_index; + goto tnt_error; } /* @@ -2275,9 +2279,7 @@ sql_create_index(struct Parse *parse, struct Token *token, index = (struct index *) region_alloc(&parse->region, sizeof(*index)); if (index == NULL) { diag_set(OutOfMemory, sizeof(*index), "region", "index"); - parse->rc = SQL_TARANTOOL_ERROR; - parse->nErr++; - goto exit_create_index; + goto tnt_error; } memset(index, 0, sizeof(*index)); @@ -2436,6 +2438,10 @@ sql_create_index(struct Parse *parse, struct Token *token, sql_expr_list_delete(db, col_list); sqlSrcListDelete(db, tbl_name); sqlDbFree(db, name); + return; +tnt_error: + sql_parser_error(parse); + goto exit_create_index; } void @@ -2448,31 +2454,28 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list, /* Never called with prior errors. */ assert(parse_context->nErr == 0); assert(table_token != NULL); - const char *table_name = sqlNameFromToken(db, table_token); - if (db->mallocFailed) { - goto exit_drop_index; - } + const char *table_name = sql_name_from_token(db, table_token); + if (table_name == NULL) + goto tnt_error; sqlVdbeCountChanges(v); assert(index_name_list->nSrc == 1); assert(table_token->n > 0); struct space *space = space_by_name(table_name); if (space == NULL) { - if (!if_exists) { - diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); - sql_parser_error(parse_context); - } - goto exit_drop_index; + if (if_exists) + goto exit_drop_index; + diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); + goto tnt_error; } const char *index_name = index_name_list->a[0].zName; uint32_t index_id = box_index_id_by_name(space->def->id, index_name, strlen(index_name)); if (index_id == BOX_ID_NIL) { - if (!if_exists) { - diag_set(ClientError, ER_NO_SUCH_INDEX_NAME, - index_name, table_name); - sql_parser_error(parse_context); - } - goto exit_drop_index; + if (if_exists) + goto exit_drop_index; + diag_set(ClientError, ER_NO_SUCH_INDEX_NAME, index_name, + table_name); + goto tnt_error; } struct index *index = space_index(space, index_id); assert(index != NULL); @@ -2493,6 +2496,10 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list, exit_drop_index: sqlSrcListDelete(db, index_name_list); sqlDbFree(db, (void *) table_name); + return; +tnt_error: + sql_parser_error(parse_context); + goto exit_drop_index; } /* @@ -2538,30 +2545,28 @@ sqlArrayAllocate(sql * db, /* Connection to notify of malloc failures */ return pArray; } -/* - * Append a new element to the given IdList. Create a new IdList if - * need be. - * - * A new IdList is returned, or NULL if malloc() fails. - */ -IdList * -sqlIdListAppend(sql * db, IdList * pList, Token * pToken) +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *list, + struct Token *name_token) { - int i; - if (pList == 0) { - pList = sqlDbMallocZero(db, sizeof(IdList)); - if (pList == 0) - return 0; - } - pList->a = sqlArrayAllocate(db, - pList->a, - sizeof(pList->a[0]), &pList->nId, &i); - if (i < 0) { - sqlIdListDelete(db, pList); - return 0; + if (list == NULL && + (list = sqlDbMallocZero(db, sizeof(IdList))) == NULL) { + diag_set(OutOfMemory, sizeof(IdList), "sqlDbMallocZero", + "list"); + return NULL; } - pList->a[i].zName = sqlNameFromToken(db, pToken); - return pList; + int i; + list->a = sqlArrayAllocate(db, list->a, sizeof(list->a[0]), + &list->nId, &i); + if (i < 0) + goto error; + list->a[i].zName = sql_name_from_token(db, name_token); + if (list->a[i].zName == NULL) + goto error; + return list; +error: + sqlIdListDelete(db, list); + return NULL; } /* @@ -2671,7 +2676,11 @@ sql_src_list_append(struct sql *db, struct SrcList *list, list = new_list; } pItem = &list->a[list->nSrc - 1]; - pItem->zName = sqlNameFromToken(db, name_token); + if (name_token != NULL) { + pItem->zName = sql_name_from_token(db, name_token); + if (pItem->zName == NULL) + goto error; + } return list; error: sqlSrcListDelete(db, list); @@ -2773,8 +2782,10 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); - if (pAlias->n) { - pItem->zAlias = sqlNameFromToken(db, pAlias); + if (pAlias->n != 0) { + pItem->zAlias = sql_name_from_token(db, pAlias); + if (pItem->zAlias == NULL) + goto tnt_error; } pItem->pSelect = pSubquery; pItem->pOn = pOn; @@ -2811,8 +2822,15 @@ sqlSrcListIndexedBy(Parse * pParse, SrcList * p, Token * pIndexedBy) */ pItem->fg.notIndexed = 1; } else { - pItem->u1.zIndexedBy = - sqlNameFromToken(pParse->db, pIndexedBy); + if (pIndexedBy->z != NULL) { + pItem->u1.zIndexedBy = + sql_name_from_token(pParse->db, + pIndexedBy); + if (pItem->u1.zIndexedBy == NULL) { + sql_parser_error(pParse); + return; + } + } pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy != 0); } } @@ -2898,11 +2916,12 @@ sql_transaction_rollback(Parse *pParse) void sqlSavepoint(Parse * pParse, int op, Token * pName) { - char *zName = sqlNameFromToken(pParse->db, pName); + struct sql *db = pParse->db; + char *zName = sql_name_from_token(db, pName); if (zName) { Vdbe *v = sqlGetVdbe(pParse); if (!v) { - sqlDbFree(pParse->db, zName); + sqlDbFree(db, zName); return; } if (op == SAVEPOINT_BEGIN && @@ -2912,6 +2931,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName) return; } sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); + } else { + sql_parser_error(pParse); } } @@ -2987,19 +3008,23 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ { sql *db = pParse->db; With *pNew; - char *zName; - /* Check that the CTE name is unique within this WITH clause. If - * not, store an error in the Parse structure. + /* + * Check that the CTE name is unique within this WITH + * clause. If not, store an error in the Parse structure. */ - zName = sqlNameFromToken(pParse->db, pName); - if (zName && pWith) { + char *name = sql_name_from_token(db, pName); + if (name == NULL) { + sql_parser_error(pParse); + return NULL; + } + if (pWith != NULL) { int i; for (i = 0; i < pWith->nCte; i++) { - if (strcmp(zName, pWith->a[i].zName) == 0) { + if (strcmp(name, pWith->a[i].zName) == 0) { sqlErrorMsg(pParse, - "duplicate WITH table name: %s", - zName); + "duplicate WITH table name: %s", + name); } } } @@ -3011,17 +3036,17 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ } else { pNew = sqlDbMallocZero(db, sizeof(*pWith)); } - assert((pNew != 0 && zName != 0) || db->mallocFailed); + assert((pNew != NULL && name != NULL) || db->mallocFailed); if (db->mallocFailed) { sql_expr_list_delete(db, pArglist); sql_select_delete(db, pQuery); - sqlDbFree(db, zName); + sqlDbFree(db, name); pNew = pWith; } else { pNew->a[pNew->nCte].pSelect = pQuery; pNew->a[pNew->nCte].pCols = pArglist; - pNew->a[pNew->nCte].zName = zName; + pNew->a[pNew->nCte].zName = name; pNew->a[pNew->nCte].zCteErr = 0; pNew->nCte++; } diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index c914cfb68..fa2ff1ab3 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1638,7 +1638,7 @@ sqlIdListDup(sql * db, IdList * p) return 0; } /* Note that because the size of the allocation for p->a[] is not - * necessarily a power of two, sqlIdListAppend() may not be called + * necessarily a power of two, sql_id_list_append() may not be called * on the duplicate created by this function. */ for (i = 0; i < p->nId; i++) { diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 4ae9eff83..f56b831e5 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -797,10 +797,17 @@ insert_cmd(A) ::= REPLACE. {A = ON_CONFLICT_ACTION_REPLACE;} idlist_opt(A) ::= . {A = 0;} idlist_opt(A) ::= LP idlist(X) RP. {A = X;} -idlist(A) ::= idlist(A) COMMA nm(Y). - {A = sqlIdListAppend(pParse->db,A,&Y);} -idlist(A) ::= nm(Y). - {A = sqlIdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} +idlist(A) ::= idlist(A) COMMA nm(Y). { + A = sql_id_list_append(pParse->db,A,&Y); + if (A == NULL) + sql_parser_error(pParse); +} +idlist(A) ::= nm(Y). { + /* A-overwrites-Y. */ + A = sql_id_list_append(pParse->db,0,&Y); + if (A == NULL) + sql_parser_error(pParse); +} /////////////////////////// Expression Processing ///////////////////////////// // diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c index a7224f5c2..1e4ba3518 100644 --- a/src/box/sql/pragma.c +++ b/src/box/sql/pragma.c @@ -452,19 +452,27 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ sqlVdbeRunOnlyOnce(v); pParse->nMem = 2; - zLeft = sqlNameFromToken(db, pId); - if (!zLeft) { + if (pId == NULL) { printActivePragmas(user_session); return; } - + zLeft = sql_name_from_token(db, pId); + if (zLeft == NULL) + goto tnt_error; if (minusFlag) { zRight = sqlMPrintf(db, "-%T", pValue); } else { - zRight = sqlNameFromToken(db, pValue); + if (pValue != NULL) { + zRight = sql_name_from_token(db, pValue); + if (zRight == NULL) + goto tnt_error; + } + } + if (pValue2 != NULL) { + zTable = sql_name_from_token(db, pValue2); + if (zTable == NULL) + goto tnt_error; } - zTable = sqlNameFromToken(db, pValue2); - /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if (pPragma == 0) { @@ -664,4 +672,8 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ sqlDbFree(db, zLeft); sqlDbFree(db, zRight); sqlDbFree(db, zTable); + return; +tnt_error: + sql_parser_error(pParse); + goto pragma_out; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index eae2ec8e8..10943d3c8 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3424,7 +3424,20 @@ sql_drop_table(struct Parse *, struct SrcList *, bool, bool); void sqlInsert(Parse *, SrcList *, Select *, IdList *, enum on_conflict_action); void *sqlArrayAllocate(sql *, void *, int, int *, int *); -IdList *sqlIdListAppend(sql *, IdList *, Token *); + +/* + * Append a new element to the given IdList. Create a new IdList if + * need be. + * A new IdList is returned, or NULL if malloc() fails. + * @param db The database connection. + * @param list The pointer to existent Id list if exists. + * @param name_token The token containing name. + * @retval not NULL IdList pointer is returned on success. + * @retval NULL otherwise. + */ +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *pList, struct Token *pToken); + int sqlIdListIndex(IdList *, const char *); /** @@ -3691,7 +3704,25 @@ int sqlExprCodeExprList(Parse *, ExprList *, int, int, u8); void sqlExprIfTrue(Parse *, Expr *, int, int); void sqlExprIfFalse(Parse *, Expr *, int, int); -char *sqlNameFromToken(sql *, Token *); +/* + * Given a token, return a string that consists of the text of + * that token. Space to hold the returned string is obtained + * from sqlMalloc() and must be freed by the calling function. + * + * Any quotation marks (ex: "name", 'name', [name], or `name`) + * that surround the body of the token are removed. + * + * Tokens are often just pointers into the original SQL text and + * so are not \000 terminated and are not persistent. The returned + * string is \000 terminated and is persistent. + * @param db The database connection. + * @param name_token The source token with text. + * @retval not NULL string pointer on success. + * @retval NULL otherwise. + */ +char * +sql_name_from_token(struct sql *db, struct Token *name_token); + int sqlExprCompare(Expr *, Expr *, int); int sqlExprListCompare(ExprList *, ExprList *, int); int sqlExprImpliesExpr(Expr *, Expr *, int); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 20d41e6a1..65218121b 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -82,9 +82,9 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm, goto trigger_cleanup; assert(table->nSrc == 1); - trigger_name = sqlNameFromToken(db, name); + trigger_name = sql_name_from_token(db, name); if (trigger_name == NULL) - goto trigger_cleanup; + goto set_tarantool_error_and_cleanup; if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK) goto trigger_cleanup; -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 4/7] sql: refactor sql_name_from_token to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 4/7] sql: refactor sql_name_from_token " Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov See 5 comments. On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_name_from_token routine to use diag_set in case > of memory allocation error. > This change is necessary because the sql_name_from_token body has > a sqlNameFromToken call that will be changed in subsequent > patches. > > Needed for #3931 > --- > src/box/sql/alter.c | 8 +- > src/box/sql/analyze.c | 47 ++++---- > src/box/sql/build.c | 241 +++++++++++++++++++++++------------------- > src/box/sql/expr.c | 2 +- > src/box/sql/parse.y | 15 ++- > src/box/sql/pragma.c | 24 +++-- > src/box/sql/sqlInt.h | 35 +++++- > src/box/sql/trigger.c | 4 +- > 8 files changed, 228 insertions(+), 148 deletions(-) > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 4fe838608..2084dbfeb 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -332,7 +320,10 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) > goto cleanup; > sqlVdbeCountChanges(v); > > - zName = sqlNameFromToken(db, pName); > + assert(pName != NULL); 1. You already have the same assertion in sql_name_from_token. Why do you need so many new equal assertions? Check other places like build.c:1792 by yourself, please. > + zName = sql_name_from_token(db, pName); > + if (zName == NULL) > + goto tnt_error; > > pParse->sNameToken = *pName; > if (zName == 0) 2. Double check for zName == NULL. > diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c > index a7224f5c2..1e4ba3518 100644 > --- a/src/box/sql/pragma.c > +++ b/src/box/sql/pragma.c > @@ -452,19 +452,27 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ > sqlVdbeRunOnlyOnce(v); > pParse->nMem = 2; > > - zLeft = sqlNameFromToken(db, pId); > - if (!zLeft) { > + if (pId == NULL) { > printActivePragmas(user_session); > return; > } > - > + zLeft = sql_name_from_token(db, pId); > + if (zLeft == NULL) > + goto tnt_error; > if (minusFlag) { > zRight = sqlMPrintf(db, "-%T", pValue); > } else { > - zRight = sqlNameFromToken(db, pValue); > + if (pValue != NULL) { 3. You can make it shorter writing } else if (pValue != NULL) { ... } Instead of } else { if (pValue != NULL) { ... } } > + zRight = sql_name_from_token(db, pValue); > + if (zRight == NULL) > + goto tnt_error; > + } > + } > + if (pValue2 != NULL) { > + zTable = sql_name_from_token(db, pValue2); > + if (zTable == NULL) > + goto tnt_error; > } > - zTable = sqlNameFromToken(db, pValue2); > - > /* Locate the pragma in the lookup table */ > pPragma = pragmaLocate(zLeft); > if (pPragma == 0) { > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index eae2ec8e8..10943d3c8 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3424,7 +3424,20 @@ sql_drop_table(struct Parse *, struct SrcList *, bool, bool); > void sqlInsert(Parse *, SrcList *, Select *, IdList *, > enum on_conflict_action); > void *sqlArrayAllocate(sql *, void *, int, int *, int *); > -IdList *sqlIdListAppend(sql *, IdList *, Token *); > + > +/* > + * Append a new element to the given IdList. Create a new IdList if > + * need be. > + * A new IdList is returned, or NULL if malloc() fails. 4. The last sentence is exactly what you wrote after @retvals. Please, do not duplicate it. > + * @param db The database connection. > + * @param list The pointer to existent Id list if exists. > + * @param name_token The token containing name. > + * @retval not NULL IdList pointer is returned on success. > + * @retval NULL otherwise. 5. Sentences are started from capital letters. I will not write that again. Please, find and fix other places. > + */ > +struct IdList * > +sql_id_list_append(struct sql *db, struct IdList *pList, struct Token *pToken); > + > int sqlIdListIndex(IdList *, const char *); > > /** ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 4/7] sql: refactor sql_name_from_token to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy On 07.03.2019 20:34, Vladislav Shpilevoy wrote: > See 5 comments.First of all, I've split this patch on two patches. > 1. You already have the same assertion in sql_name_from_token. > Why do you need so many new equal assertions? Check other places > like build.c:1792 by yourself, please. I've dropped them all. > 2. Double check for zName == NULL. Fixed. > 3. You can make it shorter writing > > } else if (pValue != NULL) { > ... > } > > Instead of > > } else { > if (pValue != NULL) { > ... > } > } Ok. > 4. The last sentence is exactly what you wrote after @retvals. > Please, do not duplicate it. I've fixed it here. > 5. Sentences are started from capital letters. I will not write > that again. Please, find and fix other places. I've really looked over the patch for this. ========================================================= Refactored sqlIdListAppend routine as sql_id_list_append and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_id_list_append body has a sqlNameFromToken call that will be changed in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its sqlNameFromToken call remained to be non-Tarantool (for now). It means, that if sqlNameFromToken fails in sql_id_list_append there is no diag message created. Part of #3931 --- src/box/sql/build.c | 44 +++++++++++++++++++++----------------------- src/box/sql/expr.c | 2 +- src/box/sql/parse.y | 15 +++++++++++---- src/box/sql/sqlInt.h | 15 ++++++++++++++- 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 04f65f165..d374acb47 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2538,30 +2538,28 @@ sqlArrayAllocate(sql * db, /* Connection to notify of malloc failures */ return pArray; } -/* - * Append a new element to the given IdList. Create a new IdList if - * need be. - * - * A new IdList is returned, or NULL if malloc() fails. - */ -IdList * -sqlIdListAppend(sql * db, IdList * pList, Token * pToken) -{ - int i; - if (pList == 0) { - pList = sqlDbMallocZero(db, sizeof(IdList)); - if (pList == 0) - return 0; - } - pList->a = sqlArrayAllocate(db, - pList->a, - sizeof(pList->a[0]), &pList->nId, &i); - if (i < 0) { - sqlIdListDelete(db, pList); - return 0; +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *list, + struct Token *name_token) +{ + if (list == NULL && + (list = sqlDbMallocZero(db, sizeof(IdList))) == NULL) { + diag_set(OutOfMemory, sizeof(IdList), "sqlDbMallocZero", + "list"); + return NULL; } - pList->a[i].zName = sqlNameFromToken(db, pToken); - return pList; + int i; + list->a = sqlArrayAllocate(db, list->a, sizeof(list->a[0]), + &list->nId, &i); + if (i < 0) + goto error; + list->a[i].zName = sqlNameFromToken(db, name_token); + if (list->a[i].zName == NULL) + goto error; + return list; +error: + sqlIdListDelete(db, list); + return NULL; } /* diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index a75f23756..44b6ce11f 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1638,7 +1638,7 @@ sqlIdListDup(sql * db, IdList * p) return 0; } /* Note that because the size of the allocation for p->a[] is not - * necessarily a power of two, sqlIdListAppend() may not be called + * necessarily a power of two, sql_id_list_append() may not be called * on the duplicate created by this function. */ for (i = 0; i < p->nId; i++) { diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 122fda2f0..8a8f1b82f 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -797,10 +797,17 @@ insert_cmd(A) ::= REPLACE. {A = ON_CONFLICT_ACTION_REPLACE;} idlist_opt(A) ::= . {A = 0;} idlist_opt(A) ::= LP idlist(X) RP. {A = X;} -idlist(A) ::= idlist(A) COMMA nm(Y). - {A = sqlIdListAppend(pParse->db,A,&Y);} -idlist(A) ::= nm(Y). - {A = sqlIdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} +idlist(A) ::= idlist(A) COMMA nm(Y). { + A = sql_id_list_append(pParse->db,A,&Y); + if (A == NULL) + sql_parser_error(pParse); +} +idlist(A) ::= nm(Y). { + /* A-overwrites-Y. */ + A = sql_id_list_append(pParse->db,0,&Y); + if (A == NULL) + sql_parser_error(pParse); +} /////////////////////////// Expression Processing ///////////////////////////// // diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 2df1830d6..56ae90a95 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3388,7 +3388,20 @@ sql_drop_table(struct Parse *, struct SrcList *, bool, bool); void sqlInsert(Parse *, SrcList *, Select *, IdList *, enum on_conflict_action); void *sqlArrayAllocate(sql *, void *, int, int *, int *); -IdList *sqlIdListAppend(sql *, IdList *, Token *); + +/** + * Append a new element to the given IdList. Create a new IdList + * if need be. + * + * @param db The database connection. + * @param list The pointer to existent Id list if exists. + * @param name_token The token containing name. + * @retval Not NULL IdList pointer is returned on success. + * @retval NULL otherwise. Diag message is set. + */ +struct IdList * +sql_id_list_append(struct sql *db, struct IdList *pList, struct Token *pToken); + int sqlIdListIndex(IdList *, const char *); /** -- 2.21.0 =========================================================== Refactored sqlNameFromToken routine as sql_name_from_token and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_name_from_token body has a sqlNameFromToken call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/alter.c | 7 +- src/box/sql/analyze.c | 47 +++++----- src/box/sql/build.c | 206 +++++++++++++++++++++++------------------- src/box/sql/pragma.c | 24 +++-- src/box/sql/sqlInt.h | 21 ++++- src/box/sql/trigger.c | 4 +- 6 files changed, 183 insertions(+), 126 deletions(-) diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c index d49ebb8df..3a9e9095e 100644 --- a/src/box/sql/alter.c +++ b/src/box/sql/alter.c @@ -43,9 +43,9 @@ sql_alter_table_rename(struct Parse *parse, struct SrcList *src_tab, { assert(src_tab->nSrc == 1); struct sql *db = parse->db; - char *new_name = sqlNameFromToken(db, new_name_tk); + char *new_name = sql_name_from_token(db, new_name_tk); if (new_name == NULL) - goto exit_rename_table; + goto tnt_error; /* Check that new name isn't occupied by another table. */ if (space_by_name(new_name) != NULL) { diag_set(ClientError, ER_SPACE_EXISTS, new_name); @@ -72,8 +72,7 @@ exit_rename_table: return; tnt_error: sqlDbFree(db, new_name); - parse->rc = SQL_TARANTOOL_ERROR; - parse->nErr++; + sql_parser_error(parse); goto exit_rename_table; } diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 8c83288e6..cf5af7a8b 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -1111,34 +1111,39 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space) void sqlAnalyze(Parse * pParse, Token * pName) { - sql *db = pParse->db; + char *name = NULL; + struct sql *db = pParse->db; + struct Vdbe *v = sqlGetVdbe(pParse); + if (v == NULL) + return; if (pName == NULL) { /* Form 1: Analyze everything */ sql_analyze_database(pParse); } else { /* Form 2: Analyze table named */ - char *z = sqlNameFromToken(db, pName); - if (z != NULL) { - struct space *sp = space_by_name(z); - if (sp != NULL) { - if (sp->def->opts.is_view) { - sqlErrorMsg(pParse, "VIEW isn't "\ - "allowed to be "\ - "analyzed"); - } else { - vdbe_emit_analyze_table(pParse, sp); - } - } else { - diag_set(ClientError, ER_NO_SUCH_SPACE, z); - pParse->rc = SQL_TARANTOOL_ERROR; - pParse->nErr++; - } - sqlDbFree(db, z); + name = sql_name_from_token(db, pName); + if (name == NULL) + goto tnt_error; + struct space *sp = space_by_name(name); + if (sp == NULL) { + diag_set(ClientError, ER_NO_SUCH_SPACE, name); + goto tnt_error; + } + if (sp->def->opts.is_view) { + sqlErrorMsg(pParse, + "VIEW isn't allowed to be analyzed"); + goto cleanup; + } else { + vdbe_emit_analyze_table(pParse, sp); } } - Vdbe *v = sqlGetVdbe(pParse); - if (v != NULL) - sqlVdbeAddOp0(v, OP_Expire); + sqlVdbeAddOp0(v, OP_Expire); +cleanup: + sqlDbFree(db, name); + return; +tnt_error: + sql_parser_error(pParse); + goto cleanup; } ssize_t diff --git a/src/box/sql/build.c b/src/box/sql/build.c index d374acb47..1864bd0ad 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -232,30 +232,18 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column) return false; } -/* - * Given a token, return a string that consists of the text of that - * token. Space to hold the returned string - * is obtained from sqlMalloc() and must be freed by the calling - * function. - * - * Any quotation marks (ex: "name", 'name', [name], or `name`) that - * surround the body of the token are removed. - * - * Tokens are often just pointers into the original SQL text and so - * are not \000 terminated and are not persistent. The returned string - * is \000 terminated and is persistent. - */ char * -sqlNameFromToken(sql * db, Token * pName) +sql_name_from_token(struct sql *db, struct Token *name_token) { - char *zName; - if (pName) { - zName = sqlDbStrNDup(db, (char *)pName->z, pName->n); - sqlNormalizeName(zName); - } else { - zName = 0; + assert(name_token != NULL && name_token->z != NULL); + char *name = sqlDbStrNDup(db, (char *)name_token->z, name_token->n); + if (name == NULL) { + diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", + "name"); + return NULL; } - return zName; + sqlNormalizeName(name); + return name; } /* @@ -332,11 +320,11 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) goto cleanup; sqlVdbeCountChanges(v); - zName = sqlNameFromToken(db, pName); + zName = sql_name_from_token(db, pName); + if (zName == NULL) + goto tnt_error; pParse->sNameToken = *pName; - if (zName == 0) - return; if (sqlCheckIdentifierName(pParse, zName) != SQL_OK) goto cleanup; @@ -344,10 +332,9 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) if (space != NULL) { if (!noErr) { diag_set(ClientError, ER_SPACE_EXISTS, zName); - sql_parser_error(pParse); - } else { - assert(!db->init.busy || CORRUPT_DB); + goto tnt_error; } + assert(!db->init.busy || CORRUPT_DB); goto cleanup; } @@ -367,6 +354,9 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) cleanup: sqlDbFree(db, zName); return; +tnt_error: + sql_parser_error(pParse); + goto cleanup; } /** @@ -708,11 +698,13 @@ sqlAddCollateType(Parse * pParse, Token * pToken) struct space *space = pParse->new_space; uint32_t i = space->def->field_count - 1; sql *db = pParse->db; - char *zColl = sqlNameFromToken(db, pToken); - if (!zColl) + char *coll_name = sql_name_from_token(db, pToken); + if (coll_name == NULL) { + sql_parser_error(pParse); return; + } uint32_t *coll_id = &space->def->fields[i].coll_id; - if (sql_get_coll_seq(pParse, zColl, coll_id) != NULL) { + if (sql_get_coll_seq(pParse, coll_name, coll_id) != NULL) { /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>", * then an index may have been created on this column before the * collation type was added. Correct this if it is the case. @@ -726,7 +718,7 @@ sqlAddCollateType(Parse * pParse, Token * pToken) } } } - sqlDbFree(db, zColl); + sqlDbFree(db, coll_name); } struct coll * @@ -1749,10 +1741,9 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, memset(fk_parse, 0, sizeof(*fk_parse)); rlist_add_entry(&parse_context->new_fk_constraint, fk_parse, link); } - assert(parent != NULL); - parent_name = sqlNameFromToken(db, parent); + parent_name = sql_name_from_token(db, parent); if (parent_name == NULL) - goto exit_create_fk; + goto tnt_error; /* * Within ALTER TABLE ADD CONSTRAINT FK also can be * self-referenced, but in this case parent (which is @@ -1785,15 +1776,19 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, sqlMPrintf(db, "FK_CONSTRAINT_%d_%s", ++parse_context->fk_constraint_count, space->def->name); + if (constraint_name == NULL) + goto exit_create_fk; } else { struct Token *cnstr_nm = &parse_context->constraintName; - constraint_name = sqlNameFromToken(db, cnstr_nm); + constraint_name = sql_name_from_token(db, cnstr_nm); + if (constraint_name == NULL) + goto tnt_error; } } else { - constraint_name = sqlNameFromToken(db, constraint); + constraint_name = sql_name_from_token(db, constraint); + if (constraint_name == NULL) + goto tnt_error; } - if (constraint_name == NULL) - goto exit_create_fk; const char *error_msg = "number of columns in foreign key does not " "match the number of columns in the primary " "index of referenced table"; @@ -1922,15 +1917,17 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table, struct space *child = space_by_name(table_name); if (child == NULL) { diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); - parse_context->rc = SQL_TARANTOOL_ERROR; - parse_context->nErr++; + sql_parser_error(parse_context); + return; + } + char *constraint_name = + sql_name_from_token(parse_context->db, constraint); + if (constraint_name == NULL) { + sql_parser_error(parse_context); return; } - char *constraint_name = sqlNameFromToken(parse_context->db, - constraint); - if (constraint_name != NULL) - vdbe_emit_fk_constraint_drop(parse_context, constraint_name, - child->def->id); + vdbe_emit_fk_constraint_drop(parse_context, constraint_name, + child->def->id); /* * We account changes to row count only if drop of * foreign keys take place in a separate @@ -2192,9 +2189,9 @@ sql_create_index(struct Parse *parse, struct Token *token, */ if (token != NULL) { assert(token->z != NULL); - name = sqlNameFromToken(db, token); + name = sql_name_from_token(db, token); if (name == NULL) - goto exit_create_index; + goto tnt_error; if (sql_space_index_by_name(space, name) != NULL) { if (!if_not_exist) { diag_set(ClientError, ER_INDEX_EXISTS_IN_SPACE, @@ -2205,10 +2202,13 @@ sql_create_index(struct Parse *parse, struct Token *token, } } else { char *constraint_name = NULL; - if (parse->constraintName.z != NULL) + if (parse->constraintName.z != NULL) { constraint_name = - sqlNameFromToken(db, - &parse->constraintName); + sql_name_from_token(db, + &parse->constraintName); + if (constraint_name == NULL) + goto tnt_error; + } /* * This naming is temporary. Now it's not @@ -2246,9 +2246,7 @@ sql_create_index(struct Parse *parse, struct Token *token, if (tbl_name != NULL && space_is_system(space)) { diag_set(ClientError, ER_MODIFY_INDEX, name, def->name, "can't create index on system space"); - parse->nErr++; - parse->rc = SQL_TARANTOOL_ERROR; - goto exit_create_index; + goto tnt_error; } /* @@ -2275,9 +2273,7 @@ sql_create_index(struct Parse *parse, struct Token *token, index = (struct index *) region_alloc(&parse->region, sizeof(*index)); if (index == NULL) { diag_set(OutOfMemory, sizeof(*index), "region", "index"); - parse->rc = SQL_TARANTOOL_ERROR; - parse->nErr++; - goto exit_create_index; + goto tnt_error; } memset(index, 0, sizeof(*index)); @@ -2436,6 +2432,10 @@ sql_create_index(struct Parse *parse, struct Token *token, sql_expr_list_delete(db, col_list); sqlSrcListDelete(db, tbl_name); sqlDbFree(db, name); + return; +tnt_error: + sql_parser_error(parse); + goto exit_create_index; } void @@ -2447,32 +2447,28 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list, struct sql *db = parse_context->db; /* Never called with prior errors. */ assert(parse_context->nErr == 0); - assert(table_token != NULL); - const char *table_name = sqlNameFromToken(db, table_token); - if (db->mallocFailed) { - goto exit_drop_index; - } + const char *table_name = sql_name_from_token(db, table_token); + if (table_name == NULL) + goto tnt_error; sqlVdbeCountChanges(v); assert(index_name_list->nSrc == 1); assert(table_token->n > 0); struct space *space = space_by_name(table_name); if (space == NULL) { - if (!if_exists) { - diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); - sql_parser_error(parse_context); - } - goto exit_drop_index; + if (if_exists) + goto exit_drop_index; + diag_set(ClientError, ER_NO_SUCH_SPACE, table_name); + goto tnt_error; } const char *index_name = index_name_list->a[0].zName; uint32_t index_id = box_index_id_by_name(space->def->id, index_name, strlen(index_name)); if (index_id == BOX_ID_NIL) { - if (!if_exists) { - diag_set(ClientError, ER_NO_SUCH_INDEX_NAME, - index_name, table_name); - sql_parser_error(parse_context); - } - goto exit_drop_index; + if (if_exists) + goto exit_drop_index; + diag_set(ClientError, ER_NO_SUCH_INDEX_NAME, index_name, + table_name); + goto tnt_error; } struct index *index = space_index(space, index_id); assert(index != NULL); @@ -2493,6 +2489,10 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list, exit_drop_index: sqlSrcListDelete(db, index_name_list); sqlDbFree(db, (void *) table_name); + return; +tnt_error: + sql_parser_error(parse_context); + goto exit_drop_index; } /* @@ -2553,7 +2553,7 @@ sql_id_list_append(struct sql *db, struct IdList *list, &list->nId, &i); if (i < 0) goto error; - list->a[i].zName = sqlNameFromToken(db, name_token); + list->a[i].zName = sql_name_from_token(db, name_token); if (list->a[i].zName == NULL) goto error; return list; @@ -2668,7 +2668,13 @@ sql_src_list_append(struct sql *db, struct SrcList *list, list = new_list; } struct SrcList_item *item = &list->a[list->nSrc - 1]; - item->zName = sqlNameFromToken(db, name_token); + if (name_token != NULL) { + item->zName = sql_name_from_token(db, name_token); + if (item->zName == NULL) { + sqlSrcListDelete(db, list); + return NULL; + } + } return list; } @@ -2769,8 +2775,12 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); - if (pAlias->n) { - pItem->zAlias = sqlNameFromToken(db, pAlias); + if (pAlias->n != 0) { + pItem->zAlias = sql_name_from_token(db, pAlias); + if (pItem->zAlias == NULL) { + sql_parser_error(pParse); + goto append_from_error; + } } pItem->pSelect = pSubquery; pItem->pOn = pOn; @@ -2805,8 +2815,15 @@ sqlSrcListIndexedBy(Parse * pParse, SrcList * p, Token * pIndexedBy) */ pItem->fg.notIndexed = 1; } else { - pItem->u1.zIndexedBy = - sqlNameFromToken(pParse->db, pIndexedBy); + if (pIndexedBy->z != NULL) { + pItem->u1.zIndexedBy = + sql_name_from_token(pParse->db, + pIndexedBy); + if (pItem->u1.zIndexedBy == NULL) { + sql_parser_error(pParse); + return; + } + } pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy != 0); } } @@ -2892,11 +2909,12 @@ sql_transaction_rollback(Parse *pParse) void sqlSavepoint(Parse * pParse, int op, Token * pName) { - char *zName = sqlNameFromToken(pParse->db, pName); + struct sql *db = pParse->db; + char *zName = sql_name_from_token(db, pName); if (zName) { Vdbe *v = sqlGetVdbe(pParse); if (!v) { - sqlDbFree(pParse->db, zName); + sqlDbFree(db, zName); return; } if (op == SAVEPOINT_BEGIN && @@ -2906,6 +2924,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName) return; } sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); + } else { + sql_parser_error(pParse); } } @@ -2981,19 +3001,23 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ { sql *db = pParse->db; With *pNew; - char *zName; - /* Check that the CTE name is unique within this WITH clause. If - * not, store an error in the Parse structure. + /* + * Check that the CTE name is unique within this WITH + * clause. If not, store an error in the Parse structure. */ - zName = sqlNameFromToken(pParse->db, pName); - if (zName && pWith) { + char *name = sql_name_from_token(db, pName); + if (name == NULL) { + sql_parser_error(pParse); + return NULL; + } + if (pWith != NULL) { int i; for (i = 0; i < pWith->nCte; i++) { - if (strcmp(zName, pWith->a[i].zName) == 0) { + if (strcmp(name, pWith->a[i].zName) == 0) { sqlErrorMsg(pParse, - "duplicate WITH table name: %s", - zName); + "duplicate WITH table name: %s", + name); } } } @@ -3005,17 +3029,17 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ } else { pNew = sqlDbMallocZero(db, sizeof(*pWith)); } - assert((pNew != 0 && zName != 0) || db->mallocFailed); + assert((pNew != NULL && name != NULL) || db->mallocFailed); if (db->mallocFailed) { sql_expr_list_delete(db, pArglist); sql_select_delete(db, pQuery); - sqlDbFree(db, zName); + sqlDbFree(db, name); pNew = pWith; } else { pNew->a[pNew->nCte].pSelect = pQuery; pNew->a[pNew->nCte].pCols = pArglist; - pNew->a[pNew->nCte].zName = zName; + pNew->a[pNew->nCte].zName = name; pNew->a[pNew->nCte].zCteErr = 0; pNew->nCte++; } diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c index 8b909f2e1..97b716d47 100644 --- a/src/box/sql/pragma.c +++ b/src/box/sql/pragma.c @@ -423,19 +423,25 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ sqlVdbeRunOnlyOnce(v); pParse->nMem = 2; - zLeft = sqlNameFromToken(db, pId); - if (!zLeft) { + if (pId == NULL) { vdbe_emit_pragma_status(pParse); return; } - + zLeft = sql_name_from_token(db, pId); + if (zLeft == NULL) + goto tnt_error; if (minusFlag) { zRight = sqlMPrintf(db, "-%T", pValue); - } else { - zRight = sqlNameFromToken(db, pValue); + } else if (pValue != NULL) { + zRight = sql_name_from_token(db, pValue); + if (zRight == NULL) + goto tnt_error; + } + if (pValue2 != NULL) { + zTable = sql_name_from_token(db, pValue2); + if (zTable == NULL) + goto tnt_error; } - zTable = sqlNameFromToken(db, pValue2); - /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if (pPragma == 0) { @@ -615,4 +621,8 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ sqlDbFree(db, zLeft); sqlDbFree(db, zRight); sqlDbFree(db, zTable); + return; +tnt_error: + sql_parser_error(pParse); + goto pragma_out; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 56ae90a95..b49229b26 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3644,7 +3644,26 @@ int sqlExprCodeExprList(Parse *, ExprList *, int, int, u8); void sqlExprIfTrue(Parse *, Expr *, int, int); void sqlExprIfFalse(Parse *, Expr *, int, int); -char *sqlNameFromToken(sql *, Token *); +/** + * Given a token, return a string that consists of the text of + * that token. Space to hold the returned string is obtained + * from sqlMalloc() and must be freed by the calling function. + * + * Any quotation marks (ex: "name", 'name', [name], or `name`) + * that surround the body of the token are removed. + * + * Tokens are often just pointers into the original SQL text and + * so are not \000 terminated and are not persistent. The returned + * string is \000 terminated and is persistent. + * + * @param db The database connection. + * @param name_token The source token with text. + * @retval Not NULL string pointer on success. + * @retval NULL Otherwise. Diag message is set. + */ +char * +sql_name_from_token(struct sql *db, struct Token *name_token); + int sqlExprCompare(Expr *, Expr *, int); int sqlExprListCompare(ExprList *, ExprList *, int); int sqlExprImpliesExpr(Expr *, Expr *, int); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 20d41e6a1..65218121b 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -82,9 +82,9 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm, goto trigger_cleanup; assert(table->nSrc == 1); - trigger_name = sqlNameFromToken(db, name); + trigger_name = sql_name_from_token(db, name); if (trigger_name == NULL) - goto trigger_cleanup; + goto set_tarantool_error_and_cleanup; if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK) goto trigger_cleanup; -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 4/7] sql: refactor sql_name_from_token to set diag 2019-03-11 15:04 ` Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov The diff in your email has nothing to do with sql_name_from_token. You've not sent a new version of that patch, so I did it below with my 2 comments inlined. > commit b052b1b0a43159ac79320d5dca43a201fffa6ab9 > Author: Kirill Shcherbatov <kshcherbatov@tarantool.org> > Date: Wed Feb 13 15:15:22 2019 +0300 > > sql: rework sqlNameFromToken to set diag > > Refactored sqlNameFromToken routine as sql_name_from_token and > reworked it to use diag_set in case of memory allocation error. > This change is necessary because the sql_name_from_token body has > a sqlNameFromToken call that will be changed in subsequent > patches. 1. Now, reread that paragraph and fix what is wrong. You do not call sqlNameFromToken from sql_name_from_token. > > Part of #3931 > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index d374acb47..1864bd0ad 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -2981,19 +3001,23 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ > { > sql *db = pParse->db; > With *pNew; > - char *zName; > > - /* Check that the CTE name is unique within this WITH clause. If > - * not, store an error in the Parse structure. > + /* > + * Check that the CTE name is unique within this WITH > + * clause. If not, store an error in the Parse structure. > */ > - zName = sqlNameFromToken(pParse->db, pName); > - if (zName && pWith) { > + char *name = sql_name_from_token(db, pName); > + if (name == NULL) { > + sql_parser_error(pParse); > + return NULL; 2. Leak. sqlWithAdd should delete some structures in a case of OOM. Lines 3035 - 3038. Also, that function does not return NULL on an error - it should return the old value. Otherwise you have a second leak in parse.y:1494. > + } > + if (pWith != NULL) { > int i; > for (i = 0; i < pWith->nCte; i++) { > - if (strcmp(zName, pWith->a[i].zName) == 0) { > + if (strcmp(name, pWith->a[i].zName) == 0) { > sqlErrorMsg(pParse, > - "duplicate WITH table name: %s", > - zName); > + "duplicate WITH table name: %s", > + name); > } > } > } ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 4/7] sql: refactor sql_name_from_token to set diag 2019-03-18 19:33 ` Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 0 replies; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy On 18.03.2019 22:33, Vladislav Shpilevoy wrote: > The diff in your email has nothing to do with sql_name_from_token. > You've not sent a new version of that patch, so I did it below > with my 2 comments inlined. > >> commit b052b1b0a43159ac79320d5dca43a201fffa6ab9 >> Author: Kirill Shcherbatov <kshcherbatov@tarantool.org> >> Date: Wed Feb 13 15:15:22 2019 +0300 >> >> sql: rework sqlNameFromToken to set diag >> >> Refactored sqlNameFromToken routine as sql_name_from_token and >> reworked it to use diag_set in case of memory allocation error. >> This change is necessary because the sql_name_from_token body has >> a sqlNameFromToken call that will be changed in subsequent >> patches. > > 1. Now, reread that paragraph and fix what is wrong. You do not call > sqlNameFromToken from sql_name_from_token. sql: rework sqlNameFromToken to set diag Refactored sqlNameFromToken routine as sql_name_from_token and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_name_from_token body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 >> - zName = sqlNameFromToken(pParse->db, pName); >> - if (zName && pWith) { >> + char *name = sql_name_from_token(db, pName); >> + if (name == NULL) { >> + sql_parser_error(pParse); >> + return NULL; > > 2. Leak. sqlWithAdd should delete some structures in a case of OOM. > Lines 3035 - 3038. Also, that function does not return NULL on an > error - it should return the old value. Otherwise you have a second > leak in parse.y:1494. diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 88383b7c5..40bab170d 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -3009,7 +3009,7 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ char *name = sql_name_from_token(db, pName); if (name == NULL) { sql_parser_error(pParse); - return NULL; + goto oom_error; } if (pWith != NULL) { int i; @@ -3032,6 +3032,7 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ assert((pNew != NULL && name != NULL) || db->mallocFailed); if (db->mallocFailed) { +oom_error: sql_expr_list_delete(db, pArglist); sql_select_delete(db, pQuery); sqlDbFree(db, name); ====================================================== Refactored sqlNameFromToken routine as sql_name_from_token and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_name_from_token body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/alter.c | 4 +- src/box/sql/analyze.c | 44 +++++++----- src/box/sql/build.c | 163 +++++++++++++++++++++++++----------------- src/box/sql/pragma.c | 26 +++++-- src/box/sql/sqlInt.h | 21 +++++- src/box/sql/trigger.c | 4 +- 6 files changed, 165 insertions(+), 97 deletions(-) diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c index bd9b034c4..bf4f03a17 100644 --- a/src/box/sql/alter.c +++ b/src/box/sql/alter.c @@ -43,9 +43,9 @@ sql_alter_table_rename(struct Parse *parse, struct SrcList *src_tab, { assert(src_tab->nSrc == 1); struct sql *db = parse->db; - char *new_name = sqlNameFromToken(db, new_name_tk); + char *new_name = sql_name_from_token(db, new_name_tk); if (new_name == NULL) - goto exit_rename_table; + goto tnt_error; /* Check that new name isn't occupied by another table. */ if (space_by_name(new_name) != NULL) { diag_set(ClientError, ER_SPACE_EXISTS, new_name); diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 6ea598c13..86b13ffad 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -1110,34 +1110,40 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space) void sqlAnalyze(Parse * pParse, Token * pName) { - sql *db = pParse->db; + char *name = NULL; + struct sql *db = pParse->db; + struct Vdbe *v = sqlGetVdbe(pParse); + if (v == NULL) + return; if (pName == NULL) { /* Form 1: Analyze everything */ sql_analyze_database(pParse); } else { /* Form 2: Analyze table named */ - char *z = sqlNameFromToken(db, pName); - if (z != NULL) { - struct space *sp = space_by_name(z); - if (sp != NULL) { - if (sp->def->opts.is_view) { - diag_set(ClientError, - ER_SQL_ANALYZE_ARGUMENT, - sp->def->name); - pParse->is_aborted = true; - } else { - vdbe_emit_analyze_table(pParse, sp); - } - } else { - diag_set(ClientError, ER_NO_SUCH_SPACE, z); + char *z = sql_name_from_token(db, pName); + if (z == NULL) { + pParse->is_aborted = true; + goto cleanup; + } + struct space *sp = space_by_name(z); + if (sp != NULL) { + if (sp->def->opts.is_view) { + diag_set(ClientError, ER_SQL_ANALYZE_ARGUMENT, + sp->def->name); pParse->is_aborted = true; + } else { + vdbe_emit_analyze_table(pParse, sp); } - sqlDbFree(db, z); + } else { + diag_set(ClientError, ER_NO_SUCH_SPACE, z); + pParse->is_aborted = true; } + sqlDbFree(db, z); } - Vdbe *v = sqlGetVdbe(pParse); - if (v != NULL) - sqlVdbeAddOp0(v, OP_Expire); + sqlVdbeAddOp0(v, OP_Expire); +cleanup: + sqlDbFree(db, name); + return; } ssize_t diff --git a/src/box/sql/build.c b/src/box/sql/build.c index dae582d1f..f52cfd7dd 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -229,30 +229,18 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column) return false; } -/* - * Given a token, return a string that consists of the text of that - * token. Space to hold the returned string - * is obtained from sqlMalloc() and must be freed by the calling - * function. - * - * Any quotation marks (ex: "name", 'name', [name], or `name`) that - * surround the body of the token are removed. - * - * Tokens are often just pointers into the original SQL text and so - * are not \000 terminated and are not persistent. The returned string - * is \000 terminated and is persistent. - */ char * -sqlNameFromToken(sql * db, Token * pName) +sql_name_from_token(struct sql *db, struct Token *name_token) { - char *zName; - if (pName) { - zName = sqlDbStrNDup(db, (char *)pName->z, pName->n); - sqlNormalizeName(zName); - } else { - zName = 0; + assert(name_token != NULL && name_token->z != NULL); + char *name = sqlDbStrNDup(db, name_token->z, name_token->n); + if (name == NULL) { + diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", + "name"); + return NULL; } - return zName; + sqlNormalizeName(name); + return name; } /* @@ -329,11 +317,13 @@ sqlStartTable(Parse *pParse, Token *pName, int noErr) goto cleanup; sqlVdbeCountChanges(v); - zName = sqlNameFromToken(db, pName); + zName = sql_name_from_token(db, pName); + if (zName == NULL) { + pParse->is_aborted = true; + goto cleanup; + } pParse->sNameToken = *pName; - if (zName == 0) - return; if (sqlCheckIdentifierName(pParse, zName) != SQL_OK) goto cleanup; @@ -700,11 +690,13 @@ sqlAddCollateType(Parse * pParse, Token * pToken) struct space *space = pParse->new_space; uint32_t i = space->def->field_count - 1; sql *db = pParse->db; - char *zColl = sqlNameFromToken(db, pToken); - if (!zColl) + char *coll_name = sql_name_from_token(db, pToken); + if (coll_name == NULL) { + pParse->is_aborted = true; return; + } uint32_t *coll_id = &space->def->fields[i].coll_id; - if (sql_get_coll_seq(pParse, zColl, coll_id) != NULL) { + if (sql_get_coll_seq(pParse, coll_name, coll_id) != NULL) { /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>", * then an index may have been created on this column before the * collation type was added. Correct this if it is the case. @@ -718,7 +710,7 @@ sqlAddCollateType(Parse * pParse, Token * pToken) } } } - sqlDbFree(db, zColl); + sqlDbFree(db, coll_name); } struct coll * @@ -1733,10 +1725,9 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, memset(fk_parse, 0, sizeof(*fk_parse)); rlist_add_entry(&parse_context->new_fk_constraint, fk_parse, link); } - assert(parent != NULL); - parent_name = sqlNameFromToken(db, parent); + parent_name = sql_name_from_token(db, parent); if (parent_name == NULL) - goto exit_create_fk; + goto tnt_error; /* * Within ALTER TABLE ADD CONSTRAINT FK also can be * self-referenced, but in this case parent (which is @@ -1769,15 +1760,19 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child, sqlMPrintf(db, "FK_CONSTRAINT_%d_%s", ++parse_context->fk_constraint_count, space->def->name); + if (constraint_name == NULL) + goto exit_create_fk; } else { struct Token *cnstr_nm = &parse_context->constraintName; - constraint_name = sqlNameFromToken(db, cnstr_nm); + constraint_name = sql_name_from_token(db, cnstr_nm); + if (constraint_name == NULL) + goto tnt_error; } } else { - constraint_name = sqlNameFromToken(db, constraint); + constraint_name = sql_name_from_token(db, constraint); + if (constraint_name == NULL) + goto tnt_error; } - if (constraint_name == NULL) - goto exit_create_fk; const char *error_msg = "number of columns in foreign key does not " "match the number of columns in the primary " "index of referenced table"; @@ -1908,11 +1903,14 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table, parse_context->is_aborted = true; return; } - char *constraint_name = sqlNameFromToken(parse_context->db, - constraint); - if (constraint_name != NULL) - vdbe_emit_fk_constraint_drop(parse_context, constraint_name, - child->def->id); + char *constraint_name = + sql_name_from_token(parse_context->db, constraint); + if (constraint_name == NULL) { + parse_context->is_aborted = true; + return; + } + vdbe_emit_fk_constraint_drop(parse_context, constraint_name, + child->def->id); /* * We account changes to row count only if drop of * foreign keys take place in a separate @@ -2172,9 +2170,11 @@ sql_create_index(struct Parse *parse, struct Token *token, */ if (token != NULL) { assert(token->z != NULL); - name = sqlNameFromToken(db, token); - if (name == NULL) + name = sql_name_from_token(db, token); + if (name == NULL) { + parse->is_aborted = true; goto exit_create_index; + } if (sql_space_index_by_name(space, name) != NULL) { if (!if_not_exist) { diag_set(ClientError, ER_INDEX_EXISTS_IN_SPACE, @@ -2185,10 +2185,15 @@ sql_create_index(struct Parse *parse, struct Token *token, } } else { char *constraint_name = NULL; - if (parse->constraintName.z != NULL) + if (parse->constraintName.z != NULL) { constraint_name = - sqlNameFromToken(db, - &parse->constraintName); + sql_name_from_token(db, + &parse->constraintName); + if (constraint_name == NULL) { + parse->is_aborted = true; + goto exit_create_index; + } + } /* * This naming is temporary. Now it's not @@ -2426,8 +2431,9 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list, /* Never called with prior errors. */ assert(!parse_context->is_aborted); assert(table_token != NULL); - const char *table_name = sqlNameFromToken(db, table_token); - if (db->mallocFailed) { + const char *table_name = sql_name_from_token(db, table_token); + if (table_name == NULL) { + parse_context->is_aborted = true; goto exit_drop_index; } sqlVdbeCountChanges(v); @@ -2531,7 +2537,7 @@ sql_id_list_append(struct sql *db, struct IdList *list, &list->nId, &i); if (i < 0) goto error; - list->a[i].zName = sqlNameFromToken(db, name_token); + list->a[i].zName = sql_name_from_token(db, name_token); if (list->a[i].zName == NULL) goto error; return list; @@ -2646,7 +2652,13 @@ sql_src_list_append(struct sql *db, struct SrcList *list, list = new_list; } struct SrcList_item *item = &list->a[list->nSrc - 1]; - item->zName = sqlNameFromToken(db, name_token); + if (name_token != NULL) { + item->zName = sql_name_from_token(db, name_token); + if (item->zName == NULL) { + sqlSrcListDelete(db, list); + return NULL; + } + } return list; } @@ -2747,8 +2759,12 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); - if (pAlias->n) { - pItem->zAlias = sqlNameFromToken(db, pAlias); + if (pAlias->n != 0) { + pItem->zAlias = sql_name_from_token(db, pAlias); + if (pItem->zAlias == NULL) { + pParse->is_aborted = true; + goto append_from_error; + } } pItem->pSelect = pSubquery; pItem->pOn = pOn; @@ -2782,8 +2798,15 @@ sqlSrcListIndexedBy(Parse * pParse, SrcList * p, Token * pIndexedBy) */ pItem->fg.notIndexed = 1; } else { - pItem->u1.zIndexedBy = - sqlNameFromToken(pParse->db, pIndexedBy); + if (pIndexedBy->z != NULL) { + pItem->u1.zIndexedBy = + sql_name_from_token(pParse->db, + pIndexedBy); + if (pItem->u1.zIndexedBy == NULL) { + pParse->is_aborted = true; + return; + } + } pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy != 0); } } @@ -2869,11 +2892,12 @@ sql_transaction_rollback(Parse *pParse) void sqlSavepoint(Parse * pParse, int op, Token * pName) { - char *zName = sqlNameFromToken(pParse->db, pName); + struct sql *db = pParse->db; + char *zName = sql_name_from_token(db, pName); if (zName) { Vdbe *v = sqlGetVdbe(pParse); if (!v) { - sqlDbFree(pParse->db, zName); + sqlDbFree(db, zName); return; } if (op == SAVEPOINT_BEGIN && @@ -2883,6 +2907,8 @@ sqlSavepoint(Parse * pParse, int op, Token * pName) return; } sqlVdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); + } else { + pParse->is_aborted = true; } } @@ -2958,19 +2984,23 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ { sql *db = pParse->db; With *pNew; - char *zName; - /* Check that the CTE name is unique within this WITH clause. If - * not, store an error in the Parse structure. + /* + * Check that the CTE name is unique within this WITH + * clause. If not, store an error in the Parse structure. */ - zName = sqlNameFromToken(pParse->db, pName); - if (zName && pWith) { + char *name = sql_name_from_token(db, pName); + if (name == NULL) { + pParse->is_aborted = true; + goto oom_error; + } + if (pWith != NULL) { int i; for (i = 0; i < pWith->nCte; i++) { - if (strcmp(zName, pWith->a[i].zName) == 0) { + if (strcmp(name, pWith->a[i].zName) == 0) { sqlErrorMsg(pParse, - "duplicate WITH table name: %s", - zName); + "duplicate WITH table name: %s", + name); } } } @@ -2982,17 +3012,18 @@ sqlWithAdd(Parse * pParse, /* Parsing context */ } else { pNew = sqlDbMallocZero(db, sizeof(*pWith)); } - assert((pNew != 0 && zName != 0) || db->mallocFailed); + assert((pNew != NULL && name != NULL) || db->mallocFailed); if (db->mallocFailed) { +oom_error: sql_expr_list_delete(db, pArglist); sql_select_delete(db, pQuery); - sqlDbFree(db, zName); + sqlDbFree(db, name); pNew = pWith; } else { pNew->a[pNew->nCte].pSelect = pQuery; pNew->a[pNew->nCte].pCols = pArglist; - pNew->a[pNew->nCte].zName = zName; + pNew->a[pNew->nCte].zName = name; pNew->a[pNew->nCte].zCteErr = 0; pNew->nCte++; } diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c index 2b9c9b441..53524b617 100644 --- a/src/box/sql/pragma.c +++ b/src/box/sql/pragma.c @@ -423,19 +423,31 @@ sqlPragma(Parse * pParse, Token * pId, /* First part of [schema.]id field */ sqlVdbeRunOnlyOnce(v); pParse->nMem = 2; - zLeft = sqlNameFromToken(db, pId); - if (!zLeft) { + if (pId == NULL) { vdbe_emit_pragma_status(pParse); return; } - + zLeft = sql_name_from_token(db, pId); + if (zLeft == NULL) { + pParse->is_aborted = true; + goto pragma_out; + } if (minusFlag) { zRight = sqlMPrintf(db, "-%T", pValue); - } else { - zRight = sqlNameFromToken(db, pValue); + } else if (pValue != NULL) { + zRight = sql_name_from_token(db, pValue); + if (zRight == NULL) { + pParse->is_aborted = true; + goto pragma_out; + } + } + if (pValue2 != NULL) { + zTable = sql_name_from_token(db, pValue2); + if (zTable == NULL) { + pParse->is_aborted = true; + goto pragma_out; + } } - zTable = sqlNameFromToken(db, pValue2); - /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if (pPragma == 0) { diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 8f56f3e63..3aedde4cc 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3666,7 +3666,26 @@ int sqlExprCodeExprList(Parse *, ExprList *, int, int, u8); void sqlExprIfTrue(Parse *, Expr *, int, int); void sqlExprIfFalse(Parse *, Expr *, int, int); -char *sqlNameFromToken(sql *, Token *); +/** + * Given a token, return a string that consists of the text of + * that token. Space to hold the returned string is obtained + * from sqlMalloc() and must be freed by the calling function. + * + * Any quotation marks (ex: "name", 'name', [name], or `name`) + * that surround the body of the token are removed. + * + * Tokens are often just pointers into the original SQL text and + * so are not \000 terminated and are not persistent. The returned + * string is \000 terminated and is persistent. + * + * @param db The database connection. + * @param name_token The source token with text. + * @retval Not NULL String pointer on success. + * @retval NULL Otherwise. Diag message is set. + */ +char * +sql_name_from_token(struct sql *db, struct Token *name_token); + int sqlExprCompare(Expr *, Expr *, int); int sqlExprListCompare(ExprList *, ExprList *, int); int sqlExprImpliesExpr(Expr *, Expr *, int); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index b1f5033c4..dc30c5f2c 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -82,9 +82,9 @@ sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm, goto trigger_cleanup; assert(table->nSrc == 1); - trigger_name = sqlNameFromToken(db, name); + trigger_name = sql_name_from_token(db, name); if (trigger_name == NULL) - goto trigger_cleanup; + goto set_tarantool_error_and_cleanup; if (sqlCheckIdentifierName(parse, trigger_name) != SQL_OK) goto trigger_cleanup; -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (3 preceding siblings ...) 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 4/7] sql: refactor sql_name_from_token " Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 6/7] sql: refactor sql_expr_create " Kirill Shcherbatov ` (3 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_trigger_step_allocate routine to use diag_set in case of memory allocation error. Also performed some additional name refactoring in adjacent places. This change is necessary because the sql_trigger_step_allocate body has a sqlNameFromToken call that will be changed in subsequent patches. Needed for #3931 --- src/box/sql/parse.y | 30 +++++-- src/box/sql/sqlInt.h | 73 +++++++++++++++-- src/box/sql/trigger.c | 187 +++++++++++++++++------------------------- 3 files changed, 162 insertions(+), 128 deletions(-) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index f56b831e5..db03714a9 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1423,20 +1423,34 @@ tridxby ::= NOT INDEXED. { %destructor trigger_cmd {sqlDeleteTriggerStep(pParse->db, $$);} // UPDATE trigger_cmd(A) ::= - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). - {A = sqlTriggerUpdateStep(pParse->db, &X, Y, Z, R);} + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { + A = sql_trigger_update_step(pParse->db, &X, Y, Z, R); + if (A == NULL) + sql_parser_error(pParse); + } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). - {A = sqlTriggerInsertStep(pParse->db, &X, F, S, R);/*A-overwrites-R*/} +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). { + /*A-overwrites-R. */ + A = sql_trigger_insert_step(pParse->db, &X, F, S, R); + if (A == NULL) + sql_parser_error(pParse); +} // DELETE -trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). - {A = sqlTriggerDeleteStep(pParse->db, &X, Y);} +trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). { + A = sql_trigger_delete_step(pParse->db, &X, Y); + if (A == NULL) + sql_parser_error(pParse); +} // SELECT -trigger_cmd(A) ::= select(X). - {A = sqlTriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} +trigger_cmd(A) ::= select(X). { + /* A-overwrites-X. */ + A = sql_trigger_select_step(pParse->db, X); + if (A == NULL) + sql_parser_error(pParse); +} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 10943d3c8..531445f33 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4146,12 +4146,73 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger, int ignore_jump); void sqlDeleteTriggerStep(sql *, TriggerStep *); -TriggerStep *sqlTriggerSelectStep(sql *, Select *); -TriggerStep *sqlTriggerInsertStep(sql *, Token *, IdList *, - Select *, u8); -TriggerStep *sqlTriggerUpdateStep(sql *, Token *, ExprList *, Expr *, - u8); -TriggerStep *sqlTriggerDeleteStep(sql *, Token *, Expr *); + +/** + * Turn a SELECT statement (that the pSelect parameter points to) + * into a trigger step. Return a pointer to a TriggerStep + * structure. + * The parser calls this routine when it finds a SELECT statement in + * body of a TRIGGER. + * @param db The database connection. + * @param select The SELECT statement to process. + * @retval not NULL TriggerStep object on success. + * @retval NULL otherwise. + */ +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select); + +/** + * Build a trigger step out of an INSERT statement. Return a + * pointer to the new trigger step. + * The parser calls this routine when it sees an INSERT inside the + * body of a trigger. + * @param db The database connection. + * @param table_name Name of the table into which we insert. + * @param column_list List of columns in table to insert into. + * @param select The SELECT statement that supplies values. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval not NULL TriggerStep object on success. + * @retval NULL otherwise. + */ +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + u8 orconf); + +/* + * Construct a trigger step that implements an UPDATE statement + * and return a pointer to that trigger step. The parser calls + * this routine when it sees an UPDATE statement inside the body + * of a CREATE TRIGGER. + * @param db The database connection. + * @param table_name Name of the table to be updated. + * @param new_list The SET clause: list of column and new values. + * @param where The WHERE clause. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval not NULL TriggerStep object on success. + * @retval NULL otherwise. + */ +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf); + +/* + * Construct a trigger step that implements a DELETE statement and + * return a pointer to that trigger step. The parser calls this + * routine when it sees a DELETE statement inside the body of a + * CREATE TRIGGER. + * @param db The database connection. + * @param table_name The table from which rows are deleted. + * @param where The WHERE clause. + * @retval not NULL TriggerStep object on success. + * @retval NULL otherwise. + */ +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token * table_name, + struct Expr *where); /** * Triggers may access values stored in the old.* or new.* diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 65218121b..af62a5eff 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -249,142 +249,101 @@ cleanup: sqlDeleteTriggerStep(db, step_list); } -/* - * Turn a SELECT statement (that the pSelect parameter points to) into - * a trigger step. Return a pointer to a TriggerStep structure. - * - * The parser calls this routine when it finds a SELECT statement in - * body of a TRIGGER. - */ -TriggerStep * -sqlTriggerSelectStep(sql * db, Select * pSelect) +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select) { - TriggerStep *pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep)); - if (pTriggerStep == 0) { - sql_select_delete(db, pSelect); - return 0; + struct TriggerStep *trigger_step = + sqlDbMallocZero(db, sizeof(TriggerStep)); + if (trigger_step == NULL) { + sql_select_delete(db, select); + diag_set(OutOfMemory, sizeof(TriggerStep), "sqlDbMallocZero", + "trigger_step"); + return NULL; } - pTriggerStep->op = TK_SELECT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; - return pTriggerStep; + trigger_step->op = TK_SELECT; + trigger_step->pSelect = select; + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; + return trigger_step; } /* * Allocate space to hold a new trigger step. The allocated space - * holds both the TriggerStep object and the TriggerStep.target.z string. - * - * If an OOM error occurs, NULL is returned and db->mallocFailed is set. + * holds both the TriggerStep object and the TriggerStep.target.z + * string. + * @param db The database connection. + * @param op Trigger opcode. + * @param target_name The target name token. + * @retval not NULL TriggerStep object on success, NULL otherwise. */ -static TriggerStep * -triggerStepAllocate(sql * db, /* Database connection */ - u8 op, /* Trigger opcode */ - Token * pName /* The target name */ - ) +static struct TriggerStep * +sql_trigger_step_allocate(struct sql *db, u8 op, struct Token *target_name) { - TriggerStep *pTriggerStep; - - pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); - if (pTriggerStep) { - char *z = (char *)&pTriggerStep[1]; - memcpy(z, pName->z, pName->n); - sqlNormalizeName(z); - pTriggerStep->zTarget = z; - pTriggerStep->op = op; + int size = sizeof(TriggerStep) + target_name->n + 1; + struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); + if (trigger_step == NULL) { + diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + return NULL; } - return pTriggerStep; + char *z = (char *)&trigger_step[1]; + memcpy(z, target_name->z, target_name->n); + sqlNormalizeName(z); + trigger_step->zTarget = z; + trigger_step->op = op; + return trigger_step; } -/* - * Build a trigger step out of an INSERT statement. Return a pointer - * to the new trigger step. - * - * The parser calls this routine when it sees an INSERT inside the - * body of a trigger. - */ -TriggerStep * -sqlTriggerInsertStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table into which we insert */ - IdList * pColumn, /* List of columns in pTableName to insert into */ - Select * pSelect, /* A SELECT statement that supplies values */ - u8 orconf /* The conflict algorithm - * (ON_CONFLICT_ACTION_ABORT, _REPLACE, - * etc.) - */ - ) +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + u8 orconf) { - TriggerStep *pTriggerStep; - - assert(pSelect != 0 || db->mallocFailed); - - pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); - if (pTriggerStep) { - pTriggerStep->pSelect = - sqlSelectDup(db, pSelect, EXPRDUP_REDUCE); - pTriggerStep->pIdList = pColumn; - pTriggerStep->orconf = orconf; + assert(select != 0 || db->mallocFailed); + struct TriggerStep *trigger_step = + sql_trigger_step_allocate(db, TK_INSERT, table_name); + if (trigger_step != NULL) { + trigger_step->pSelect = + sqlSelectDup(db, select, EXPRDUP_REDUCE); + trigger_step->pIdList = column_list; + trigger_step->orconf = orconf; } else { - sqlIdListDelete(db, pColumn); + sqlIdListDelete(db, column_list); } - sql_select_delete(db, pSelect); - - return pTriggerStep; + sql_select_delete(db, select); + return trigger_step; } -/* - * Construct a trigger step that implements an UPDATE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees an UPDATE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerUpdateStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table to be updated */ - ExprList * pEList, /* The SET clause: list of column and new values */ - Expr * pWhere, /* The WHERE clause */ - u8 orconf /* The conflict algorithm. - * (ON_CONFLICT_ACTION_ABORT, _IGNORE, - * etc) - */ - ) +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName); - if (pTriggerStep) { - pTriggerStep->pExprList = - sql_expr_list_dup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = orconf; + struct TriggerStep *trigger_step = + sql_trigger_step_allocate(db, TK_UPDATE, table_name); + if (trigger_step != NULL) { + trigger_step->pExprList = + sql_expr_list_dup(db, new_list, EXPRDUP_REDUCE); + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = orconf; } - sql_expr_list_delete(db, pEList); - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_list_delete(db, new_list); + sql_expr_delete(db, where, false); + return trigger_step; } -/* - * Construct a trigger step that implements a DELETE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees a DELETE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerDeleteStep(sql * db, /* Database connection */ - Token * pTableName, /* The table from which rows are deleted */ - Expr * pWhere /* The WHERE clause */ - ) +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token * table_name, + struct Expr *where) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName); - if (pTriggerStep) { - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; + struct TriggerStep *trigger_step = + sql_trigger_step_allocate(db, TK_DELETE, table_name); + if (trigger_step != NULL) { + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; } - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_delete(db, where, false); + return trigger_step; } void -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate " Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_trigger_step_allocate routine to use diag_set in > case of memory allocation error. Also performed some additional > name refactoring in adjacent places. > This change is necessary because the sql_trigger_step_allocate > body has a sqlNameFromToken call that will be changed in > subsequent patches. But you have changed it in the previous one ... . And here a function with such name already does not exist. The same about the next commit. > > Needed for #3931 > --- ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > On 27/02/2019 14:13, Kirill Shcherbatov wrote: >> Refactored sql_trigger_step_allocate routine to use diag_set in >> case of memory allocation error. Also performed some additional >> name refactoring in adjacent places. >> This change is necessary because the sql_trigger_step_allocate >> body has a sqlNameFromToken call that will be changed in >> subsequent patches. > > But you have changed it in the previous one ... . And here a > function with such name already does not exist. > > The same about the next commit. Hope this accounted in the new commit message. ==================================================== Refactored triggerSterAllocate routine as sql_trigger_step_new and reworked it to set diag_set in case of memory allocation error. Also performed some additional name refactoring in adjacent places. This change is necessary because the sql_trigger_step_allocate body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/parse.y | 30 +++++-- src/box/sql/sqlInt.h | 77 +++++++++++++++-- src/box/sql/trigger.c | 187 +++++++++++++++++------------------------- 3 files changed, 167 insertions(+), 127 deletions(-) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 8a8f1b82f..f0388b481 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1415,20 +1415,34 @@ tridxby ::= NOT INDEXED. { %destructor trigger_cmd {sqlDeleteTriggerStep(pParse->db, $$);} // UPDATE trigger_cmd(A) ::= - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). - {A = sqlTriggerUpdateStep(pParse->db, &X, Y, Z, R);} + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { + A = sql_trigger_update_step(pParse->db, &X, Y, Z, R); + if (A == NULL) + sql_parser_error(pParse); + } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). - {A = sqlTriggerInsertStep(pParse->db, &X, F, S, R);/*A-overwrites-R*/} +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). { + /*A-overwrites-R. */ + A = sql_trigger_insert_step(pParse->db, &X, F, S, R); + if (A == NULL) + sql_parser_error(pParse); +} // DELETE -trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). - {A = sqlTriggerDeleteStep(pParse->db, &X, Y);} +trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). { + A = sql_trigger_delete_step(pParse->db, &X, Y); + if (A == NULL) + sql_parser_error(pParse); +} // SELECT -trigger_cmd(A) ::= select(X). - {A = sqlTriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} +trigger_cmd(A) ::= select(X). { + /* A-overwrites-X. */ + A = sql_trigger_select_step(pParse->db, X); + if (A == NULL) + sql_parser_error(pParse); +} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index b49229b26..4e6c5717b 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4087,12 +4087,77 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger, int ignore_jump); void sqlDeleteTriggerStep(sql *, TriggerStep *); -TriggerStep *sqlTriggerSelectStep(sql *, Select *); -TriggerStep *sqlTriggerInsertStep(sql *, Token *, IdList *, - Select *, u8); -TriggerStep *sqlTriggerUpdateStep(sql *, Token *, ExprList *, Expr *, - u8); -TriggerStep *sqlTriggerDeleteStep(sql *, Token *, Expr *); + +/** + * Turn a SELECT statement (that the pSelect parameter points to) + * into a trigger step. Return a pointer to a TriggerStep + * structure. + * The parser calls this routine when it finds a SELECT statement + * in body of a TRIGGER. + * + * @param db The database connection. + * @param select The SELECT statement to process. + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select); + +/** + * Build a trigger step out of an INSERT statement. Return a + * pointer to the new trigger step. + * The parser calls this routine when it sees an INSERT inside the + * body of a trigger. + * + * @param db The database connection. + * @param table_name Name of the table into which we insert. + * @param column_list List of columns in table to insert into. + * @param select The SELECT statement that supplies values. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + u8 orconf); + +/** + * Construct a trigger step that implements an UPDATE statement + * and return a pointer to that trigger step. The parser calls + * this routine when it sees an UPDATE statement inside the body + * of a CREATE TRIGGER. + * + * @param db The database connection. + * @param table_name Name of the table to be updated. + * @param new_list The SET clause: list of column and new values. + * @param where The WHERE clause. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. + */ +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf); + +/** + * Construct a trigger step that implements a DELETE statement and + * return a pointer to that trigger step. The parser calls this + * routine when it sees a DELETE statement inside the body of a + * CREATE TRIGGER. + * + * @param db The database connection. + * @param table_name The table from which rows are deleted. + * @param where The WHERE clause. + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. + */ +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token * table_name, + struct Expr *where); /** * Triggers may access values stored in the old.* or new.* diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 65218121b..96c3a4a2f 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -249,142 +249,103 @@ cleanup: sqlDeleteTriggerStep(db, step_list); } -/* - * Turn a SELECT statement (that the pSelect parameter points to) into - * a trigger step. Return a pointer to a TriggerStep structure. - * - * The parser calls this routine when it finds a SELECT statement in - * body of a TRIGGER. - */ -TriggerStep * -sqlTriggerSelectStep(sql * db, Select * pSelect) +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select) { - TriggerStep *pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep)); - if (pTriggerStep == 0) { - sql_select_delete(db, pSelect); - return 0; + struct TriggerStep *trigger_step = + sqlDbMallocZero(db, sizeof(TriggerStep)); + if (trigger_step == NULL) { + sql_select_delete(db, select); + diag_set(OutOfMemory, sizeof(TriggerStep), "sqlDbMallocZero", + "trigger_step"); + return NULL; } - pTriggerStep->op = TK_SELECT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; - return pTriggerStep; + trigger_step->op = TK_SELECT; + trigger_step->pSelect = select; + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; + return trigger_step; } /* * Allocate space to hold a new trigger step. The allocated space - * holds both the TriggerStep object and the TriggerStep.target.z string. + * holds both the TriggerStep object and the TriggerStep.target.z + * string. * - * If an OOM error occurs, NULL is returned and db->mallocFailed is set. + * @param db The database connection. + * @param op Trigger opcode. + * @param target_name The target name token. + * @retval Not NULL TriggerStep object on success. + * @retval NULL otherwise. The diag message is set. */ -static TriggerStep * -triggerStepAllocate(sql * db, /* Database connection */ - u8 op, /* Trigger opcode */ - Token * pName /* The target name */ - ) +static struct TriggerStep * +sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) { - TriggerStep *pTriggerStep; - - pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); - if (pTriggerStep) { - char *z = (char *)&pTriggerStep[1]; - memcpy(z, pName->z, pName->n); - sqlNormalizeName(z); - pTriggerStep->zTarget = z; - pTriggerStep->op = op; + int size = sizeof(TriggerStep) + target_name->n + 1; + struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); + if (trigger_step == NULL) { + diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + return NULL; } - return pTriggerStep; + char *z = (char *)&trigger_step[1]; + memcpy(z, target_name->z, target_name->n); + sqlNormalizeName(z); + trigger_step->zTarget = z; + trigger_step->op = op; + return trigger_step; } -/* - * Build a trigger step out of an INSERT statement. Return a pointer - * to the new trigger step. - * - * The parser calls this routine when it sees an INSERT inside the - * body of a trigger. - */ -TriggerStep * -sqlTriggerInsertStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table into which we insert */ - IdList * pColumn, /* List of columns in pTableName to insert into */ - Select * pSelect, /* A SELECT statement that supplies values */ - u8 orconf /* The conflict algorithm - * (ON_CONFLICT_ACTION_ABORT, _REPLACE, - * etc.) - */ - ) +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + u8 orconf) { - TriggerStep *pTriggerStep; - - assert(pSelect != 0 || db->mallocFailed); - - pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); - if (pTriggerStep) { - pTriggerStep->pSelect = - sqlSelectDup(db, pSelect, EXPRDUP_REDUCE); - pTriggerStep->pIdList = pColumn; - pTriggerStep->orconf = orconf; + assert(select != 0 || db->mallocFailed); + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_INSERT, table_name); + if (trigger_step != NULL) { + trigger_step->pSelect = + sqlSelectDup(db, select, EXPRDUP_REDUCE); + trigger_step->pIdList = column_list; + trigger_step->orconf = orconf; } else { - sqlIdListDelete(db, pColumn); + sqlIdListDelete(db, column_list); } - sql_select_delete(db, pSelect); - - return pTriggerStep; + sql_select_delete(db, select); + return trigger_step; } -/* - * Construct a trigger step that implements an UPDATE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees an UPDATE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerUpdateStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table to be updated */ - ExprList * pEList, /* The SET clause: list of column and new values */ - Expr * pWhere, /* The WHERE clause */ - u8 orconf /* The conflict algorithm. - * (ON_CONFLICT_ACTION_ABORT, _IGNORE, - * etc) - */ - ) +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName); - if (pTriggerStep) { - pTriggerStep->pExprList = - sql_expr_list_dup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = orconf; + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_UPDATE, table_name); + if (trigger_step != NULL) { + trigger_step->pExprList = + sql_expr_list_dup(db, new_list, EXPRDUP_REDUCE); + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = orconf; } - sql_expr_list_delete(db, pEList); - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_list_delete(db, new_list); + sql_expr_delete(db, where, false); + return trigger_step; } -/* - * Construct a trigger step that implements a DELETE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees a DELETE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerDeleteStep(sql * db, /* Database connection */ - Token * pTableName, /* The table from which rows are deleted */ - Expr * pWhere /* The WHERE clause */ - ) +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token * table_name, + struct Expr *where) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName); - if (pTriggerStep) { - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_DELETE, table_name); + if (trigger_step != NULL) { + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; } - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_delete(db, where, false); + return trigger_step; } void -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-03-11 15:04 ` Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes. See 6 comments below. > > Part of #3931 > --- > src/box/sql/parse.y | 30 +++++-- > src/box/sql/sqlInt.h | 77 +++++++++++++++-- > src/box/sql/trigger.c | 187 +++++++++++++++++------------------------- > 3 files changed, 167 insertions(+), 127 deletions(-) > > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > index 8a8f1b82f..f0388b481 100644 > --- a/src/box/sql/parse.y > +++ b/src/box/sql/parse.y > @@ -1415,20 +1415,34 @@ tridxby ::= NOT INDEXED. { > %destructor trigger_cmd {sqlDeleteTriggerStep(pParse->db, $$);} > // UPDATE > trigger_cmd(A) ::= > - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). > - {A = sqlTriggerUpdateStep(pParse->db, &X, Y, Z, R);} > + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { > + A = sql_trigger_update_step(pParse->db, &X, Y, Z, R); > + if (A == NULL) > + sql_parser_error(pParse); 1. Why do not you return after an error here and in other places? > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index b49229b26..4e6c5717b 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -4087,12 +4087,77 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger, > int ignore_jump); > > void sqlDeleteTriggerStep(sql *, TriggerStep *); > -TriggerStep *sqlTriggerSelectStep(sql *, Select *); > -TriggerStep *sqlTriggerInsertStep(sql *, Token *, IdList *, > - Select *, u8); > -TriggerStep *sqlTriggerUpdateStep(sql *, Token *, ExprList *, Expr *, > - u8); > -TriggerStep *sqlTriggerDeleteStep(sql *, Token *, Expr *); > + > +/** > + * Turn a SELECT statement (that the pSelect parameter points to) 2. Again old names. > + * into a trigger step. Return a pointer to a TriggerStep > + * structure. 3. In that case a type of return value is strictly determined thanks to C language. Not sure if that sentence makes a sense. The same about other places. > + * The parser calls this routine when it finds a SELECT statement > + * in body of a TRIGGER. > + * > + * @param db The database connection. > + * @param select The SELECT statement to process. > + * @retval Not NULL TriggerStep object on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct TriggerStep * > +sql_trigger_select_step(struct sql *db, struct Select *select); > + > +/** > + * Build a trigger step out of an INSERT statement. Return a > + * pointer to the new trigger step. > + * The parser calls this routine when it sees an INSERT inside the > + * body of a trigger. > + * > + * @param db The database connection. > + * @param table_name Name of the table into which we insert. > + * @param column_list List of columns in table to insert into. > + * @param select The SELECT statement that supplies values. > + * @param orconf The conflict algorithm > + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). > + * @retval Not NULL TriggerStep object on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct TriggerStep * > +sql_trigger_insert_step(struct sql *db, struct Token *table_name, > + struct IdList *column_list, struct Select *select, > + u8 orconf); 4. If you decided to completely refactor these functions, then use enum on_conflict_action, please. > + > +/** > + * Construct a trigger step that implements an UPDATE statement > + * and return a pointer to that trigger step. The parser calls > + * this routine when it sees an UPDATE statement inside the body > + * of a CREATE TRIGGER. > + * > + * @param db The database connection. > + * @param table_name Name of the table to be updated. > + * @param new_list The SET clause: list of column and new values. > + * @param where The WHERE clause. > + * @param orconf The conflict algorithm > + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). > + * @retval Not NULL TriggerStep object on success. > + * @retval NULL Otherwise. 5. In other places you wrote 'The diag message is set.'. Is it set here? I know, that it is, but the comment differs from the others. We should either never say about diag explicitly, or say it everywhere. And I think that now, while some SQL functions do not set diag, we should say about diag explicitly. > + */ > +struct TriggerStep * > +sql_trigger_update_step(struct sql *db, struct Token *table_name, 6. On the branch I see a whitespace after '*' before 'table_name'. Please, remove. > + struct ExprList *new_list, struct Expr *where, > + u8 orconf); > + ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-03-18 19:33 ` Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > Thanks for the fixes. See 6 comments below. >> trigger_cmd(A) ::= >> - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). >> - {A = sqlTriggerUpdateStep(pParse->db, &X, Y, Z, R);} >> + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { >> + A = sql_trigger_update_step(pParse->db, &X, Y, Z, R); >> + if (A == NULL) >> + sql_parser_error(pParse); > > 1. Why do not you return after an error here and in other places? It was not reasonable - here and in other places: it is the last function in this scope; but I've introduced "return" everywhere in parse.y; As for some other places, especially about patch "sql: refactor sql_expr_create to set diag" sql_expr_new usages in fk_constraint_action_trigger, there are many memory allocations so I don't like to manually release them in case of some failed allocation, so I've kept original logic. > >> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h >> index b49229b26..4e6c5717b 100644 >> --- a/src/box/sql/sqlInt.h >> +++ b/src/box/sql/sqlInt.h >> @@ -4087,12 +4087,77 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger, >> int ignore_jump); >> >> void sqlDeleteTriggerStep(sql *, TriggerStep *); >> -TriggerStep *sqlTriggerSelectStep(sql *, Select *); >> -TriggerStep *sqlTriggerInsertStep(sql *, Token *, IdList *, >> - Select *, u8); >> -TriggerStep *sqlTriggerUpdateStep(sql *, Token *, ExprList *, Expr *, >> - u8); >> -TriggerStep *sqlTriggerDeleteStep(sql *, Token *, Expr *); >> + >> +/** >> + * Turn a SELECT statement (that the pSelect parameter points to) > > 2. Again old names. Fixed... >> + * into a trigger step. Return a pointer to a TriggerStep >> + * structure. > > 3. In that case a type of return value is strictly determined > thanks to C language. Not sure if that sentence makes a sense. > The same about other places. Okay. > 4. If you decided to completely refactor these functions,> then use enum on_conflict_action, please. Done. > >> + >> +/** >> + * Construct a trigger step that implements an UPDATE statement >> + * and return a pointer to that trigger step. The parser calls >> + * this routine when it sees an UPDATE statement inside the body >> + * of a CREATE TRIGGER. >> + * >> + * @param db The database connection. >> + * @param table_name Name of the table to be updated. >> + * @param new_list The SET clause: list of column and new values. >> + * @param where The WHERE clause. >> + * @param orconf The conflict algorithm >> + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). >> + * @retval Not NULL TriggerStep object on success. >> + * @retval NULL Otherwise. > > 5. In other places you wrote 'The diag message is set.'. Is it > set here? I know, that it is, but the comment differs from the others. > We should either never say about diag explicitly, or say it everywhere. > And I think that now, while some SQL functions do not set diag, we > should say about diag explicitly. Done. > 6. On the branch I see a whitespace after '*' before 'table_name'. > Please, remove. Fixed. ====================================================== Refactored triggerSterAllocate routine as sql_trigger_step_new and reworked it to set diag_set in case of memory allocation error. Also performed some additional name refactoring in adjacent places. This change is necessary because the sql_trigger_step_allocate body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/parse.y | 38 +++++++-- src/box/sql/sqlInt.h | 73 +++++++++++++++-- src/box/sql/trigger.c | 187 +++++++++++++++++------------------------- 3 files changed, 171 insertions(+), 127 deletions(-) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index daeb6e84b..194af99b7 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1430,20 +1430,42 @@ tridxby ::= NOT INDEXED. { %destructor trigger_cmd {sqlDeleteTriggerStep(pParse->db, $$);} // UPDATE trigger_cmd(A) ::= - UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). - {A = sqlTriggerUpdateStep(pParse->db, &X, Y, Z, R);} + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { + A = sql_trigger_update_step(pParse->db, &X, Y, Z, R); + if (A == NULL) { + pParse->is_aborted = true; + return; + } + } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). - {A = sqlTriggerInsertStep(pParse->db, &X, F, S, R);/*A-overwrites-R*/} +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). { + /*A-overwrites-R. */ + A = sql_trigger_insert_step(pParse->db, &X, F, S, R); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} // DELETE -trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). - {A = sqlTriggerDeleteStep(pParse->db, &X, Y);} +trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). { + A = sql_trigger_delete_step(pParse->db, &X, Y); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} // SELECT -trigger_cmd(A) ::= select(X). - {A = sqlTriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} +trigger_cmd(A) ::= select(X). { + /* A-overwrites-X. */ + A = sql_trigger_select_step(pParse->db, X); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 3aedde4cc..d3ffa9d9b 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4109,12 +4109,73 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger, int ignore_jump); void sqlDeleteTriggerStep(sql *, TriggerStep *); -TriggerStep *sqlTriggerSelectStep(sql *, Select *); -TriggerStep *sqlTriggerInsertStep(sql *, Token *, IdList *, - Select *, u8); -TriggerStep *sqlTriggerUpdateStep(sql *, Token *, ExprList *, Expr *, - u8); -TriggerStep *sqlTriggerDeleteStep(sql *, Token *, Expr *); + +/** + * Turn a SELECT statement (that the select parameter points to) + * into a trigger step. + * The parser calls this routine when it finds a SELECT statement + * in body of a TRIGGER. + * + * @param db The database connection. + * @param select The SELECT statement to process. + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select); + +/** + * Build a trigger step out of an INSERT statement. + * The parser calls this routine when it sees an INSERT inside the + * body of a trigger. + * + * @param db The database connection. + * @param table_name Name of the table into which we insert. + * @param column_list List of columns in table to insert into. + * @param select The SELECT statement that supplies values. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + enum on_conflict_action orconf); + +/** + * Construct a trigger step that implements an UPDATE statemen. + * The parser calls this routine when it sees an UPDATE statement + * inside the body of a CREATE TRIGGER. + * + * @param db The database connection. + * @param table_name Name of the table to be updated. + * @param new_list The SET clause: list of column and new values. + * @param where The WHERE clause. + * @param orconf The conflict algorithm + * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf); + +/** + * Construct a trigger step that implements a DELETE statement. + * The parser calls this routine when it sees a DELETE statement + * inside the body of a CREATE TRIGGER. + * + * @param db The database connection. + * @param table_name The table from which rows are deleted. + * @param where The WHERE clause. + * @retval Not NULL TriggerStep object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token *table_name, + struct Expr *where); /** * Triggers may access values stored in the old.* or new.* diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index dc30c5f2c..544ba2a5a 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -248,142 +248,103 @@ cleanup: sqlDeleteTriggerStep(db, step_list); } -/* - * Turn a SELECT statement (that the pSelect parameter points to) into - * a trigger step. Return a pointer to a TriggerStep structure. - * - * The parser calls this routine when it finds a SELECT statement in - * body of a TRIGGER. - */ -TriggerStep * -sqlTriggerSelectStep(sql * db, Select * pSelect) +struct TriggerStep * +sql_trigger_select_step(struct sql *db, struct Select *select) { - TriggerStep *pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep)); - if (pTriggerStep == 0) { - sql_select_delete(db, pSelect); - return 0; + struct TriggerStep *trigger_step = + sqlDbMallocZero(db, sizeof(struct TriggerStep)); + if (trigger_step == NULL) { + sql_select_delete(db, select); + diag_set(OutOfMemory, sizeof(struct TriggerStep), + "sqlDbMallocZero", "trigger_step"); + return NULL; } - pTriggerStep->op = TK_SELECT; - pTriggerStep->pSelect = pSelect; - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; - return pTriggerStep; + trigger_step->op = TK_SELECT; + trigger_step->pSelect = select; + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; + return trigger_step; } /* * Allocate space to hold a new trigger step. The allocated space - * holds both the TriggerStep object and the TriggerStep.target.z string. + * holds both the TriggerStep object and the TriggerStep.target.z + * string. * - * If an OOM error occurs, NULL is returned and db->mallocFailed is set. + * @param db The database connection. + * @param op Trigger opcode. + * @param target_name The target name token. + * @retval Not NULL TriggerStep object on success. + * @retval NULL otherwise. The diag message is set. */ -static TriggerStep * -triggerStepAllocate(sql * db, /* Database connection */ - u8 op, /* Trigger opcode */ - Token * pName /* The target name */ - ) +static struct TriggerStep * +sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) { - TriggerStep *pTriggerStep; - - pTriggerStep = - sqlDbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); - if (pTriggerStep) { - char *z = (char *)&pTriggerStep[1]; - memcpy(z, pName->z, pName->n); - sqlNormalizeName(z); - pTriggerStep->zTarget = z; - pTriggerStep->op = op; + int size = sizeof(struct TriggerStep) + target_name->n + 1; + struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); + if (trigger_step == NULL) { + diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + return NULL; } - return pTriggerStep; + char *z = (char *)&trigger_step[1]; + memcpy(z, target_name->z, target_name->n); + sqlNormalizeName(z); + trigger_step->zTarget = z; + trigger_step->op = op; + return trigger_step; } -/* - * Build a trigger step out of an INSERT statement. Return a pointer - * to the new trigger step. - * - * The parser calls this routine when it sees an INSERT inside the - * body of a trigger. - */ -TriggerStep * -sqlTriggerInsertStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table into which we insert */ - IdList * pColumn, /* List of columns in pTableName to insert into */ - Select * pSelect, /* A SELECT statement that supplies values */ - u8 orconf /* The conflict algorithm - * (ON_CONFLICT_ACTION_ABORT, _REPLACE, - * etc.) - */ - ) +struct TriggerStep * +sql_trigger_insert_step(struct sql *db, struct Token *table_name, + struct IdList *column_list, struct Select *select, + enum on_conflict_action orconf) { - TriggerStep *pTriggerStep; - - assert(pSelect != 0 || db->mallocFailed); - - pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); - if (pTriggerStep) { - pTriggerStep->pSelect = - sqlSelectDup(db, pSelect, EXPRDUP_REDUCE); - pTriggerStep->pIdList = pColumn; - pTriggerStep->orconf = orconf; + assert(select != 0 || db->mallocFailed); + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_INSERT, table_name); + if (trigger_step != NULL) { + trigger_step->pSelect = + sqlSelectDup(db, select, EXPRDUP_REDUCE); + trigger_step->pIdList = column_list; + trigger_step->orconf = orconf; } else { - sqlIdListDelete(db, pColumn); + sqlIdListDelete(db, column_list); } - sql_select_delete(db, pSelect); - - return pTriggerStep; + sql_select_delete(db, select); + return trigger_step; } -/* - * Construct a trigger step that implements an UPDATE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees an UPDATE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerUpdateStep(sql * db, /* The database connection */ - Token * pTableName, /* Name of the table to be updated */ - ExprList * pEList, /* The SET clause: list of column and new values */ - Expr * pWhere, /* The WHERE clause */ - u8 orconf /* The conflict algorithm. - * (ON_CONFLICT_ACTION_ABORT, _IGNORE, - * etc) - */ - ) +struct TriggerStep * +sql_trigger_update_step(struct sql *db, struct Token *table_name, + struct ExprList *new_list, struct Expr *where, + u8 orconf) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName); - if (pTriggerStep) { - pTriggerStep->pExprList = - sql_expr_list_dup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = orconf; + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_UPDATE, table_name); + if (trigger_step != NULL) { + trigger_step->pExprList = + sql_expr_list_dup(db, new_list, EXPRDUP_REDUCE); + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = orconf; } - sql_expr_list_delete(db, pEList); - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_list_delete(db, new_list); + sql_expr_delete(db, where, false); + return trigger_step; } -/* - * Construct a trigger step that implements a DELETE statement and return - * a pointer to that trigger step. The parser calls this routine when it - * sees a DELETE statement inside the body of a CREATE TRIGGER. - */ -TriggerStep * -sqlTriggerDeleteStep(sql * db, /* Database connection */ - Token * pTableName, /* The table from which rows are deleted */ - Expr * pWhere /* The WHERE clause */ - ) +struct TriggerStep * +sql_trigger_delete_step(struct sql *db, struct Token *table_name, + struct Expr *where) { - TriggerStep *pTriggerStep; - - pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName); - if (pTriggerStep) { - pTriggerStep->pWhere = - sqlExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->orconf = ON_CONFLICT_ACTION_DEFAULT; + struct TriggerStep *trigger_step = + sql_trigger_step_new(db, TK_DELETE, table_name); + if (trigger_step != NULL) { + trigger_step->pWhere = + sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; } - sql_expr_delete(db, pWhere, false); - return pTriggerStep; + sql_expr_delete(db, where, false); + return trigger_step; } void -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate to set diag 2019-03-20 11:02 ` Kirill Shcherbatov @ 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 0 replies; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 17:08 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes! Please, look at the diff below and on the branch. ============================================================= commit 0acf8d462df1c5c095d8a1e52b90ac5c27209c34 Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Mon Mar 25 17:13:44 2019 +0300 Review fixes diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 95f489957..6c01deea4 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4113,9 +4113,10 @@ void sqlDeleteTriggerStep(sql *, TriggerStep *); * in body of a TRIGGER. * * @param db The database connection. - * @param select The SELECT statement to process. + * @param select The SELECT statement to process. Deleted on + * error. * @retval Not NULL TriggerStep object on success. - * @retval NULL Otherwise. The diag message is set. + * @retval NULL Error. The diag message is set. */ struct TriggerStep * sql_trigger_select_step(struct sql *db, struct Select *select); @@ -4127,12 +4128,13 @@ sql_trigger_select_step(struct sql *db, struct Select *select); * * @param db The database connection. * @param table_name Name of the table into which we insert. - * @param column_list List of columns in table to insert into. - * @param select The SELECT statement that supplies values. - * @param orconf The conflict algorithm - * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * @param column_list List of columns in table to insert into. Is + * deleted on error. + * @param select The SELECT statement that supplies values. Is + * deleted anyway. + * @param orconf A conflict processing algorithm. * @retval Not NULL TriggerStep object on success. - * @retval NULL Otherwise. The diag message is set. + * @retval NULL Error. The diag message is set. */ struct TriggerStep * sql_trigger_insert_step(struct sql *db, struct Token *table_name, @@ -4147,16 +4149,16 @@ sql_trigger_insert_step(struct sql *db, struct Token *table_name, * @param db The database connection. * @param table_name Name of the table to be updated. * @param new_list The SET clause: list of column and new values. - * @param where The WHERE clause. - * @param orconf The conflict algorithm - * (ON_CONFLICT_ACTION_ABORT, _REPLACE, etc.). + * Is deleted anyway. + * @param where The WHERE clause. Is deleted anyway. + * @param orconf A conflict processing algorithm. * @retval Not NULL TriggerStep object on success. - * @retval NULL Otherwise. The diag message is set. + * @retval NULL Error. The diag message is set. */ struct TriggerStep * sql_trigger_update_step(struct sql *db, struct Token *table_name, struct ExprList *new_list, struct Expr *where, - u8 orconf); + enum on_conflict_action orconf); /** * Construct a trigger step that implements a DELETE statement. @@ -4165,9 +4167,9 @@ sql_trigger_update_step(struct sql *db, struct Token *table_name, * * @param db The database connection. * @param table_name The table from which rows are deleted. - * @param where The WHERE clause. + * @param where The WHERE clause. Is deleted anyway. * @retval Not NULL TriggerStep object on success. - * @retval NULL Otherwise. The diag message is set. + * @retval NULL Error. The diag message is set. */ struct TriggerStep * sql_trigger_delete_step(struct sql *db, struct Token *table_name, diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 544ba2a5a..20838f51b 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -274,7 +274,7 @@ sql_trigger_select_step(struct sql *db, struct Select *select) * @param op Trigger opcode. * @param target_name The target name token. * @retval Not NULL TriggerStep object on success. - * @retval NULL otherwise. The diag message is set. + * @retval NULL Otherwise. The diag message is set. */ static struct TriggerStep * sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) @@ -298,7 +298,7 @@ sql_trigger_insert_step(struct sql *db, struct Token *table_name, struct IdList *column_list, struct Select *select, enum on_conflict_action orconf) { - assert(select != 0 || db->mallocFailed); + assert(select != NULL || db->mallocFailed); struct TriggerStep *trigger_step = sql_trigger_step_new(db, TK_INSERT, table_name); if (trigger_step != NULL) { @@ -316,15 +316,14 @@ sql_trigger_insert_step(struct sql *db, struct Token *table_name, struct TriggerStep * sql_trigger_update_step(struct sql *db, struct Token *table_name, struct ExprList *new_list, struct Expr *where, - u8 orconf) + enum on_conflict_action orconf) { struct TriggerStep *trigger_step = sql_trigger_step_new(db, TK_UPDATE, table_name); if (trigger_step != NULL) { trigger_step->pExprList = sql_expr_list_dup(db, new_list, EXPRDUP_REDUCE); - trigger_step->pWhere = - sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->pWhere = sqlExprDup(db, where, EXPRDUP_REDUCE); trigger_step->orconf = orconf; } sql_expr_list_delete(db, new_list); @@ -339,8 +338,7 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name, struct TriggerStep *trigger_step = sql_trigger_step_new(db, TK_DELETE, table_name); if (trigger_step != NULL) { - trigger_step->pWhere = - sqlExprDup(db, where, EXPRDUP_REDUCE); + trigger_step->pWhere = sqlExprDup(db, where, EXPRDUP_REDUCE); trigger_step->orconf = ON_CONFLICT_ACTION_DEFAULT; } sql_expr_delete(db, where, false); ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (4 preceding siblings ...) 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate " Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 7/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (2 subsequent siblings) 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Refactored sql_expr_create routine to use diag_set in case of memory allocation error. This change is necessary because the sql_expr_create body has a sqlNameFromToken call that will be changed in subsequent patches. Needed for #3931 --- src/box/sql/build.c | 44 +++++-- src/box/sql/expr.c | 249 +++++++++++++++--------------------- src/box/sql/fk_constraint.c | 185 +++++++++++++++++---------- src/box/sql/parse.y | 35 +++-- src/box/sql/resolve.c | 47 ++++--- src/box/sql/select.c | 90 +++++++++---- src/box/sql/sqlInt.h | 80 +++++++++++- src/box/sql/wherecode.c | 9 +- src/box/sql/whereexpr.c | 22 ++-- 9 files changed, 462 insertions(+), 299 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 2084dbfeb..24f20836b 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -627,9 +627,11 @@ sqlAddPrimaryKey(Parse * pParse, /* Parsing context */ struct ExprList *list; struct Token token; sqlTokenInit(&token, space->def->fields[iCol].name); - list = sql_expr_list_append(db, NULL, - sqlExprAlloc(db, TK_ID, - &token, 0)); + struct Expr *expr = + sql_expr_create(db, TK_ID, &token, false); + if (expr == NULL) + goto tnt_error; + list = sql_expr_list_append(db, NULL, expr); if (list == NULL) goto primary_key_exit; sql_create_index(pParse, 0, 0, list, 0, SORT_ORDER_ASC, @@ -659,6 +661,9 @@ sqlAddPrimaryKey(Parse * pParse, /* Parsing context */ primary_key_exit: sql_expr_list_delete(pParse->db, pList); return; +tnt_error: + sql_parser_error(pParse); + goto primary_key_exit; } void @@ -1372,12 +1377,15 @@ sql_id_eq_str_expr(struct Parse *parse, const char *col_name, const char *col_value) { struct sql *db = parse->db; - - struct Expr *col_name_expr = sqlExpr(db, TK_ID, col_name); - if (col_name_expr == NULL) + struct Expr *col_name_expr = sql_op_expr_create(db, TK_ID, col_name); + if (col_name_expr == NULL) { + sql_parser_error(parse); return NULL; - struct Expr *col_value_expr = sqlExpr(db, TK_STRING, col_value); + } + struct Expr *col_value_expr = + sql_op_expr_create(db, TK_STRING, col_value); if (col_value_expr == NULL) { + sql_parser_error(parse); sql_expr_delete(db, col_name_expr, false); return NULL; } @@ -1400,13 +1408,19 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, struct Expr *where = NULL; if (idx_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_create(db, expr, where); + if (where == NULL) + sql_parser_error(parse); + } } if (table_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_create(db, expr, where); + if (where == NULL) + sql_parser_error(parse); + } } /** * On memory allocation error sql_table delete_from @@ -2265,9 +2279,11 @@ sql_create_index(struct Parse *parse, struct Token *token, struct Token prev_col; uint32_t last_field = def->field_count - 1; sqlTokenInit(&prev_col, def->fields[last_field].name); - col_list = sql_expr_list_append(parse->db, NULL, - sqlExprAlloc(db, TK_ID, - &prev_col, 0)); + struct Expr *expr = sql_expr_create(db, TK_ID, &prev_col, + false); + if (expr == NULL) + goto tnt_error; + col_list = sql_expr_list_append(parse->db, NULL, expr); if (col_list == NULL) goto exit_create_index; assert(col_list->nExpr == 1); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index fa2ff1ab3..dd5e2c28d 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -149,16 +149,17 @@ sqlExprAddCollateToken(Parse * pParse, /* Parsing context */ int dequote /* True to dequote pCollName */ ) { - if (pCollName->n > 0) { - Expr *pNew = - sqlExprAlloc(pParse->db, TK_COLLATE, pCollName, - dequote); - if (pNew) { - pNew->pLeft = pExpr; - pNew->flags |= EP_Collate | EP_Skip; - pExpr = pNew; - } + if (pCollName->n == 0) + return pExpr; + struct Expr *new_expr = + sql_expr_create(pParse->db, TK_COLLATE, pCollName, dequote); + if (new_expr == NULL) { + sql_parser_error(pParse); + return NULL; } + new_expr->pLeft = pExpr; + new_expr->flags |= EP_Collate | EP_Skip; + pExpr = new_expr; return pExpr; } @@ -854,113 +855,62 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) #define exprSetHeight(y) #endif /* SQL_MAX_EXPR_DEPTH>0 */ -/* - * This routine is the core allocator for Expr nodes. - * - * Construct a new expression node and return a pointer to it. Memory - * for this node and for the pToken argument is a single allocation - * obtained from sqlDbMalloc(). The calling function - * is responsible for making sure the node eventually gets freed. - * - * If dequote is true, then the token (if it exists) is dequoted. - * If dequote is false, no dequoting is performed. The deQuote - * parameter is ignored if pToken is NULL or if the token does not - * appear to be quoted. If the quotes were of the form "..." (double-quotes) - * then the EP_DblQuoted flag is set on the expression node. - * - * Special case: If op==TK_INTEGER and pToken points to a string that - * can be translated into a 32-bit integer, then the token is not - * stored in u.zToken. Instead, the integer values is written - * into u.iValue and the EP_IntValue flag is set. No extra storage - * is allocated to hold the integer text and the dequote flag is ignored. - */ -Expr * -sqlExprAlloc(sql * db, /* Handle for sqlDbMallocRawNN() */ - int op, /* Expression opcode */ - const Token * pToken, /* Token argument. Might be NULL */ - int dequote /* True to dequote */ - ) +struct Expr * +sql_expr_create(struct sql *db, int op, const Token *token, bool dequote) { - Expr *pNew; - int nExtra = 0; - int iValue = 0; - - assert(db != 0); - if (pToken) { - if (op != TK_INTEGER || pToken->z == 0 - || sqlGetInt32(pToken->z, &iValue) == 0) { - nExtra = pToken->n + 1; - assert(iValue >= 0); + int extra_sz = 0; + int val = 0; + if (token != NULL) { + if (op != TK_INTEGER || token->z == NULL || + sqlGetInt32(token->z, &val) == 0) { + extra_sz = token->n + 1; + assert(val >= 0); } } - pNew = sqlDbMallocRawNN(db, sizeof(Expr) + nExtra); - if (pNew) { - memset(pNew, 0, sizeof(Expr)); - pNew->op = (u8) op; - pNew->iAgg = -1; - if (pToken) { - if (nExtra == 0) { - pNew->flags |= EP_IntValue; - pNew->u.iValue = iValue; - } else { - pNew->u.zToken = (char *)&pNew[1]; - assert(pToken->z != 0 || pToken->n == 0); - if (pToken->n) - memcpy(pNew->u.zToken, pToken->z, - pToken->n); - pNew->u.zToken[pToken->n] = 0; - if (dequote){ - if (pNew->u.zToken[0] == '"') - pNew->flags |= EP_DblQuoted; - if (pNew->op == TK_ID || - pNew->op == TK_COLLATE || - pNew->op == TK_FUNCTION){ - sqlNormalizeName(pNew->u.zToken); - }else{ - sqlDequote(pNew->u.zToken); - } - } - } - } -#if SQL_MAX_EXPR_DEPTH>0 - pNew->nHeight = 1; -#endif + struct Expr *expr = sqlDbMallocRawNN(db, sizeof(*expr) + extra_sz); + if (expr == NULL) { + diag_set(OutOfMemory, sizeof(*expr), "sqlDbMallocRawNN", + "expr"); + return NULL; } - return pNew; -} -/* - * Allocate a new expression node from a zero-terminated token that has - * already been dequoted. - */ -Expr * -sqlExpr(sql * db, /* Handle for sqlDbMallocZero() (may be null) */ - int op, /* Expression opcode */ - const char *zToken /* Token argument. Might be NULL */ - ) -{ - Token x; - x.z = zToken; - x.n = zToken ? sqlStrlen30(zToken) : 0; - return sqlExprAlloc(db, op, &x, 0); + memset(expr, 0, sizeof(*expr)); + expr->op = (u8)op; + expr->iAgg = -1; +#if SQL_MAX_EXPR_DEPTH > 0 + expr->nHeight = 1; +#endif + if (token == NULL) + return expr; + + if (extra_sz == 0) { + expr->flags |= EP_IntValue; + expr->u.iValue = val; + } else { + expr->u.zToken = (char *)&expr[1]; + assert(token->z != NULL || token->n == 0); + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) { + if (expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION) + sqlNormalizeName(expr->u.zToken); + else + sqlDequote(expr->u.zToken); + } + } + return expr; } -/* Allocate a new expression and initialize it as integer. - * @param db sql engine. - * @param value Value to initialize by. - * - * @retval not NULL Allocated and initialized expr. - * @retval NULL Memory error. - */ -Expr * -sqlExprInteger(sql * db, int value) +struct Expr * +sql_op_expr_create(struct sql *db, int op, const char *name) { - Expr *ret = sqlExpr(db, TK_INTEGER, NULL); - if (ret != NULL) { - ret->flags = EP_IntValue; - ret->u.iValue = value; - } - return ret; + struct Token name_token; + name_token.z = name; + name_token.n = name != NULL ? strlen(name) : 0; + return sql_expr_create(db, op, &name_token, false); } /* @@ -1006,8 +956,15 @@ sqlPExpr(Parse * pParse, /* Parsing context */ { Expr *p; if (op == TK_AND && pParse->nErr == 0) { - /* Take advantage of short-circuit false optimization for AND */ - p = sqlExprAnd(pParse->db, pLeft, pRight); + /* + * Take advantage of short-circuit false + * optimization for AND. + */ + if (pLeft == NULL && pRight == NULL) + return NULL; + p = sql_and_expr_create(pParse->db, pLeft, pRight); + if (p == NULL) + sql_parser_error(pParse); } else { p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)); if (p) { @@ -1076,30 +1033,26 @@ exprAlwaysFalse(Expr * p) return v == 0; } -/* - * Join two expressions using an AND operator. If either expression is - * NULL, then just return the other expression. - * - * If one side or the other of the AND is known to be false, then instead - * of returning an AND expression, just return a constant expression with - * a value of false. - */ -Expr * -sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) +struct Expr * +sql_and_expr_create(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr) { - if (pLeft == 0) { - return pRight; - } else if (pRight == 0) { - return pLeft; - } else if (exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight)) { - sql_expr_delete(db, pLeft, false); - sql_expr_delete(db, pRight, false); - return sqlExprAlloc(db, TK_INTEGER, &sqlIntTokens[0], - 0); + if (left_expr == NULL) { + assert(right_expr != NULL); + return right_expr; + } else if (right_expr == NULL) { + assert(left_expr != NULL); + return left_expr; + } else if (exprAlwaysFalse(left_expr) || exprAlwaysFalse(right_expr)) { + sql_expr_delete(db, left_expr, false); + sql_expr_delete(db, right_expr, false); + return sql_expr_create(db, TK_INTEGER, &sqlIntTokens[0], + false); } else { - Expr *pNew = sqlExprAlloc(db, TK_AND, 0, 0); - sqlExprAttachSubtrees(db, pNew, pLeft, pRight); - return pNew; + struct Expr *new_expr = + sql_expr_create(db, TK_AND, NULL, false); + sqlExprAttachSubtrees(db, new_expr, left_expr, right_expr); + return new_expr; } } @@ -1110,18 +1063,18 @@ sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) Expr * sqlExprFunction(Parse * pParse, ExprList * pList, Token * pToken) { - Expr *pNew; - sql *db = pParse->db; - assert(pToken); - pNew = sqlExprAlloc(db, TK_FUNCTION, pToken, 1); - if (pNew == 0) { - sql_expr_list_delete(db, pList); /* Avoid memory leak when malloc fails */ - return 0; + struct sql *db = pParse->db; + assert(pToken != NULL); + struct Expr *new_expr = sql_expr_create(db, TK_FUNCTION, pToken, true); + if (new_expr == NULL) { + sql_expr_list_delete(db, pList); + sql_parser_error(pParse); + return NULL; } - pNew->x.pList = pList; - assert(!ExprHasProperty(pNew, EP_xIsSelect)); - sqlExprSetHeightAndFlags(pParse, pNew); - return pNew; + new_expr->x.pList = pList; + assert(!ExprHasProperty(new_expr, EP_xIsSelect)); + sqlExprSetHeightAndFlags(pParse, new_expr); + return new_expr; } /* @@ -2911,10 +2864,12 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */ } if (pSel->pLimit == NULL) { pSel->pLimit = - sqlExprAlloc(pParse->db, TK_INTEGER, - &sqlIntTokens[1], - 0); - if (pSel->pLimit != NULL) { + sql_expr_create(pParse->db, TK_INTEGER, + &sqlIntTokens[1], + false); + if (pSel->pLimit == NULL) { + sql_parser_error(pParse); + } else { ExprSetProperty(pSel->pLimit, EP_System); } diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index 4f761d610..c81eb4ac1 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -308,32 +308,30 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, * regBase is the first of an array of register that contains * the data for given space. regBase+1 holds the first column. * regBase+2 holds the second column, and so forth. - * - * @param pParse Parsing and code generating context. + * @param parser The parsing context. * @param def Definition of space whose content is at r[regBase]... - * @param regBase Contents of table defined by def. - * @param iCol Which column of space is desired. - * @return an Expr object that refers to a memory register - * corresponding to column iCol of given space. + * @param reg_base Contents of table table. + * @param column Index of table column is desired. + * @retval no NULL expression pointer on success. + * @retval NULL otherwise. */ -static Expr * -space_field_register(Parse *pParse, struct space_def *def, int regBase, - i16 iCol) +static struct Expr * +sql_register_expr_create(struct Parse *parser, struct space_def *def, int reg_base, + int column) { - Expr *pExpr; - sql *db = pParse->db; - - pExpr = sqlExpr(db, TK_REGISTER, 0); - if (pExpr) { - if (iCol >= 0) { - pExpr->iTable = regBase + iCol + 1; - pExpr->type = def->fields[iCol].type; - } else { - pExpr->iTable = regBase; - pExpr->type = FIELD_TYPE_INTEGER; - } + struct Expr *expr = sql_op_expr_create(parser->db, TK_REGISTER, NULL); + if (expr == NULL) { + sql_parser_error(parser); + return NULL; + } + if (column >= 0) { + expr->iTable = reg_base + column + 1; + expr->type = def->fields[column].type; + } else { + expr->iTable = reg_base; + expr->type = FIELD_TYPE_INTEGER; } - return pExpr; + return expr; } /** @@ -346,16 +344,17 @@ space_field_register(Parse *pParse, struct space_def *def, int regBase, * @retval not NULL on success. * @retval NULL on error. */ -static Expr * -exprTableColumn(sql * db, struct space_def *def, int cursor, i16 column) +static struct Expr * +sql_column_cursor_expr_create(struct sql *db, struct space_def *def, + int cursor, int column) { - Expr *pExpr = sqlExpr(db, TK_COLUMN, 0); - if (pExpr) { - pExpr->space_def = def; - pExpr->iTable = cursor; - pExpr->iColumn = column; - } - return pExpr; + struct Expr *expr = sql_op_expr_create(db, TK_COLUMN, NULL); + if (expr == NULL) + return NULL; + expr->space_def = def; + expr->iTable = cursor; + expr->iColumn = column; + return expr; } /* @@ -435,12 +434,20 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; struct Expr *pexpr = - space_field_register(parser, def, reg_data, fieldno); + sql_register_expr_create(parser, def, reg_data, + fieldno); fieldno = fk_def->links[i].child_field; const char *field_name = child_space->def->fields[fieldno].name; - struct Expr *chexpr = sqlExpr(db, TK_ID, field_name); + struct Expr *chexpr = + sql_op_expr_create(db, TK_ID, field_name); + if (chexpr == NULL) + sql_parser_error(parser); struct Expr *eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - where = sqlExprAnd(db, where, eq); + if (where != NULL || eq != NULL) { + where = sql_and_expr_create(db, where, eq); + if (where == NULL) + sql_parser_error(parser); + } } /* @@ -456,15 +463,26 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, struct Expr *expr = NULL, *pexpr, *chexpr, *eq; for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; - pexpr = space_field_register(parser, def, reg_data, - fieldno); - chexpr = exprTableColumn(db, def, src->a[0].iCursor, - fieldno); + pexpr = sql_register_expr_create(parser, def, reg_data, + fieldno); + int cursor = src->a[0].iCursor; + chexpr = sql_column_cursor_expr_create(db, def, cursor, + fieldno); + if (chexpr == NULL) + sql_parser_error(parser); eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - expr = sqlExprAnd(db, expr, eq); + if (expr != NULL || eq != NULL) { + expr = sql_and_expr_create(db, expr, eq); + if (expr == NULL) + sql_parser_error(parser); + } } struct Expr *pNe = sqlPExpr(parser, TK_NOT, expr, 0); - where = sqlExprAnd(db, where, pNe); + if (where != NULL || pNe != NULL) { + where = sql_and_expr_create(db, where, pNe); + if (where == NULL) + sql_parser_error(parser); + } } /* Resolve the references in the WHERE clause. */ @@ -785,14 +803,26 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * type and collation sequence associated with * the parent table are used for the comparison. */ - struct Expr *to_col = - sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); - struct Expr *from_col = - sqlExprAlloc(db, TK_ID, &t_from_col, 0); - struct Expr *eq = sqlPExpr(pParse, TK_EQ, to_col, from_col); - where = sqlExprAnd(db, where, eq); + struct Expr *old_expr = NULL, *new_expr = NULL, *expr = NULL; + old_expr = sql_expr_create(db, TK_ID, &t_old, false); + if (old_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_create(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); + struct Expr *from_col_expr = + sql_expr_create(db, TK_ID, &t_from_col, false); + if (from_col_expr == NULL) + sql_parser_error(pParse); + struct Expr *to_col_expr = + sqlPExpr(pParse, TK_DOT, old_expr, expr); + struct Expr *eq = + sqlPExpr(pParse, TK_EQ, to_col_expr, from_col_expr); + if (where != NULL || eq != NULL) { + where = sql_and_expr_create(db, where, eq); + if (where == NULL) + sql_parser_error(pParse); + } /* * For ON UPDATE, construct the next term of the @@ -810,12 +840,22 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * no_action_needed(colN)) */ if (is_update) { + old_expr = sql_expr_create(db, TK_ID, &t_old, false); + if (old_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_create(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); struct Expr *old_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + old_expr, expr); + new_expr = sql_expr_create(db, TK_ID, &t_new, false); + if (new_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_create(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); struct Expr *new_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_new, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + new_expr, expr); struct Expr *old_is_null = sqlPExpr( pParse, TK_ISNULL, sqlExprDup(db, old_val, 0), NULL); @@ -828,29 +868,41 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Expr *no_action_needed = sqlPExpr(pParse, TK_OR, old_is_null, non_null_eq); - when = sqlExprAnd(db, when, no_action_needed); + if (when != NULL || no_action_needed != NULL) { + when = sql_and_expr_create(db, when, + no_action_needed); + if (when == NULL) + sql_parser_error(pParse); + } } if (action != FKEY_ACTION_RESTRICT && (action != FKEY_ACTION_CASCADE || is_update)) { struct Expr *new, *d; if (action == FKEY_ACTION_CASCADE) { - new = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, - &t_new, 0), - sqlExprAlloc(db, TK_ID, - &t_to_col, - 0)); + new_expr = sql_expr_create(db, TK_ID, &t_new, + false); + if (new_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_create(db, TK_ID, &t_to_col, + false); + if (expr == NULL) + sql_parser_error(pParse); + new = sqlPExpr(pParse, TK_DOT, new_expr, expr); } else if (action == FKEY_ACTION_SET_DEFAULT) { d = child_fields[chcol].default_value_expr; if (d != NULL) { new = sqlExprDup(db, d, 0); } else { - new = sqlExprAlloc(db, TK_NULL, - NULL, 0); + new = sql_expr_create(db, TK_NULL, + NULL, false); + if (new == NULL) + sql_parser_error(pParse); } } else { - new = sqlExprAlloc(db, TK_NULL, NULL, 0); + new = sql_expr_create(db, TK_NULL, NULL, false); + if (new == NULL) + sql_parser_error(pParse); } list = sql_expr_list_append(db, list, new); sqlExprListSetName(pParse, list, &t_from_col, 0); @@ -864,9 +916,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Token err; err.z = space_name; err.n = name_len; - struct Expr *r = sqlExpr(db, TK_RAISE, "FOREIGN KEY "\ - "constraint failed"); - if (r != NULL) + struct Expr *r = + sql_op_expr_create(db, TK_RAISE, + "FOREIGN KEY constraint failed"); + if (r == NULL) + sql_parser_error(pParse); + else r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; struct SrcList *src_list = sql_src_list_append(db, NULL, &err); if (src_list == NULL) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index db03714a9..d25995ec0 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -529,12 +529,16 @@ selcollist(A) ::= sclp(A) expr(X) as(Y). { sqlExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { - Expr *p = sqlExpr(pParse->db, TK_ASTERISK, 0); + struct Expr *p = sql_op_expr_create(pParse->db, TK_ASTERISK, NULL); + if (p == NULL) + sql_parser_error(pParse); A = sql_expr_list_append(pParse->db, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { Expr *pRight = sqlPExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = sqlExprAlloc(pParse->db, TK_ID, &X, 1); + struct Expr *pLeft = sql_expr_create(pParse->db, TK_ID, &X, true); + if (pLeft == NULL) + sql_parser_error(pParse); Expr *pDot = sqlPExpr(pParse, TK_DOT, pLeft, pRight); A = sql_expr_list_append(pParse->db,A, pDot); } @@ -887,15 +891,21 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { - Expr *temp1 = sqlExprAlloc(pParse->db, TK_ID, &X, 1); - Expr *temp2 = sqlExprAlloc(pParse->db, TK_ID, &Y, 1); + struct Expr *temp1 = sql_expr_create(pParse->db, TK_ID, &X, true); + if (temp1 == NULL) + sql_parser_error(pParse); + struct Expr *temp2 = sql_expr_create(pParse->db, TK_ID, &Y, true); + if (temp2 == NULL) + sql_parser_error(pParse); spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlPExpr(pParse, TK_DOT, temp1, temp2); } term(A) ::= FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= INTEGER(X). { - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER, &X, 1); + A.pExpr = sql_expr_create(pParse->db, TK_INTEGER, &X, true); + if (A.pExpr == NULL) + sql_parser_error(pParse); A.pExpr->type = FIELD_TYPE_INTEGER; A.zStart = X.z; A.zEnd = X.z + X.n; @@ -928,7 +938,9 @@ expr(A) ::= expr(A) COLLATE id(C). { %ifndef SQL_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_CAST, 0, 1); + A.pExpr = sql_expr_create(pParse->db, TK_CAST, NULL, true); + if (A.pExpr == NULL) + sql_parser_error(pParse); A.pExpr->type = T.type; sqlExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0); } @@ -1118,7 +1130,9 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] { ** regardless of the value of expr1. */ sql_expr_delete(pParse->db, A.pExpr, false); - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER,&sqlIntTokens[N],1); + A.pExpr = sql_expr_create(pParse->db, TK_INTEGER, &sqlIntTokens[N], true); + if (A.pExpr == NULL) + sql_parser_error(pParse); }else if( Y->nExpr==1 ){ /* Expressions of the form: ** @@ -1462,10 +1476,11 @@ expr(A) ::= RAISE(X) LP IGNORE RP(Y). { } expr(A) ::= RAISE(X) LP raisetype(T) COMMA STRING(Z) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_RAISE, &Z, 1); - if( A.pExpr ) { + A.pExpr = sql_expr_create(pParse->db, TK_RAISE, &Z, true); + if(A.pExpr == NULL) + sql_parser_error(pParse); + else A.pExpr->on_conflict_action = (enum on_conflict_action) T; - } } %type raisetype {int} diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index aed9e261d..3731aee31 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -487,26 +487,20 @@ lookupName(Parse * pParse, /* The parsing context */ } } -/* - * Allocate and return a pointer to an expression to load the column iCol - * from datasource iSrc in SrcList pSrc. - */ -Expr * -sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol) +struct Expr * +sql_column_expr_create(struct sql *db, struct SrcList *src_list, int src_idx, + int column) { - Expr *p = sqlExprAlloc(db, TK_COLUMN, 0, 0); - if (p) { - struct SrcList_item *pItem = &pSrc->a[iSrc]; - p->space_def = pItem->space->def; - p->iTable = pItem->iCursor; - p->iColumn = (ynVar) iCol; - testcase(iCol == BMS); - testcase(iCol == BMS - 1); - pItem->colUsed |= - ((Bitmask) 1) << (iCol >= BMS ? BMS - 1 : iCol); - ExprSetProperty(p, EP_Resolved); - } - return p; + struct Expr *expr = sql_expr_create(db, TK_COLUMN, NULL, false); + if (expr == NULL) + return NULL; + struct SrcList_item *item = &src_list->a[src_idx]; + expr->space_def = item->space->def; + expr->iTable = item->iCursor; + expr->iColumn = column; + item->colUsed |= ((Bitmask) 1) << (column >= BMS ? BMS - 1 : column); + ExprSetProperty(expr, EP_Resolved); + return expr; } /* @@ -1000,9 +994,13 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context. Leave error messages /* Convert the ORDER BY term into an integer column number iCol, * taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + Expr *pNew = + sql_op_expr_create(db, TK_INTEGER, + NULL); + if (pNew == NULL) { + sql_parser_error(pParse); return 1; + } pNew->flags |= EP_IntValue; pNew->u.iValue = iCol; if (pItem->pExpr == pE) { @@ -1348,9 +1346,10 @@ resolveSelectStep(Walker * pWalker, Select * p) * restrict it directly). */ sql_expr_delete(db, p->pLimit, false); - p->pLimit = - sqlExprAlloc(db, TK_INTEGER, - &sqlIntTokens[1], 0); + p->pLimit = sql_expr_create(db, TK_INTEGER, + &sqlIntTokens[1], false); + if (p->pLimit == NULL) + sql_parser_error(pParse); } else { if (sqlResolveExprNames(&sNC, p->pHaving)) return WRC_Abort; diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 7eea45dd7..2ae27e6c4 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -164,8 +164,11 @@ sqlSelectNew(Parse * pParse, /* Parsing context */ pNew = &standin; } if (pEList == 0) { - pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = + sql_op_expr_create(db, TK_ASTERISK, NULL); + if (expr == NULL) + sql_parser_error(pParse); + pEList = sql_expr_list_append(pParse->db, NULL, expr); } struct session MAYBE_UNUSED *user_session; user_session = current_session(); @@ -486,7 +489,6 @@ addWhereTerm(Parse * pParse, /* Parsing context */ int isOuterJoin, /* True if this is an OUTER join */ Expr ** ppWhere) /* IN/OUT: The WHERE clause to add to */ { - sql *db = pParse->db; Expr *pE1; Expr *pE2; Expr *pEq; @@ -496,8 +498,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ assert(pSrc->a[iLeft].space != NULL); assert(pSrc->a[iRight].space != NULL); - pE1 = sqlCreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = sqlCreateColumnExpr(db, pSrc, iRight, iColRight); + pE1 = sql_column_expr_create(pParse->db, pSrc, iLeft, iColLeft); + if (pE1 == NULL) + sql_parser_error(pParse); + pE2 = sql_column_expr_create(pParse->db, pSrc, iRight, iColRight); + if (pE2 == NULL) + sql_parser_error(pParse); pEq = sqlPExpr(pParse, TK_EQ, pE1, pE2); if (pEq && isOuterJoin) { @@ -506,7 +512,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ ExprSetVVAProperty(pEq, EP_NoReduce); pEq->iRightJoinTable = (i16) pE2->iTable; } - *ppWhere = sqlExprAnd(db, *ppWhere, pEq); + if (*ppWhere != NULL || pEq != NULL) { + *ppWhere = sql_and_expr_create(pParse->db, *ppWhere, pEq); + if (*ppWhere == NULL) + sql_parser_error(pParse); + } + return; } /* @@ -627,8 +638,13 @@ sqlProcessJoin(Parse * pParse, Select * p) if (pRight->pOn) { if (isOuter) setJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = - sqlExprAnd(pParse->db, p->pWhere, pRight->pOn); + if (p->pWhere != NULL || pRight->pOn != NULL) { + p->pWhere = sql_and_expr_create(pParse->db, + p->pWhere, + pRight->pOn); + if (p->pWhere == NULL) + sql_parser_error(pParse); + } pRight->pOn = 0; } @@ -3332,9 +3348,13 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */ break; } if (j == nOrderBy) { - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + Expr *pNew = + sql_op_expr_create(db, TK_INTEGER, + NULL); + if (pNew == NULL) { + sql_parser_error(pParse); return SQL_NOMEM_BKPT; + } pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sql_expr_list_append(pParse->db, @@ -4207,17 +4227,23 @@ flattenSubquery(Parse * pParse, /* Parsing context */ assert(pParent->pHaving == 0); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; - pParent->pHaving = sqlExprAnd(db, - sqlExprDup(db, - pSub->pHaving, - 0), - pParent->pHaving); + struct Expr *sub_having = sqlExprDup(db, pSub->pHaving, 0); + if (sub_having != NULL || pParent->pHaving != NULL) { + pParent->pHaving = + sql_and_expr_create(db, sub_having, + pParent->pHaving); + if (pParent->pHaving == NULL) + sql_parser_error(pParse); + } assert(pParent->pGroupBy == 0); pParent->pGroupBy = sql_expr_list_dup(db, pSub->pGroupBy, 0); - } else { + } else if (pWhere != NULL || pParent->pWhere != NULL) { pParent->pWhere = - sqlExprAnd(db, pWhere, pParent->pWhere); + sql_and_expr_create(db, pWhere, + pParent->pWhere); + if (pParent->pWhere == NULL) + sql_parser_error(pParse); } substSelect(pParse, pParent, iParent, pSub->pEList, 0); @@ -4322,8 +4348,14 @@ pushDownWhereTerms(Parse * pParse, /* Parse context (for malloc() and error repo while (pSubq) { pNew = sqlExprDup(pParse->db, pWhere, 0); pNew = substExpr(pParse, pNew, iCursor, pSubq->pEList); - pSubq->pWhere = - sqlExprAnd(pParse->db, pSubq->pWhere, pNew); + if (pSubq->pWhere != NULL || pNew != NULL) { + pSubq->pWhere = + sql_and_expr_create(pParse->db, + pSubq->pWhere, + pNew); + if (pSubq->pWhere == NULL) + sql_parser_error(pParse); + } pSubq = pSubq->pPrior; } } @@ -4505,8 +4537,10 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; - p->pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = sql_op_expr_create(db, TK_ASTERISK, NULL); + if (expr == NULL) + sql_parser_error(pParse); + p->pEList = sql_expr_list_append(pParse->db, NULL, expr); p->op = TK_SELECT; p->pWhere = 0; pNew->pGroupBy = 0; @@ -4990,18 +5024,24 @@ selectExpander(Walker * pWalker, Select * p) continue; } } - pRight = - sqlExpr(db, TK_ID, + pRight = sql_op_expr_create( + db, TK_ID, zName); + if (pRight == NULL) + sql_parser_error(pParse); zColname = zName; zToFree = 0; if (longNames || pTabList->nSrc > 1) { Expr *pLeft; - pLeft = - sqlExpr(db, + pLeft = sql_op_expr_create( + db, TK_ID, zTabName); + if (pLeft == NULL) { + sql_parser_error( + pParse); + } pExpr = sqlPExpr(pParse, TK_DOT, diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 531445f33..82aad0077 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3254,13 +3254,70 @@ void sqlClearTempRegCache(Parse *); #ifdef SQL_DEBUG int sqlNoTempsInRange(Parse *, int, int); #endif -Expr *sqlExprAlloc(sql *, int, const Token *, int); -Expr *sqlExpr(sql *, int, const char *); -Expr *sqlExprInteger(sql *, int); + +/* + * This routine is the core allocator for Expr nodes. + * Construct a new expression node and return a pointer to it. + * Memory for this node and for the token argument is a single + * allocation obtained from sqlDbMalloc(). The calling function + * is responsible for making sure the node eventually gets freed. + * + * If dequote is true, then the token (if it exists) is dequoted. + * If dequote is false, no dequoting is performed. The deQuote + * parameter is ignored if token is NULL or if the token does + * not appear to be quoted. If the quotes were of the form "..." + * (double-quotes) then the EP_DblQuoted flag is set on the + * expression node. + * + * Special case: If op==TK_INTEGER and token points to a string + * that can be translated into a 32-bit integer, then the token is + * not stored in u.zToken. Instead, the integer values is written + * into u.iValue and the EP_IntValue flag is set. No extra storage + * is allocated to hold the integer text and the dequote flag is + * ignored. + * @param db The database connection. + * @param op Expression opcode (TK_*). + * @param token Source token. Might be NULL. + * @param dequote True to dequote string. + * @retval not NULL new expression object on success. + * @retval NULL otherwise. + */ +struct Expr * +sql_expr_create(struct sql *db, int op, const Token *token, bool dequote); + +/* + * Allocate a new expression node from a zero-terminated token + * that has already been dequoted. + * @param db The database connection. + * @param op Expression opcode. + * @param name The object name string. + * @retval not NULL expression pointer on success. + * @retval NULL otherwise. + */ +struct Expr * +sql_op_expr_create(struct sql *db, int op, const char *name); + void sqlExprAttachSubtrees(sql *, Expr *, Expr *, Expr *); Expr *sqlPExpr(Parse *, int, Expr *, Expr *); void sqlPExprAddSelect(Parse *, Expr *, Select *); -Expr *sqlExprAnd(sql *, Expr *, Expr *); + +/* + * Join two expressions using an AND operator. If either + * expression is NULL, then just return the other expression. + * + * If one side or the other of the AND is known to be false, then + * instead of returning an AND expression, just return a constant + * expression with a value of false. + * @param db The database connection. + * @param left_expr The left-branch expresion to join. + * @param right_expr The right-branch expression to join. + * @retval not NULL new expression root node pointer on success. + * @retval NULL otherwise. + */ +struct Expr * +sql_and_expr_create(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr); + Expr *sqlExprFunction(Parse *, ExprList *, Token *); void sqlExprAssignVarNumber(Parse *, Expr *, u32); ExprList *sqlExprListAppendVector(Parse *, ExprList *, IdList *, Expr *); @@ -4724,7 +4781,20 @@ void sqlAppendChar(StrAccum *, int, char); char *sqlStrAccumFinish(StrAccum *); void sqlStrAccumReset(StrAccum *); void sqlSelectDestInit(SelectDest *, int, int, int); -Expr *sqlCreateColumnExpr(sql *, SrcList *, int, int); + +/* + * Allocate and return a pointer to an expression to load the + * column from datasource src_idx in SrcList src_list. + * @param db The database connection. + * @param src_list The source list described with FROM clause. + * @param src_idx The resource index to use in src_list. + * @param column The column index. + * @retval not NULL expression pointer on success. + * @retval NULL otherwise. + */ +struct Expr * +sql_column_expr_create(struct sql *db, struct SrcList *src_list, + int src_idx, int column); int sqlExprCheckIN(Parse *, Expr *); diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 04b79ab36..01ecb9464 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -1437,7 +1437,14 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W continue; testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); pExpr = sqlExprDup(db, pExpr, 0); - pAndExpr = sqlExprAnd(db, pAndExpr, pExpr); + if (pAndExpr != NULL || pExpr != NULL) { + pAndExpr = + sql_and_expr_create(db, + pAndExpr, + pExpr); + if (pAndExpr == NULL) + sql_parser_error(pParse); + } } if (pAndExpr) { pAndExpr = diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index fa906e305..c2213f229 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -307,8 +307,10 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix, Expr *pPrefix; *pisComplete = c == MATCH_ALL_WILDCARD && z[cnt + 1] == 0; - pPrefix = sqlExpr(db, TK_STRING, z); - if (pPrefix) + pPrefix = sql_op_expr_create(db, TK_STRING, z); + if (pPrefix == NULL) + sql_parser_error(pParse); + else pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if (op == TK_VARIABLE) { @@ -1306,10 +1308,12 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; - - pNewExpr = sqlPExpr(pParse, TK_GT, - sqlExprDup(db, pLeft, 0), - sqlExprAlloc(db, TK_NULL, 0, 0)); + struct Expr *expr = + sql_expr_create(db, TK_NULL, NULL, false); + if (expr == NULL) + sql_parser_error(pParse); + pNewExpr = sqlPExpr(pParse, TK_GT, sqlExprDup(db, pLeft, 0), + expr); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL | TERM_DYNAMIC | @@ -1502,9 +1506,11 @@ sqlWhereTabFuncArgs(Parse * pParse, /* Parsing context */ space_def->name, j); return; } - pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0); - if (pColRef == 0) + pColRef = sql_expr_create(pParse->db, TK_COLUMN, NULL, false); + if (pColRef == NULL) { + sql_parser_error(pParse); return; + } pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pColRef->space_def = space_def; -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 6/7] sql: refactor sql_expr_create " Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Refactored sql_expr_create routine to use diag_set in case > of memory allocation error. Probably it was said several times, but we use _new suffix for constructors, creating an object on their own memory. If you decided to rename sqlExprAlloc, then use sql_expr_new. > This change is necessary because the sql_expr_create body has a > sqlNameFromToken call that will be changed in subsequent patches. > > Needed for #3931 > --- > src/box/sql/build.c | 44 +++++-- > src/box/sql/expr.c | 249 +++++++++++++++--------------------- > src/box/sql/fk_constraint.c | 185 +++++++++++++++++---------- > src/box/sql/parse.y | 35 +++-- > src/box/sql/resolve.c | 47 ++++--- > src/box/sql/select.c | 90 +++++++++---- > src/box/sql/sqlInt.h | 80 +++++++++++- > src/box/sql/wherecode.c | 9 +- > src/box/sql/whereexpr.c | 22 ++-- > 9 files changed, 462 insertions(+), 299 deletions(-) > ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > Probably it was said several times, but we use _new suffix > for constructors, creating an object on their own memory. If > you decided to rename sqlExprAlloc, then use sql_expr_new. > >> This change is necessary because the sql_expr_create body has a >> sqlNameFromToken call that will be changed in subsequent patches. Accounted here and everywhere. ====================================================== Refactored sqlExpr routine as sql_expr_new and reworked it to set diag message in case of memory allocation error. Also performed some additional name refactoring in adjacent places. This change is necessary because the sqlExpr body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/build.c | 40 ++++-- src/box/sql/expr.c | 246 +++++++++++++++--------------------- src/box/sql/fk_constraint.c | 182 ++++++++++++++++---------- src/box/sql/parse.y | 35 +++-- src/box/sql/resolve.c | 46 ++++--- src/box/sql/select.c | 87 +++++++++---- src/box/sql/sqlInt.h | 84 +++++++++++- src/box/sql/wherecode.c | 8 +- src/box/sql/whereexpr.c | 21 +-- 9 files changed, 450 insertions(+), 299 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 1864bd0ad..8eac9fdb3 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -624,9 +624,12 @@ sqlAddPrimaryKey(Parse * pParse, /* Parsing context */ struct ExprList *list; struct Token token; sqlTokenInit(&token, space->def->fields[iCol].name); - list = sql_expr_list_append(db, NULL, - sqlExprAlloc(db, TK_ID, - &token, 0)); + struct Expr *expr = sql_expr_new(db, TK_ID, &token, false); + if (expr == NULL) { + sql_parser_error(pParse); + goto primary_key_exit; + } + list = sql_expr_list_append(db, NULL, expr); if (list == NULL) goto primary_key_exit; sql_create_index(pParse, 0, 0, list, 0, SORT_ORDER_ASC, @@ -1369,12 +1372,14 @@ sql_id_eq_str_expr(struct Parse *parse, const char *col_name, const char *col_value) { struct sql *db = parse->db; - - struct Expr *col_name_expr = sqlExpr(db, TK_ID, col_name); - if (col_name_expr == NULL) + struct Expr *col_name_expr = sql_op_expr_new(db, TK_ID, col_name); + if (col_name_expr == NULL) { + sql_parser_error(parse); return NULL; - struct Expr *col_value_expr = sqlExpr(db, TK_STRING, col_value); + } + struct Expr *col_value_expr = sql_op_expr_new(db, TK_STRING, col_value); if (col_value_expr == NULL) { + sql_parser_error(parse); sql_expr_delete(db, col_name_expr, false); return NULL; } @@ -1397,13 +1402,19 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, struct Expr *where = NULL; if (idx_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_new(db, expr, where); + if (where == NULL) + sql_parser_error(parse); + } } if (table_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_new(db, expr, where); + if (where == NULL) + sql_parser_error(parse); + } } /** * On memory allocation error sql_table delete_from @@ -2259,9 +2270,10 @@ sql_create_index(struct Parse *parse, struct Token *token, struct Token prev_col; uint32_t last_field = def->field_count - 1; sqlTokenInit(&prev_col, def->fields[last_field].name); - col_list = sql_expr_list_append(parse->db, NULL, - sqlExprAlloc(db, TK_ID, - &prev_col, 0)); + struct Expr *expr = sql_expr_new(db, TK_ID, &prev_col, false); + if (expr == NULL) + goto tnt_error; + col_list = sql_expr_list_append(parse->db, NULL, expr); if (col_list == NULL) goto exit_create_index; assert(col_list->nExpr == 1); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 44b6ce11f..f5b2926c8 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -149,16 +149,17 @@ sqlExprAddCollateToken(Parse * pParse, /* Parsing context */ int dequote /* True to dequote pCollName */ ) { - if (pCollName->n > 0) { - Expr *pNew = - sqlExprAlloc(pParse->db, TK_COLLATE, pCollName, - dequote); - if (pNew) { - pNew->pLeft = pExpr; - pNew->flags |= EP_Collate | EP_Skip; - pExpr = pNew; - } + if (pCollName->n == 0) + return pExpr; + struct Expr *new_expr = + sql_expr_new(pParse->db, TK_COLLATE, pCollName, dequote); + if (new_expr == NULL) { + sql_parser_error(pParse); + return NULL; } + new_expr->pLeft = pExpr; + new_expr->flags |= EP_Collate | EP_Skip; + pExpr = new_expr; return pExpr; } @@ -854,113 +855,62 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) #define exprSetHeight(y) #endif /* SQL_MAX_EXPR_DEPTH>0 */ -/* - * This routine is the core allocator for Expr nodes. - * - * Construct a new expression node and return a pointer to it. Memory - * for this node and for the pToken argument is a single allocation - * obtained from sqlDbMalloc(). The calling function - * is responsible for making sure the node eventually gets freed. - * - * If dequote is true, then the token (if it exists) is dequoted. - * If dequote is false, no dequoting is performed. The deQuote - * parameter is ignored if pToken is NULL or if the token does not - * appear to be quoted. If the quotes were of the form "..." (double-quotes) - * then the EP_DblQuoted flag is set on the expression node. - * - * Special case: If op==TK_INTEGER and pToken points to a string that - * can be translated into a 32-bit integer, then the token is not - * stored in u.zToken. Instead, the integer values is written - * into u.iValue and the EP_IntValue flag is set. No extra storage - * is allocated to hold the integer text and the dequote flag is ignored. - */ -Expr * -sqlExprAlloc(sql * db, /* Handle for sqlDbMallocRawNN() */ - int op, /* Expression opcode */ - const Token * pToken, /* Token argument. Might be NULL */ - int dequote /* True to dequote */ - ) +struct Expr * +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) { - Expr *pNew; - int nExtra = 0; - int iValue = 0; - - assert(db != 0); - if (pToken) { - if (op != TK_INTEGER || pToken->z == 0 - || sqlGetInt32(pToken->z, &iValue) == 0) { - nExtra = pToken->n + 1; - assert(iValue >= 0); + int extra_sz = 0; + int val = 0; + if (token != NULL) { + if (op != TK_INTEGER || token->z == NULL || + sqlGetInt32(token->z, &val) == 0) { + extra_sz = token->n + 1; + assert(val >= 0); } } - pNew = sqlDbMallocRawNN(db, sizeof(Expr) + nExtra); - if (pNew) { - memset(pNew, 0, sizeof(Expr)); - pNew->op = (u8) op; - pNew->iAgg = -1; - if (pToken) { - if (nExtra == 0) { - pNew->flags |= EP_IntValue; - pNew->u.iValue = iValue; - } else { - pNew->u.zToken = (char *)&pNew[1]; - assert(pToken->z != 0 || pToken->n == 0); - if (pToken->n) - memcpy(pNew->u.zToken, pToken->z, - pToken->n); - pNew->u.zToken[pToken->n] = 0; - if (dequote){ - if (pNew->u.zToken[0] == '"') - pNew->flags |= EP_DblQuoted; - if (pNew->op == TK_ID || - pNew->op == TK_COLLATE || - pNew->op == TK_FUNCTION){ - sqlNormalizeName(pNew->u.zToken); - }else{ - sqlDequote(pNew->u.zToken); - } - } - } - } -#if SQL_MAX_EXPR_DEPTH>0 - pNew->nHeight = 1; -#endif + struct Expr *expr = sqlDbMallocRawNN(db, sizeof(*expr) + extra_sz); + if (expr == NULL) { + diag_set(OutOfMemory, sizeof(*expr), "sqlDbMallocRawNN", + "expr"); + return NULL; } - return pNew; -} -/* - * Allocate a new expression node from a zero-terminated token that has - * already been dequoted. - */ -Expr * -sqlExpr(sql * db, /* Handle for sqlDbMallocZero() (may be null) */ - int op, /* Expression opcode */ - const char *zToken /* Token argument. Might be NULL */ - ) -{ - Token x; - x.z = zToken; - x.n = zToken ? sqlStrlen30(zToken) : 0; - return sqlExprAlloc(db, op, &x, 0); + memset(expr, 0, sizeof(*expr)); + expr->op = (u8)op; + expr->iAgg = -1; +#if SQL_MAX_EXPR_DEPTH > 0 + expr->nHeight = 1; +#endif + if (token == NULL) + return expr; + + if (extra_sz == 0) { + expr->flags |= EP_IntValue; + expr->u.iValue = val; + } else { + expr->u.zToken = (char *)&expr[1]; + assert(token->z != NULL || token->n == 0); + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) { + if (expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION) + sqlNormalizeName(expr->u.zToken); + else + sqlDequote(expr->u.zToken); + } + } + return expr; } -/* Allocate a new expression and initialize it as integer. - * @param db sql engine. - * @param value Value to initialize by. - * - * @retval not NULL Allocated and initialized expr. - * @retval NULL Memory error. - */ -Expr * -sqlExprInteger(sql * db, int value) +struct Expr * +sql_op_expr_new(struct sql *db, int op, const char *name) { - Expr *ret = sqlExpr(db, TK_INTEGER, NULL); - if (ret != NULL) { - ret->flags = EP_IntValue; - ret->u.iValue = value; - } - return ret; + struct Token name_token; + name_token.z = name; + name_token.n = name != NULL ? strlen(name) : 0; + return sql_expr_new(db, op, &name_token, false); } /* @@ -1006,8 +956,15 @@ sqlPExpr(Parse * pParse, /* Parsing context */ { Expr *p; if (op == TK_AND && pParse->nErr == 0) { - /* Take advantage of short-circuit false optimization for AND */ - p = sqlExprAnd(pParse->db, pLeft, pRight); + /* + * Take advantage of short-circuit false + * optimization for AND. + */ + if (pLeft == NULL && pRight == NULL) + return NULL; + p = sql_and_expr_new(pParse->db, pLeft, pRight); + if (p == NULL) + sql_parser_error(pParse); } else { p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)); if (p) { @@ -1076,30 +1033,24 @@ exprAlwaysFalse(Expr * p) return v == 0; } -/* - * Join two expressions using an AND operator. If either expression is - * NULL, then just return the other expression. - * - * If one side or the other of the AND is known to be false, then instead - * of returning an AND expression, just return a constant expression with - * a value of false. - */ -Expr * -sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) +struct Expr * +sql_and_expr_new(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr) { - if (pLeft == 0) { - return pRight; - } else if (pRight == 0) { - return pLeft; - } else if (exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight)) { - sql_expr_delete(db, pLeft, false); - sql_expr_delete(db, pRight, false); - return sqlExprAlloc(db, TK_INTEGER, &sqlIntTokens[0], - 0); + if (left_expr == NULL) { + assert(right_expr != NULL); + return right_expr; + } else if (right_expr == NULL) { + assert(left_expr != NULL); + return left_expr; + } else if (exprAlwaysFalse(left_expr) || exprAlwaysFalse(right_expr)) { + sql_expr_delete(db, left_expr, false); + sql_expr_delete(db, right_expr, false); + return sql_expr_new(db, TK_INTEGER, &sqlIntTokens[0], false); } else { - Expr *pNew = sqlExprAlloc(db, TK_AND, 0, 0); - sqlExprAttachSubtrees(db, pNew, pLeft, pRight); - return pNew; + struct Expr *new_expr = sql_expr_new(db, TK_AND, NULL, false); + sqlExprAttachSubtrees(db, new_expr, left_expr, right_expr); + return new_expr; } } @@ -1110,18 +1061,18 @@ sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) Expr * sqlExprFunction(Parse * pParse, ExprList * pList, Token * pToken) { - Expr *pNew; - sql *db = pParse->db; - assert(pToken); - pNew = sqlExprAlloc(db, TK_FUNCTION, pToken, 1); - if (pNew == 0) { - sql_expr_list_delete(db, pList); /* Avoid memory leak when malloc fails */ - return 0; + struct sql *db = pParse->db; + assert(pToken != NULL); + struct Expr *new_expr = sql_expr_new(db, TK_FUNCTION, pToken, true); + if (new_expr == NULL) { + sql_expr_list_delete(db, pList); + sql_parser_error(pParse); + return NULL; } - pNew->x.pList = pList; - assert(!ExprHasProperty(pNew, EP_xIsSelect)); - sqlExprSetHeightAndFlags(pParse, pNew); - return pNew; + new_expr->x.pList = pList; + assert(!ExprHasProperty(new_expr, EP_xIsSelect)); + sqlExprSetHeightAndFlags(pParse, new_expr); + return new_expr; } /* @@ -2911,10 +2862,11 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */ } if (pSel->pLimit == NULL) { pSel->pLimit = - sqlExprAlloc(pParse->db, TK_INTEGER, - &sqlIntTokens[1], - 0); - if (pSel->pLimit != NULL) { + sql_expr_new(pParse->db, TK_INTEGER, + &sqlIntTokens[1], false); + if (pSel->pLimit == NULL) { + sql_parser_error(pParse); + } else { ExprSetProperty(pSel->pLimit, EP_System); } diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index d1eb6670a..ceba2db9f 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -309,31 +309,30 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, * the data for given space. regBase+1 holds the first column. * regBase+2 holds the second column, and so forth. * - * @param pParse Parsing and code generating context. + * @param parser The parsing context. * @param def Definition of space whose content is at r[regBase]... - * @param regBase Contents of table defined by def. - * @param iCol Which column of space is desired. - * @return an Expr object that refers to a memory register - * corresponding to column iCol of given space. + * @param reg_base Contents of table table. + * @param column Index of table column is desired. + * @retval Not NULL expression pointer on success. + * @retval NULL Otherwise. */ -static Expr * -space_field_register(Parse *pParse, struct space_def *def, int regBase, - i16 iCol) +static struct Expr * +sql_register_expr_new(struct Parse *parser, struct space_def *def, int reg_base, + int column) { - Expr *pExpr; - sql *db = pParse->db; - - pExpr = sqlExpr(db, TK_REGISTER, 0); - if (pExpr) { - if (iCol >= 0) { - pExpr->iTable = regBase + iCol + 1; - pExpr->type = def->fields[iCol].type; - } else { - pExpr->iTable = regBase; - pExpr->type = FIELD_TYPE_INTEGER; - } + struct Expr *expr = sql_op_expr_new(parser->db, TK_REGISTER, NULL); + if (expr == NULL) { + sql_parser_error(parser); + return NULL; + } + if (column >= 0) { + expr->iTable = reg_base + column + 1; + expr->type = def->fields[column].type; + } else { + expr->iTable = reg_base; + expr->type = FIELD_TYPE_INTEGER; } - return pExpr; + return expr; } /** @@ -346,16 +345,17 @@ space_field_register(Parse *pParse, struct space_def *def, int regBase, * @retval not NULL on success. * @retval NULL on error. */ -static Expr * -exprTableColumn(sql * db, struct space_def *def, int cursor, i16 column) +static struct Expr * +sql_column_cursor_expr_create(struct sql *db, struct space_def *def, + int cursor, int column) { - Expr *pExpr = sqlExpr(db, TK_COLUMN, 0); - if (pExpr) { - pExpr->space_def = def; - pExpr->iTable = cursor; - pExpr->iColumn = column; - } - return pExpr; + struct Expr *expr = sql_op_expr_new(db, TK_COLUMN, NULL); + if (expr == NULL) + return NULL; + expr->space_def = def; + expr->iTable = cursor; + expr->iColumn = column; + return expr; } /* @@ -435,12 +435,18 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; struct Expr *pexpr = - space_field_register(parser, def, reg_data, fieldno); + sql_register_expr_new(parser, def, reg_data, fieldno); fieldno = fk_def->links[i].child_field; const char *field_name = child_space->def->fields[fieldno].name; - struct Expr *chexpr = sqlExpr(db, TK_ID, field_name); + struct Expr *chexpr = sql_op_expr_new(db, TK_ID, field_name); + if (chexpr == NULL) + sql_parser_error(parser); struct Expr *eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - where = sqlExprAnd(db, where, eq); + if (where != NULL || eq != NULL) { + where = sql_and_expr_new(db, where, eq); + if (where == NULL) + sql_parser_error(parser); + } } /* @@ -456,15 +462,26 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, struct Expr *expr = NULL, *pexpr, *chexpr, *eq; for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; - pexpr = space_field_register(parser, def, reg_data, - fieldno); - chexpr = exprTableColumn(db, def, src->a[0].iCursor, - fieldno); + pexpr = sql_register_expr_new(parser, def, reg_data, + fieldno); + int cursor = src->a[0].iCursor; + chexpr = sql_column_cursor_expr_create(db, def, cursor, + fieldno); + if (chexpr == NULL) + sql_parser_error(parser); eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - expr = sqlExprAnd(db, expr, eq); + if (expr != NULL || eq != NULL) { + expr = sql_and_expr_new(db, expr, eq); + if (expr == NULL) + sql_parser_error(parser); + } } struct Expr *pNe = sqlPExpr(parser, TK_NOT, expr, 0); - where = sqlExprAnd(db, where, pNe); + if (where != NULL || pNe != NULL) { + where = sql_and_expr_new(db, where, pNe); + if (where == NULL) + sql_parser_error(parser); + } } /* Resolve the references in the WHERE clause. */ @@ -785,14 +802,26 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * type and collation sequence associated with * the parent table are used for the comparison. */ - struct Expr *to_col = - sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); - struct Expr *from_col = - sqlExprAlloc(db, TK_ID, &t_from_col, 0); - struct Expr *eq = sqlPExpr(pParse, TK_EQ, to_col, from_col); - where = sqlExprAnd(db, where, eq); + struct Expr *old_expr = NULL, *new_expr = NULL, *expr = NULL; + old_expr = sql_expr_new(db, TK_ID, &t_old, false); + if (old_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); + struct Expr *from_col_expr = + sql_expr_new(db, TK_ID, &t_from_col, false); + if (from_col_expr == NULL) + sql_parser_error(pParse); + struct Expr *to_col_expr = + sqlPExpr(pParse, TK_DOT, old_expr, expr); + struct Expr *eq = + sqlPExpr(pParse, TK_EQ, to_col_expr, from_col_expr); + if (where != NULL || eq != NULL) { + where = sql_and_expr_new(db, where, eq); + if (where == NULL) + sql_parser_error(pParse); + } /* * For ON UPDATE, construct the next term of the @@ -810,12 +839,22 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * no_action_needed(colN)) */ if (is_update) { + old_expr = sql_expr_new(db, TK_ID, &t_old, false); + if (old_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); struct Expr *old_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + old_expr, expr); + new_expr = sql_expr_new(db, TK_ID, &t_new, false); + if (new_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + sql_parser_error(pParse); struct Expr *new_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_new, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + new_expr, expr); struct Expr *old_is_null = sqlPExpr( pParse, TK_ISNULL, sqlExprDup(db, old_val, 0), NULL); @@ -828,29 +867,41 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Expr *no_action_needed = sqlPExpr(pParse, TK_OR, old_is_null, non_null_eq); - when = sqlExprAnd(db, when, no_action_needed); + if (when != NULL || no_action_needed != NULL) { + when = sql_and_expr_new(db, when, + no_action_needed); + if (when == NULL) + sql_parser_error(pParse); + } } if (action != FKEY_ACTION_RESTRICT && (action != FKEY_ACTION_CASCADE || is_update)) { struct Expr *new, *d; if (action == FKEY_ACTION_CASCADE) { - new = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, - &t_new, 0), - sqlExprAlloc(db, TK_ID, - &t_to_col, - 0)); + new_expr = sql_expr_new(db, TK_ID, &t_new, + false); + if (new_expr == NULL) + sql_parser_error(pParse); + expr = sql_expr_new(db, TK_ID, &t_to_col, + false); + if (expr == NULL) + sql_parser_error(pParse); + new = sqlPExpr(pParse, TK_DOT, new_expr, expr); } else if (action == FKEY_ACTION_SET_DEFAULT) { d = child_fields[chcol].default_value_expr; if (d != NULL) { new = sqlExprDup(db, d, 0); } else { - new = sqlExprAlloc(db, TK_NULL, - NULL, 0); + new = sql_expr_new(db, TK_NULL, NULL, + false); + if (new == NULL) + sql_parser_error(pParse); } } else { - new = sqlExprAlloc(db, TK_NULL, NULL, 0); + new = sql_expr_new(db, TK_NULL, NULL, false); + if (new == NULL) + sql_parser_error(pParse); } list = sql_expr_list_append(db, list, new); sqlExprListSetName(pParse, list, &t_from_col, 0); @@ -864,9 +915,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Token err; err.z = space_name; err.n = name_len; - struct Expr *r = sqlExpr(db, TK_RAISE, "FOREIGN KEY "\ - "constraint failed"); - if (r != NULL) + struct Expr *r = + sql_op_expr_new(db, TK_RAISE, + "FOREIGN KEY constraint failed"); + if (r == NULL) + sql_parser_error(pParse); + else r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; struct SrcList *src_list = sql_src_list_append(db, NULL, &err); if (src_list == NULL) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index f0388b481..1a5882dda 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -529,12 +529,16 @@ selcollist(A) ::= sclp(A) expr(X) as(Y). { sqlExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { - Expr *p = sqlExpr(pParse->db, TK_ASTERISK, 0); + struct Expr *p = sql_op_expr_new(pParse->db, TK_ASTERISK, NULL); + if (p == NULL) + sql_parser_error(pParse); A = sql_expr_list_append(pParse->db, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { Expr *pRight = sqlPExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = sqlExprAlloc(pParse->db, TK_ID, &X, 1); + struct Expr *pLeft = sql_expr_new(pParse->db, TK_ID, &X, true); + if (pLeft == NULL) + sql_parser_error(pParse); Expr *pDot = sqlPExpr(pParse, TK_DOT, pLeft, pRight); A = sql_expr_list_append(pParse->db,A, pDot); } @@ -887,15 +891,21 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { - Expr *temp1 = sqlExprAlloc(pParse->db, TK_ID, &X, 1); - Expr *temp2 = sqlExprAlloc(pParse->db, TK_ID, &Y, 1); + struct Expr *temp1 = sql_expr_new(pParse->db, TK_ID, &X, true); + if (temp1 == NULL) + sql_parser_error(pParse); + struct Expr *temp2 = sql_expr_new(pParse->db, TK_ID, &Y, true); + if (temp2 == NULL) + sql_parser_error(pParse); spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlPExpr(pParse, TK_DOT, temp1, temp2); } term(A) ::= FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= INTEGER(X). { - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER, &X, 1); + A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &X, true); + if (A.pExpr == NULL) + sql_parser_error(pParse); A.pExpr->type = FIELD_TYPE_INTEGER; A.zStart = X.z; A.zEnd = X.z + X.n; @@ -928,7 +938,9 @@ expr(A) ::= expr(A) COLLATE id(C). { %ifndef SQL_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_CAST, 0, 1); + A.pExpr = sql_expr_new(pParse->db, TK_CAST, NULL, true); + if (A.pExpr == NULL) + sql_parser_error(pParse); A.pExpr->type = T.type; sqlExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0); } @@ -1122,7 +1134,9 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] { ** regardless of the value of expr1. */ sql_expr_delete(pParse->db, A.pExpr, false); - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER,&sqlIntTokens[N],1); + A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &sqlIntTokens[N], true); + if (A.pExpr == NULL) + sql_parser_error(pParse); }else if( Y->nExpr==1 ){ /* Expressions of the form: ** @@ -1454,10 +1468,11 @@ expr(A) ::= RAISE(X) LP IGNORE RP(Y). { } expr(A) ::= RAISE(X) LP raisetype(T) COMMA STRING(Z) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_RAISE, &Z, 1); - if( A.pExpr ) { + A.pExpr = sql_expr_new(pParse->db, TK_RAISE, &Z, true); + if(A.pExpr == NULL) + sql_parser_error(pParse); + else A.pExpr->on_conflict_action = (enum on_conflict_action) T; - } } %type raisetype {int} diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index aed9e261d..66a795bb4 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -487,26 +487,20 @@ lookupName(Parse * pParse, /* The parsing context */ } } -/* - * Allocate and return a pointer to an expression to load the column iCol - * from datasource iSrc in SrcList pSrc. - */ -Expr * -sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol) +struct Expr * +sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, + int column) { - Expr *p = sqlExprAlloc(db, TK_COLUMN, 0, 0); - if (p) { - struct SrcList_item *pItem = &pSrc->a[iSrc]; - p->space_def = pItem->space->def; - p->iTable = pItem->iCursor; - p->iColumn = (ynVar) iCol; - testcase(iCol == BMS); - testcase(iCol == BMS - 1); - pItem->colUsed |= - ((Bitmask) 1) << (iCol >= BMS ? BMS - 1 : iCol); - ExprSetProperty(p, EP_Resolved); - } - return p; + struct Expr *expr = sql_expr_new(db, TK_COLUMN, NULL, false); + if (expr == NULL) + return NULL; + struct SrcList_item *item = &src_list->a[src_idx]; + expr->space_def = item->space->def; + expr->iTable = item->iCursor; + expr->iColumn = column; + item->colUsed |= ((Bitmask) 1) << (column >= BMS ? BMS - 1 : column); + ExprSetProperty(expr, EP_Resolved); + return expr; } /* @@ -1000,9 +994,12 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context. Leave error messages /* Convert the ORDER BY term into an integer column number iCol, * taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + Expr *pNew = + sql_op_expr_new(db, TK_INTEGER, NULL); + if (pNew == NULL) { + sql_parser_error(pParse); return 1; + } pNew->flags |= EP_IntValue; pNew->u.iValue = iCol; if (pItem->pExpr == pE) { @@ -1348,9 +1345,10 @@ resolveSelectStep(Walker * pWalker, Select * p) * restrict it directly). */ sql_expr_delete(db, p->pLimit, false); - p->pLimit = - sqlExprAlloc(db, TK_INTEGER, - &sqlIntTokens[1], 0); + p->pLimit = sql_expr_new(db, TK_INTEGER, + &sqlIntTokens[1], false); + if (p->pLimit == NULL) + sql_parser_error(pParse); } else { if (sqlResolveExprNames(&sNC, p->pHaving)) return WRC_Abort; diff --git a/src/box/sql/select.c b/src/box/sql/select.c index fda4296cc..5434a2ab0 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -164,8 +164,10 @@ sqlSelectNew(Parse * pParse, /* Parsing context */ pNew = &standin; } if (pEList == 0) { - pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + if (expr == NULL) + sql_parser_error(pParse); + pEList = sql_expr_list_append(pParse->db, NULL, expr); } struct session MAYBE_UNUSED *user_session; user_session = current_session(); @@ -486,7 +488,6 @@ addWhereTerm(Parse * pParse, /* Parsing context */ int isOuterJoin, /* True if this is an OUTER join */ Expr ** ppWhere) /* IN/OUT: The WHERE clause to add to */ { - sql *db = pParse->db; Expr *pE1; Expr *pE2; Expr *pEq; @@ -496,8 +497,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ assert(pSrc->a[iLeft].space != NULL); assert(pSrc->a[iRight].space != NULL); - pE1 = sqlCreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = sqlCreateColumnExpr(db, pSrc, iRight, iColRight); + pE1 = sql_column_expr_new(pParse->db, pSrc, iLeft, iColLeft); + if (pE1 == NULL) + sql_parser_error(pParse); + pE2 = sql_column_expr_new(pParse->db, pSrc, iRight, iColRight); + if (pE2 == NULL) + sql_parser_error(pParse); pEq = sqlPExpr(pParse, TK_EQ, pE1, pE2); if (pEq && isOuterJoin) { @@ -506,7 +511,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ ExprSetVVAProperty(pEq, EP_NoReduce); pEq->iRightJoinTable = (i16) pE2->iTable; } - *ppWhere = sqlExprAnd(db, *ppWhere, pEq); + if (*ppWhere != NULL || pEq != NULL) { + *ppWhere = sql_and_expr_new(pParse->db, *ppWhere, pEq); + if (*ppWhere == NULL) + sql_parser_error(pParse); + } + return; } /* @@ -627,8 +637,13 @@ sqlProcessJoin(Parse * pParse, Select * p) if (pRight->pOn) { if (isOuter) setJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = - sqlExprAnd(pParse->db, p->pWhere, pRight->pOn); + if (p->pWhere != NULL || pRight->pOn != NULL) { + p->pWhere = + sql_and_expr_new(pParse->db, p->pWhere, + pRight->pOn); + if (p->pWhere == NULL) + sql_parser_error(pParse); + } pRight->pOn = 0; } @@ -3332,9 +3347,12 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */ break; } if (j == nOrderBy) { - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + Expr *pNew = + sql_op_expr_new(db, TK_INTEGER, NULL); + if (pNew == NULL) { + sql_parser_error(pParse); return SQL_NOMEM; + } pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sql_expr_list_append(pParse->db, @@ -4207,17 +4225,22 @@ flattenSubquery(Parse * pParse, /* Parsing context */ assert(pParent->pHaving == 0); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; - pParent->pHaving = sqlExprAnd(db, - sqlExprDup(db, - pSub->pHaving, - 0), - pParent->pHaving); + struct Expr *sub_having = sqlExprDup(db, pSub->pHaving, 0); + if (sub_having != NULL || pParent->pHaving != NULL) { + pParent->pHaving = + sql_and_expr_new(db, sub_having, + pParent->pHaving); + if (pParent->pHaving == NULL) + sql_parser_error(pParse); + } assert(pParent->pGroupBy == 0); pParent->pGroupBy = sql_expr_list_dup(db, pSub->pGroupBy, 0); - } else { + } else if (pWhere != NULL || pParent->pWhere != NULL) { pParent->pWhere = - sqlExprAnd(db, pWhere, pParent->pWhere); + sql_and_expr_new(db, pWhere, pParent->pWhere); + if (pParent->pWhere == NULL) + sql_parser_error(pParse); } substSelect(pParse, pParent, iParent, pSub->pEList, 0); @@ -4322,8 +4345,13 @@ pushDownWhereTerms(Parse * pParse, /* Parse context (for malloc() and error repo while (pSubq) { pNew = sqlExprDup(pParse->db, pWhere, 0); pNew = substExpr(pParse, pNew, iCursor, pSubq->pEList); - pSubq->pWhere = - sqlExprAnd(pParse->db, pSubq->pWhere, pNew); + if (pSubq->pWhere != NULL || pNew != NULL) { + pSubq->pWhere = + sql_and_expr_new(pParse->db, + pSubq->pWhere, pNew); + if (pSubq->pWhere == NULL) + sql_parser_error(pParse); + } pSubq = pSubq->pPrior; } } @@ -4504,8 +4532,10 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; - p->pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + if (expr == NULL) + sql_parser_error(pParse); + p->pEList = sql_expr_list_append(pParse->db, NULL, expr); p->op = TK_SELECT; p->pWhere = 0; pNew->pGroupBy = 0; @@ -4989,18 +5019,23 @@ selectExpander(Walker * pWalker, Select * p) continue; } } - pRight = - sqlExpr(db, TK_ID, - zName); + pRight = sql_op_expr_new(db, + TK_ID, zName); + if (pRight == NULL) + sql_parser_error(pParse); zColname = zName; zToFree = 0; if (longNames || pTabList->nSrc > 1) { Expr *pLeft; - pLeft = - sqlExpr(db, + pLeft = sql_op_expr_new( + db, TK_ID, zTabName); + if (pLeft == NULL) { + sql_parser_error( + pParse); + } pExpr = sqlPExpr(pParse, TK_DOT, diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 4e6c5717b..4401ea8b7 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3218,13 +3218,73 @@ void sqlClearTempRegCache(Parse *); #ifdef SQL_DEBUG int sqlNoTempsInRange(Parse *, int, int); #endif -Expr *sqlExprAlloc(sql *, int, const Token *, int); -Expr *sqlExpr(sql *, int, const char *); -Expr *sqlExprInteger(sql *, int); + +/** + * This routine is the core allocator for Expr nodes. + * Construct a new expression node and return a pointer to it. + * Memory for this node and for the token argument is a single + * allocation obtained from sqlDbMalloc(). The calling function + * is responsible for making sure the node eventually gets freed. + * + * If dequote is true, then the token (if it exists) is dequoted. + * If dequote is false, no dequoting is performed. The dequote + * parameter is ignored if token is NULL or if the token does + * not appear to be quoted. If the quotes were of the form "..." + * (double-quotes) then the EP_DblQuoted flag is set on the + * expression node. + * + * Special case: If op==TK_INTEGER and token points to a string + * that can be translated into a 32-bit integer, then the token is + * not stored in u.zToken. Instead, the integer values is written + * into u.iValue and the EP_IntValue flag is set. No extra storage + * is allocated to hold the integer text and the dequote flag is + * ignored. + * + * @param db The database connection. + * @param op Expression opcode (TK_*). + * @param token Source token. Might be NULL. + * @param dequote True to dequote string. + * @retval Not NULL new expression object on success. + * @retval NULL otherwise. The diag message is set. + */ +struct Expr * +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote); + +/** + * Allocate a new expression node from a zero-terminated token + * that has already been dequoted. + * + * @param db The database connection. + * @param op Expression opcode. + * @param name The object name string. + * @retval Not NULL expression pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_op_expr_new(struct sql *db, int op, const char *name); + void sqlExprAttachSubtrees(sql *, Expr *, Expr *, Expr *); Expr *sqlPExpr(Parse *, int, Expr *, Expr *); void sqlPExprAddSelect(Parse *, Expr *, Select *); -Expr *sqlExprAnd(sql *, Expr *, Expr *); + +/** + * Join two expressions using an AND operator. If either + * expression is NULL, then just return the other expression. + * + * If one side or the other of the AND is known to be false, then + * instead of returning an AND expression, just return a constant + * expression with a value of false. + * + * @param db The database connection. + * @param left_expr The left-branch expresion to join. + * @param right_expr The right-branch expression to join. + * @retval Not NULL new expression root node pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_and_expr_new(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr); + Expr *sqlExprFunction(Parse *, ExprList *, Token *); void sqlExprAssignVarNumber(Parse *, Expr *, u32); ExprList *sqlExprListAppendVector(Parse *, ExprList *, IdList *, Expr *); @@ -4732,7 +4792,21 @@ void sqlAppendChar(StrAccum *, int, char); char *sqlStrAccumFinish(StrAccum *); void sqlStrAccumReset(StrAccum *); void sqlSelectDestInit(SelectDest *, int, int, int); -Expr *sqlCreateColumnExpr(sql *, SrcList *, int, int); + +/* + * Allocate and return a pointer to an expression to load the + * column from datasource src_idx in SrcList src_list. + * + * @param db The database connection. + * @param src_list The source list described with FROM clause. + * @param src_idx The resource index to use in src_list. + * @param column The column index. + * @retval Not NULL expression pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, + int column); int sqlExprCheckIN(Parse *, Expr *); diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 018fd8a28..9ea02970a 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -1436,7 +1436,13 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W continue; testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); pExpr = sqlExprDup(db, pExpr, 0); - pAndExpr = sqlExprAnd(db, pAndExpr, pExpr); + if (pAndExpr != NULL || pExpr != NULL) { + pAndExpr = + sql_and_expr_new(db, pAndExpr, + pExpr); + if (pAndExpr == NULL) + sql_parser_error(pParse); + } } if (pAndExpr) { pAndExpr = diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index 6df28ad8a..fce2d1d54 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -307,8 +307,10 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix, Expr *pPrefix; *pisComplete = c == MATCH_ALL_WILDCARD && z[cnt + 1] == 0; - pPrefix = sqlExpr(db, TK_STRING, z); - if (pPrefix) + pPrefix = sql_op_expr_new(db, TK_STRING, z); + if (pPrefix == NULL) + sql_parser_error(pParse); + else pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if (op == TK_VARIABLE) { @@ -1306,10 +1308,11 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; - - pNewExpr = sqlPExpr(pParse, TK_GT, - sqlExprDup(db, pLeft, 0), - sqlExprAlloc(db, TK_NULL, 0, 0)); + struct Expr *expr = sql_expr_new(db, TK_NULL, NULL, false); + if (expr == NULL) + sql_parser_error(pParse); + pNewExpr = sqlPExpr(pParse, TK_GT, sqlExprDup(db, pLeft, 0), + expr); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL | TERM_DYNAMIC | @@ -1502,9 +1505,11 @@ sqlWhereTabFuncArgs(Parse * pParse, /* Parsing context */ space_def->name, j); return; } - pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0); - if (pColRef == 0) + pColRef = sql_expr_new(pParse->db, TK_COLUMN, NULL, false); + if (pColRef == NULL) { + sql_parser_error(pParse); return; + } pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pColRef->space_def = space_def; -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-03-11 15:04 ` Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes! See 6 comments below. > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c > index 44b6ce11f..f5b2926c8 100644 > --- a/src/box/sql/expr.c > +++ b/src/box/sql/expr.c > @@ -149,16 +149,17 @@ sqlExprAddCollateToken(Parse * pParse, /* Parsing context */ > int dequote /* True to dequote pCollName */ > ) > { > - if (pCollName->n > 0) { > - Expr *pNew = > - sqlExprAlloc(pParse->db, TK_COLLATE, pCollName, > - dequote); > - if (pNew) { > - pNew->pLeft = pExpr; > - pNew->flags |= EP_Collate | EP_Skip; > - pExpr = pNew; > - } > + if (pCollName->n == 0) > + return pExpr; > + struct Expr *new_expr = > + sql_expr_new(pParse->db, TK_COLLATE, pCollName, dequote); > + if (new_expr == NULL) { > + sql_parser_error(pParse); > + return NULL; 1. Now you have a leak - parse.y:935 expects that sqlExprAddCollateToken on an error returns the old value, that is freed later, but after your patch the old value is lost. > @@ -854,113 +855,62 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) > -/* Allocate a new expression and initialize it as integer. > - * @param db sql engine. > - * @param value Value to initialize by. > - * > - * @retval not NULL Allocated and initialized expr. > - * @retval NULL Memory error. > - */ > -Expr * > -sqlExprInteger(sql * db, int value) > +struct Expr * > +sql_op_expr_new(struct sql *db, int op, const char *name) > { > - Expr *ret = sqlExpr(db, TK_INTEGER, NULL); > - if (ret != NULL) { > - ret->flags = EP_IntValue; > - ret->u.iValue = value; > - } > - return ret; > + struct Token name_token; > + name_token.z = name; > + name_token.n = name != NULL ? strlen(name) : 0; 2. sqlTokenInit. > diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c > index d1eb6670a..ceba2db9f 100644 > --- a/src/box/sql/fk_constraint.c > +++ b/src/box/sql/fk_constraint.c > @@ -309,31 +309,30 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, > * the data for given space. regBase+1 holds the first column. > * regBase+2 holds the second column, and so forth. > * 3. Now the names above are outdated. > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > index f0388b481..1a5882dda 100644 > --- a/src/box/sql/parse.y > +++ b/src/box/sql/parse.y > @@ -529,12 +529,16 @@ selcollist(A) ::= sclp(A) expr(X) as(Y). { > sqlExprListSetSpan(pParse,A,&X); > } > selcollist(A) ::= sclp(A) STAR. { > - Expr *p = sqlExpr(pParse->db, TK_ASTERISK, 0); > + struct Expr *p = sql_op_expr_new(pParse->db, TK_ASTERISK, NULL); > + if (p == NULL) > + sql_parser_error(pParse); > A = sql_expr_list_append(pParse->db, A, p); > } > selcollist(A) ::= sclp(A) nm(X) DOT STAR. { > Expr *pRight = sqlPExpr(pParse, TK_ASTERISK, 0, 0); > - Expr *pLeft = sqlExprAlloc(pParse->db, TK_ID, &X, 1); > + struct Expr *pLeft = sql_expr_new(pParse->db, TK_ID, &X, true); > + if (pLeft == NULL) > + sql_parser_error(pParse); 4. The same as about other similar places. Why not 'return'? > diff --git a/src/box/sql/select.c b/src/box/sql/select.c > index fda4296cc..5434a2ab0 100644 > --- a/src/box/sql/select.c > +++ b/src/box/sql/select.c> @@ -506,7 +511,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ > ExprSetVVAProperty(pEq, EP_NoReduce); > pEq->iRightJoinTable = (i16) pE2->iTable; > } > - *ppWhere = sqlExprAnd(db, *ppWhere, pEq); > + if (*ppWhere != NULL || pEq != NULL) { > + *ppWhere = sql_and_expr_new(pParse->db, *ppWhere, pEq); > + if (*ppWhere == NULL) > + sql_parser_error(pParse); > + } > + return; 5. Why do you need a return at the end of a 'void' function? > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index 4e6c5717b..4401ea8b7 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3218,13 +3218,73 @@ void sqlClearTempRegCache(Parse *); > #ifdef SQL_DEBUG > int sqlNoTempsInRange(Parse *, int, int); > #endif > -Expr *sqlExprAlloc(sql *, int, const Token *, int); > -Expr *sqlExpr(sql *, int, const char *); > -Expr *sqlExprInteger(sql *, int); > + > +/** > + * This routine is the core allocator for Expr nodes. > + * Construct a new expression node and return a pointer to it. > + * Memory for this node and for the token argument is a single > + * allocation obtained from sqlDbMalloc(). The calling function > + * is responsible for making sure the node eventually gets freed. > + * > + * If dequote is true, then the token (if it exists) is dequoted. > + * If dequote is false, no dequoting is performed. The dequote > + * parameter is ignored if token is NULL or if the token does > + * not appear to be quoted. If the quotes were of the form "..." > + * (double-quotes) then the EP_DblQuoted flag is set on the > + * expression node. > + * > + * Special case: If op==TK_INTEGER and token points to a string > + * that can be translated into a 32-bit integer, then the token is > + * not stored in u.zToken. Instead, the integer values is written > + * into u.iValue and the EP_IntValue flag is set. No extra storage > + * is allocated to hold the integer text and the dequote flag is > + * ignored. > + * > + * @param db The database connection. > + * @param op Expression opcode (TK_*). > + * @param token Source token. Might be NULL. > + * @param dequote True to dequote string. > + * @retval Not NULL new expression object on success. > + * @retval NULL otherwise. The diag message is set. > + */ > +struct Expr * > +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote); > + > +/** > + * Allocate a new expression node from a zero-terminated token > + * that has already been dequoted. > + * > + * @param db The database connection. > + * @param op Expression opcode. > + * @param name The object name string. > + * @retval Not NULL expression pointer on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct Expr * > +sql_op_expr_new(struct sql *db, int op, const char *name); > + > void sqlExprAttachSubtrees(sql *, Expr *, Expr *, Expr *); > Expr *sqlPExpr(Parse *, int, Expr *, Expr *); > void sqlPExprAddSelect(Parse *, Expr *, Select *); > -Expr *sqlExprAnd(sql *, Expr *, Expr *); > + > +/** > + * Join two expressions using an AND operator. If either > + * expression is NULL, then just return the other expression. > + * > + * If one side or the other of the AND is known to be false, then > + * instead of returning an AND expression, just return a constant > + * expression with a value of false. > + * > + * @param db The database connection. > + * @param left_expr The left-branch expresion to join. > + * @param right_expr The right-branch expression to join. > + * @retval Not NULL new expression root node pointer on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct Expr * > +sql_and_expr_new(struct sql *db, struct Expr *left_expr, > + struct Expr *right_expr); > + > Expr *sqlExprFunction(Parse *, ExprList *, Token *); > void sqlExprAssignVarNumber(Parse *, Expr *, u32); > ExprList *sqlExprListAppendVector(Parse *, ExprList *, IdList *, Expr *); > @@ -4732,7 +4792,21 @@ void sqlAppendChar(StrAccum *, int, char); > char *sqlStrAccumFinish(StrAccum *); > void sqlStrAccumReset(StrAccum *); > void sqlSelectDestInit(SelectDest *, int, int, int); > -Expr *sqlCreateColumnExpr(sql *, SrcList *, int, int); > + > +/* > + * Allocate and return a pointer to an expression to load the > + * column from datasource src_idx in SrcList src_list. > + * > + * @param db The database connection. > + * @param src_list The source list described with FROM clause. > + * @param src_idx The resource index to use in src_list. > + * @param column The column index. > + * @retval Not NULL expression pointer on success. 6. I am still not sure, if I said that earlier, but start sentences with a capital letter. It is a simple rule. Here and in all other places below and above. ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-03-18 19:33 ` Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > 1. Now you have a leak - parse.y:935 expects that sqlExprAddCollateToken on > an error returns the old value, that is freed later, but after your patch > the old value is lost. You are right. Return old value in case of error. struct Expr *new_expr = sql_expr_new(pParse->db, TK_COLLATE, pCollName, dequote); if (new_expr == NULL) { pParse->is_aborted = true; return pExpr; } >> + struct Token name_token; >> + name_token.z = name; >> + name_token.n = name != NULL ? strlen(name) : 0; > > 2. sqlTokenInit. Ok. >> * the data for given space. regBase+1 holds the first column. >> * regBase+2 holds the second column, and so forth. >> * > > 3. Now the names above are outdated. Fixed. >> selcollist(A) ::= sclp(A) nm(X) DOT STAR. { >> Expr *pRight = sqlPExpr(pParse, TK_ASTERISK, 0, 0); >> - Expr *pLeft = sqlExprAlloc(pParse->db, TK_ID, &X, 1); >> + struct Expr *pLeft = sql_expr_new(pParse->db, TK_ID, &X, true); >> + if (pLeft == NULL) >> + sql_parser_error(pParse); > > 4. The same as about other similar places. Why not 'return'? Already answered in previous patch. Don't mind of return here. > + return; > > 5. Why do you need a return at the end of a 'void' function? Dropped here and in other places. It's a peace of dropped goto >> +/* >> + * Allocate and return a pointer to an expression to load the >> + * column from datasource src_idx in SrcList src_list. >> + * >> + * @param db The database connection. >> + * @param src_list The source list described with FROM clause. >> + * @param src_idx The resource index to use in src_list. >> + * @param column The column index. >> + * @retval Not NULL expression pointer on success. > > 6. I am still not sure, if I said that earlier, but start > sentences with a capital letter. It is a simple rule. Here > and in all other places below and above. "Not NULL expression"... okay "Not NULL Expression" ========================================= Refactored sqlExpr routine as sql_expr_new and reworked it to set diag message in case of memory allocation error. Also performed some additional name refactoring in adjacent places. This change is necessary because the sqlExpr body has a sqlNormalizeName call that will be changed in subsequent patches. Part of #3931 --- src/box/sql/build.c | 42 ++++-- src/box/sql/expr.c | 248 ++++++++++++++---------------------- src/box/sql/fk_constraint.c | 190 +++++++++++++++++---------- src/box/sql/parse.y | 51 ++++++-- src/box/sql/resolve.c | 46 ++++--- src/box/sql/select.c | 86 +++++++++---- src/box/sql/sqlInt.h | 84 +++++++++++- src/box/sql/wherecode.c | 8 +- src/box/sql/whereexpr.c | 21 +-- 9 files changed, 471 insertions(+), 305 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index f52cfd7dd..f527928bf 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -616,9 +616,12 @@ sqlAddPrimaryKey(Parse * pParse, /* Parsing context */ struct ExprList *list; struct Token token; sqlTokenInit(&token, space->def->fields[iCol].name); - list = sql_expr_list_append(db, NULL, - sqlExprAlloc(db, TK_ID, - &token, 0)); + struct Expr *expr = sql_expr_new(db, TK_ID, &token, false); + if (expr == NULL) { + pParse->is_aborted = true; + goto primary_key_exit; + } + list = sql_expr_list_append(db, NULL, expr); if (list == NULL) goto primary_key_exit; sql_create_index(pParse, 0, 0, list, 0, SORT_ORDER_ASC, @@ -1355,13 +1358,15 @@ sql_id_eq_str_expr(struct Parse *parse, const char *col_name, const char *col_value) { struct sql *db = parse->db; - - struct Expr *col_name_expr = sqlExpr(db, TK_ID, col_name); - if (col_name_expr == NULL) + struct Expr *col_name_expr = sql_op_expr_new(db, TK_ID, col_name); + if (col_name_expr == NULL) { + parse->is_aborted = true; return NULL; - struct Expr *col_value_expr = sqlExpr(db, TK_STRING, col_value); + } + struct Expr *col_value_expr = sql_op_expr_new(db, TK_STRING, col_value); if (col_value_expr == NULL) { sql_expr_delete(db, col_name_expr, false); + parse->is_aborted = true; return NULL; } return sqlPExpr(parse, TK_EQ, col_name_expr, col_value_expr); @@ -1383,13 +1388,19 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, struct Expr *where = NULL; if (idx_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_new(db, expr, where); + if (where == NULL) + parse->is_aborted = true; + } } if (table_name != NULL) { struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name); - if (expr != NULL) - where = sqlExprAnd(db, expr, where); + if (expr != NULL && (expr != NULL || where != NULL)) { + where = sql_and_expr_new(db, expr, where); + if (where == NULL) + parse->is_aborted = true; + } } /** * On memory allocation error sql_table delete_from @@ -2245,9 +2256,12 @@ sql_create_index(struct Parse *parse, struct Token *token, struct Token prev_col; uint32_t last_field = def->field_count - 1; sqlTokenInit(&prev_col, def->fields[last_field].name); - col_list = sql_expr_list_append(parse->db, NULL, - sqlExprAlloc(db, TK_ID, - &prev_col, 0)); + struct Expr *expr = sql_expr_new(db, TK_ID, &prev_col, false); + if (expr == NULL) { + parse->is_aborted = true; + goto exit_create_index; + } + col_list = sql_expr_list_append(parse->db, NULL, expr); if (col_list == NULL) goto exit_create_index; assert(col_list->nExpr == 1); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 0457ff833..dad4ce3a6 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -149,17 +149,17 @@ sqlExprAddCollateToken(Parse * pParse, /* Parsing context */ int dequote /* True to dequote pCollName */ ) { - if (pCollName->n > 0) { - Expr *pNew = - sqlExprAlloc(pParse->db, TK_COLLATE, pCollName, - dequote); - if (pNew) { - pNew->pLeft = pExpr; - pNew->flags |= EP_Collate | EP_Skip; - pExpr = pNew; - } - } - return pExpr; + if (pCollName->n == 0) + return pExpr; + struct Expr *new_expr = + sql_expr_new(pParse->db, TK_COLLATE, pCollName, dequote); + if (new_expr == NULL) { + pParse->is_aborted = true; + return pExpr; + } + new_expr->pLeft = pExpr; + new_expr->flags |= EP_Collate | EP_Skip; + return new_expr; } Expr * @@ -875,113 +875,61 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) #define exprSetHeight(y) #endif /* SQL_MAX_EXPR_DEPTH>0 */ -/* - * This routine is the core allocator for Expr nodes. - * - * Construct a new expression node and return a pointer to it. Memory - * for this node and for the pToken argument is a single allocation - * obtained from sqlDbMalloc(). The calling function - * is responsible for making sure the node eventually gets freed. - * - * If dequote is true, then the token (if it exists) is dequoted. - * If dequote is false, no dequoting is performed. The deQuote - * parameter is ignored if pToken is NULL or if the token does not - * appear to be quoted. If the quotes were of the form "..." (double-quotes) - * then the EP_DblQuoted flag is set on the expression node. - * - * Special case: If op==TK_INTEGER and pToken points to a string that - * can be translated into a 32-bit integer, then the token is not - * stored in u.zToken. Instead, the integer values is written - * into u.iValue and the EP_IntValue flag is set. No extra storage - * is allocated to hold the integer text and the dequote flag is ignored. - */ -Expr * -sqlExprAlloc(sql * db, /* Handle for sqlDbMallocRawNN() */ - int op, /* Expression opcode */ - const Token * pToken, /* Token argument. Might be NULL */ - int dequote /* True to dequote */ - ) +struct Expr * +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) { - Expr *pNew; - int nExtra = 0; - int iValue = 0; - - assert(db != 0); - if (pToken) { - if (op != TK_INTEGER || pToken->z == 0 - || sqlGetInt32(pToken->z, &iValue) == 0) { - nExtra = pToken->n + 1; - assert(iValue >= 0); + int extra_sz = 0; + int val = 0; + if (token != NULL) { + if (op != TK_INTEGER || token->z == NULL || + sqlGetInt32(token->z, &val) == 0) { + extra_sz = token->n + 1; + assert(val >= 0); } } - pNew = sqlDbMallocRawNN(db, sizeof(Expr) + nExtra); - if (pNew) { - memset(pNew, 0, sizeof(Expr)); - pNew->op = (u8) op; - pNew->iAgg = -1; - if (pToken) { - if (nExtra == 0) { - pNew->flags |= EP_IntValue; - pNew->u.iValue = iValue; - } else { - pNew->u.zToken = (char *)&pNew[1]; - assert(pToken->z != 0 || pToken->n == 0); - if (pToken->n) - memcpy(pNew->u.zToken, pToken->z, - pToken->n); - pNew->u.zToken[pToken->n] = 0; - if (dequote){ - if (pNew->u.zToken[0] == '"') - pNew->flags |= EP_DblQuoted; - if (pNew->op == TK_ID || - pNew->op == TK_COLLATE || - pNew->op == TK_FUNCTION){ - sqlNormalizeName(pNew->u.zToken); - }else{ - sqlDequote(pNew->u.zToken); - } - } - } - } -#if SQL_MAX_EXPR_DEPTH>0 - pNew->nHeight = 1; -#endif + struct Expr *expr = sqlDbMallocRawNN(db, sizeof(*expr) + extra_sz); + if (expr == NULL) { + diag_set(OutOfMemory, sizeof(*expr), "sqlDbMallocRawNN", + "expr"); + return NULL; } - return pNew; -} -/* - * Allocate a new expression node from a zero-terminated token that has - * already been dequoted. - */ -Expr * -sqlExpr(sql * db, /* Handle for sqlDbMallocZero() (may be null) */ - int op, /* Expression opcode */ - const char *zToken /* Token argument. Might be NULL */ - ) -{ - Token x; - x.z = zToken; - x.n = zToken ? sqlStrlen30(zToken) : 0; - return sqlExprAlloc(db, op, &x, 0); + memset(expr, 0, sizeof(*expr)); + expr->op = (u8)op; + expr->iAgg = -1; +#if SQL_MAX_EXPR_DEPTH > 0 + expr->nHeight = 1; +#endif + if (token == NULL) + return expr; + + if (extra_sz == 0) { + expr->flags |= EP_IntValue; + expr->u.iValue = val; + } else { + expr->u.zToken = (char *)&expr[1]; + assert(token->z != NULL || token->n == 0); + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) { + if (expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION) + sqlNormalizeName(expr->u.zToken); + else + sqlDequote(expr->u.zToken); + } + } + return expr; } -/* Allocate a new expression and initialize it as integer. - * @param db sql engine. - * @param value Value to initialize by. - * - * @retval not NULL Allocated and initialized expr. - * @retval NULL Memory error. - */ -Expr * -sqlExprInteger(sql * db, int value) +struct Expr * +sql_op_expr_new(struct sql *db, int op, const char *name) { - Expr *ret = sqlExpr(db, TK_INTEGER, NULL); - if (ret != NULL) { - ret->flags = EP_IntValue; - ret->u.iValue = value; - } - return ret; + struct Token name_token; + sqlTokenInit(&name_token, (char *)name); + return sql_expr_new(db, op, &name_token, false); } /* @@ -1027,8 +975,15 @@ sqlPExpr(Parse * pParse, /* Parsing context */ { Expr *p; if (op == TK_AND && !pParse->is_aborted) { - /* Take advantage of short-circuit false optimization for AND */ - p = sqlExprAnd(pParse->db, pLeft, pRight); + /* + * Take advantage of short-circuit false + * optimization for AND. + */ + if (pLeft == NULL && pRight == NULL) + return NULL; + p = sql_and_expr_new(pParse->db, pLeft, pRight); + if (p == NULL) + pParse->is_aborted = true; } else { p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)); if (p) { @@ -1097,30 +1052,24 @@ exprAlwaysFalse(Expr * p) return v == 0; } -/* - * Join two expressions using an AND operator. If either expression is - * NULL, then just return the other expression. - * - * If one side or the other of the AND is known to be false, then instead - * of returning an AND expression, just return a constant expression with - * a value of false. - */ -Expr * -sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) +struct Expr * +sql_and_expr_new(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr) { - if (pLeft == 0) { - return pRight; - } else if (pRight == 0) { - return pLeft; - } else if (exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight)) { - sql_expr_delete(db, pLeft, false); - sql_expr_delete(db, pRight, false); - return sqlExprAlloc(db, TK_INTEGER, &sqlIntTokens[0], - 0); + if (left_expr == NULL) { + assert(right_expr != NULL); + return right_expr; + } else if (right_expr == NULL) { + assert(left_expr != NULL); + return left_expr; + } else if (exprAlwaysFalse(left_expr) || exprAlwaysFalse(right_expr)) { + sql_expr_delete(db, left_expr, false); + sql_expr_delete(db, right_expr, false); + return sql_expr_new(db, TK_INTEGER, &sqlIntTokens[0], false); } else { - Expr *pNew = sqlExprAlloc(db, TK_AND, 0, 0); - sqlExprAttachSubtrees(db, pNew, pLeft, pRight); - return pNew; + struct Expr *new_expr = sql_expr_new(db, TK_AND, NULL, false); + sqlExprAttachSubtrees(db, new_expr, left_expr, right_expr); + return new_expr; } } @@ -1131,18 +1080,18 @@ sqlExprAnd(sql * db, Expr * pLeft, Expr * pRight) Expr * sqlExprFunction(Parse * pParse, ExprList * pList, Token * pToken) { - Expr *pNew; - sql *db = pParse->db; - assert(pToken); - pNew = sqlExprAlloc(db, TK_FUNCTION, pToken, 1); - if (pNew == 0) { - sql_expr_list_delete(db, pList); /* Avoid memory leak when malloc fails */ - return 0; + struct sql *db = pParse->db; + assert(pToken != NULL); + struct Expr *new_expr = sql_expr_new(db, TK_FUNCTION, pToken, true); + if (new_expr == NULL) { + sql_expr_list_delete(db, pList); + pParse->is_aborted = true; + return NULL; } - pNew->x.pList = pList; - assert(!ExprHasProperty(pNew, EP_xIsSelect)); - sqlExprSetHeightAndFlags(pParse, pNew); - return pNew; + new_expr->x.pList = pList; + assert(!ExprHasProperty(new_expr, EP_xIsSelect)); + sqlExprSetHeightAndFlags(pParse, new_expr); + return new_expr; } /* @@ -2953,10 +2902,11 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */ } if (pSel->pLimit == NULL) { pSel->pLimit = - sqlExprAlloc(pParse->db, TK_INTEGER, - &sqlIntTokens[1], - 0); - if (pSel->pLimit != NULL) { + sql_expr_new(pParse->db, TK_INTEGER, + &sqlIntTokens[1], false); + if (pSel->pLimit == NULL) { + pParse->is_aborted = true; + } else { ExprSetProperty(pSel->pLimit, EP_System); } diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index cc4dc6448..3b27b2d4f 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -303,37 +303,36 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, /** * Return an Expr object that refers to a memory register - * corresponding to column iCol of given space. + * corresponding to column of given space. * - * regBase is the first of an array of register that contains - * the data for given space. regBase+1 holds the first column. - * regBase+2 holds the second column, and so forth. + * reg_base is the first of an array of register that contains + * the data for given space. reg_base+1 holds the first column. + * reg_base+2 holds the second column, and so forth. * - * @param pParse Parsing and code generating context. + * @param parser The parsing context. * @param def Definition of space whose content is at r[regBase]... - * @param regBase Contents of table defined by def. - * @param iCol Which column of space is desired. - * @return an Expr object that refers to a memory register - * corresponding to column iCol of given space. + * @param reg_base Contents of table table. + * @param column Index of table column is desired. + * @retval Not NULL expression pointer on success. + * @retval NULL Otherwise. */ -static Expr * -space_field_register(Parse *pParse, struct space_def *def, int regBase, - i16 iCol) +static struct Expr * +sql_register_expr_new(struct Parse *parser, struct space_def *def, int reg_base, + int column) { - Expr *pExpr; - sql *db = pParse->db; - - pExpr = sqlExpr(db, TK_REGISTER, 0); - if (pExpr) { - if (iCol >= 0) { - pExpr->iTable = regBase + iCol + 1; - pExpr->type = def->fields[iCol].type; - } else { - pExpr->iTable = regBase; - pExpr->type = FIELD_TYPE_INTEGER; - } + struct Expr *expr = sql_op_expr_new(parser->db, TK_REGISTER, NULL); + if (expr == NULL) { + parser->is_aborted = true; + return NULL; + } + if (column >= 0) { + expr->iTable = reg_base + column + 1; + expr->type = def->fields[column].type; + } else { + expr->iTable = reg_base; + expr->type = FIELD_TYPE_INTEGER; } - return pExpr; + return expr; } /** @@ -346,16 +345,17 @@ space_field_register(Parse *pParse, struct space_def *def, int regBase, * @retval not NULL on success. * @retval NULL on error. */ -static Expr * -exprTableColumn(sql * db, struct space_def *def, int cursor, i16 column) +static struct Expr * +sql_column_cursor_expr_create(struct sql *db, struct space_def *def, + int cursor, int column) { - Expr *pExpr = sqlExpr(db, TK_COLUMN, 0); - if (pExpr) { - pExpr->space_def = def; - pExpr->iTable = cursor; - pExpr->iColumn = column; - } - return pExpr; + struct Expr *expr = sql_op_expr_new(db, TK_COLUMN, NULL); + if (expr == NULL) + return NULL; + expr->space_def = def; + expr->iTable = cursor; + expr->iColumn = column; + return expr; } /* @@ -435,12 +435,18 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; struct Expr *pexpr = - space_field_register(parser, def, reg_data, fieldno); + sql_register_expr_new(parser, def, reg_data, fieldno); fieldno = fk_def->links[i].child_field; const char *field_name = child_space->def->fields[fieldno].name; - struct Expr *chexpr = sqlExpr(db, TK_ID, field_name); + struct Expr *chexpr = sql_op_expr_new(db, TK_ID, field_name); + if (chexpr == NULL) + parser->is_aborted = true; struct Expr *eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - where = sqlExprAnd(db, where, eq); + if (where != NULL || eq != NULL) { + where = sql_and_expr_new(db, where, eq); + if (where == NULL) + parser->is_aborted = true; + } } /* @@ -456,15 +462,26 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, struct Expr *expr = NULL, *pexpr, *chexpr, *eq; for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; - pexpr = space_field_register(parser, def, reg_data, - fieldno); - chexpr = exprTableColumn(db, def, src->a[0].iCursor, - fieldno); + pexpr = sql_register_expr_new(parser, def, reg_data, + fieldno); + int cursor = src->a[0].iCursor; + chexpr = sql_column_cursor_expr_create(db, def, cursor, + fieldno); + if (chexpr == NULL) + parser->is_aborted = true; eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - expr = sqlExprAnd(db, expr, eq); + if (expr != NULL || eq != NULL) { + expr = sql_and_expr_new(db, expr, eq); + if (expr == NULL) + parser->is_aborted = true; + } } struct Expr *pNe = sqlPExpr(parser, TK_NOT, expr, 0); - where = sqlExprAnd(db, where, pNe); + if (where != NULL || pNe != NULL) { + where = sql_and_expr_new(db, where, pNe); + if (where == NULL) + parser->is_aborted = true; + } } /* Resolve the references in the WHERE clause. */ @@ -785,14 +802,26 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * type and collation sequence associated with * the parent table are used for the comparison. */ - struct Expr *to_col = - sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); - struct Expr *from_col = - sqlExprAlloc(db, TK_ID, &t_from_col, 0); - struct Expr *eq = sqlPExpr(pParse, TK_EQ, to_col, from_col); - where = sqlExprAnd(db, where, eq); + struct Expr *old_expr = NULL, *new_expr = NULL, *expr = NULL; + old_expr = sql_expr_new(db, TK_ID, &t_old, false); + if (old_expr == NULL) + pParse->is_aborted = true; + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + pParse->is_aborted = true; + struct Expr *from_col_expr = + sql_expr_new(db, TK_ID, &t_from_col, false); + if (from_col_expr == NULL) + pParse->is_aborted = true; + struct Expr *to_col_expr = + sqlPExpr(pParse, TK_DOT, old_expr, expr); + struct Expr *eq = + sqlPExpr(pParse, TK_EQ, to_col_expr, from_col_expr); + if (where != NULL || eq != NULL) { + where = sql_and_expr_new(db, where, eq); + if (where == NULL) + pParse->is_aborted = true; + } /* * For ON UPDATE, construct the next term of the @@ -810,12 +839,22 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * no_action_needed(colN)) */ if (is_update) { + old_expr = sql_expr_new(db, TK_ID, &t_old, false); + if (old_expr == NULL) + pParse->is_aborted = true; + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + pParse->is_aborted = true; struct Expr *old_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_old, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + old_expr, expr); + new_expr = sql_expr_new(db, TK_ID, &t_new, false); + if (new_expr == NULL) + pParse->is_aborted = true; + expr = sql_expr_new(db, TK_ID, &t_to_col, false); + if (expr == NULL) + pParse->is_aborted = true; struct Expr *new_val = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, &t_new, 0), - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); + new_expr, expr); struct Expr *old_is_null = sqlPExpr( pParse, TK_ISNULL, sqlExprDup(db, old_val, 0), NULL); @@ -828,29 +867,41 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Expr *no_action_needed = sqlPExpr(pParse, TK_OR, old_is_null, non_null_eq); - when = sqlExprAnd(db, when, no_action_needed); + if (when != NULL || no_action_needed != NULL) { + when = sql_and_expr_new(db, when, + no_action_needed); + if (when == NULL) + pParse->is_aborted = true; + } } if (action != FKEY_ACTION_RESTRICT && (action != FKEY_ACTION_CASCADE || is_update)) { struct Expr *new, *d; if (action == FKEY_ACTION_CASCADE) { - new = sqlPExpr(pParse, TK_DOT, - sqlExprAlloc(db, TK_ID, - &t_new, 0), - sqlExprAlloc(db, TK_ID, - &t_to_col, - 0)); + new_expr = sql_expr_new(db, TK_ID, &t_new, + false); + if (new_expr == NULL) + pParse->is_aborted = true; + expr = sql_expr_new(db, TK_ID, &t_to_col, + false); + if (expr == NULL) + pParse->is_aborted = true; + new = sqlPExpr(pParse, TK_DOT, new_expr, expr); } else if (action == FKEY_ACTION_SET_DEFAULT) { d = child_fields[chcol].default_value_expr; if (d != NULL) { new = sqlExprDup(db, d, 0); } else { - new = sqlExprAlloc(db, TK_NULL, - NULL, 0); + new = sql_expr_new(db, TK_NULL, NULL, + false); + if (new == NULL) + pParse->is_aborted = true; } } else { - new = sqlExprAlloc(db, TK_NULL, NULL, 0); + new = sql_expr_new(db, TK_NULL, NULL, false); + if (new == NULL) + pParse->is_aborted = true; } list = sql_expr_list_append(db, list, new); sqlExprListSetName(pParse, list, &t_from_col, 0); @@ -864,9 +915,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Token err; err.z = space_name; err.n = name_len; - struct Expr *r = sqlExpr(db, TK_RAISE, "FOREIGN KEY "\ - "constraint failed"); - if (r != NULL) + struct Expr *r = + sql_op_expr_new(db, TK_RAISE, + "FOREIGN KEY constraint failed"); + if (r == NULL) + pParse->is_aborted = true; + else r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; struct SrcList *src_list = sql_src_list_append(db, NULL, &err); if (src_list == NULL) diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 194af99b7..c48cbd6f9 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -536,12 +536,20 @@ selcollist(A) ::= sclp(A) expr(X) as(Y). { sqlExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { - Expr *p = sqlExpr(pParse->db, TK_ASTERISK, 0); + struct Expr *p = sql_op_expr_new(pParse->db, TK_ASTERISK, NULL); + if (p == NULL) { + pParse->is_aborted = true; + return; + } A = sql_expr_list_append(pParse->db, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { + struct Expr *pLeft = sql_expr_new(pParse->db, TK_ID, &X, true); + if (pLeft == NULL) { + pParse->is_aborted = true; + return; + } Expr *pRight = sqlPExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = sqlExprAlloc(pParse->db, TK_ID, &X, 1); Expr *pDot = sqlPExpr(pParse, TK_DOT, pLeft, pRight); A = sql_expr_list_append(pParse->db,A, pDot); } @@ -898,15 +906,28 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { - Expr *temp1 = sqlExprAlloc(pParse->db, TK_ID, &X, 1); - Expr *temp2 = sqlExprAlloc(pParse->db, TK_ID, &Y, 1); + struct Expr *temp1 = sql_expr_new(pParse->db, TK_ID, &X, true); + if (temp1 == NULL) { + pParse->is_aborted = true; + return; + } + struct Expr *temp2 = sql_expr_new(pParse->db, TK_ID, &Y, true); + if (temp2 == NULL) { + sql_expr_delete(pParse->db, temp2, false); + pParse->is_aborted = true; + return; + } spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlPExpr(pParse, TK_DOT, temp1, temp2); } term(A) ::= FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= INTEGER(X). { - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER, &X, 1); + A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &X, true); + if (A.pExpr == NULL) { + pParse->is_aborted = true; + return; + } A.pExpr->type = FIELD_TYPE_INTEGER; A.zStart = X.z; A.zEnd = X.z + X.n; @@ -942,7 +963,11 @@ expr(A) ::= expr(A) COLLATE id(C). { %ifndef SQL_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_CAST, 0, 1); + A.pExpr = sql_expr_new(pParse->db, TK_CAST, NULL, true); + if (A.pExpr == NULL) { + pParse->is_aborted = true; + return; + } A.pExpr->type = T.type; sqlExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0); } @@ -1136,7 +1161,11 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] { ** regardless of the value of expr1. */ sql_expr_delete(pParse->db, A.pExpr, false); - A.pExpr = sqlExprAlloc(pParse->db, TK_INTEGER,&sqlIntTokens[N],1); + A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &sqlIntTokens[N], true); + if (A.pExpr == NULL) { + pParse->is_aborted = true; + return; + } }else if( Y->nExpr==1 ){ /* Expressions of the form: ** @@ -1477,10 +1506,12 @@ expr(A) ::= RAISE(X) LP IGNORE RP(Y). { } expr(A) ::= RAISE(X) LP raisetype(T) COMMA STRING(Z) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sqlExprAlloc(pParse->db, TK_RAISE, &Z, 1); - if( A.pExpr ) { - A.pExpr->on_conflict_action = (enum on_conflict_action) T; + A.pExpr = sql_expr_new(pParse->db, TK_RAISE, &Z, true); + if(A.pExpr == NULL) { + pParse->is_aborted = true; + return; } + A.pExpr->on_conflict_action = (enum on_conflict_action) T; } %type raisetype {int} diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 94bb0affc..eb74d024c 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -487,26 +487,20 @@ lookupName(Parse * pParse, /* The parsing context */ } } -/* - * Allocate and return a pointer to an expression to load the column iCol - * from datasource iSrc in SrcList pSrc. - */ -Expr * -sqlCreateColumnExpr(sql * db, SrcList * pSrc, int iSrc, int iCol) +struct Expr * +sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, + int column) { - Expr *p = sqlExprAlloc(db, TK_COLUMN, 0, 0); - if (p) { - struct SrcList_item *pItem = &pSrc->a[iSrc]; - p->space_def = pItem->space->def; - p->iTable = pItem->iCursor; - p->iColumn = (ynVar) iCol; - testcase(iCol == BMS); - testcase(iCol == BMS - 1); - pItem->colUsed |= - ((Bitmask) 1) << (iCol >= BMS ? BMS - 1 : iCol); - ExprSetProperty(p, EP_Resolved); - } - return p; + struct Expr *expr = sql_expr_new(db, TK_COLUMN, NULL, false); + if (expr == NULL) + return NULL; + struct SrcList_item *item = &src_list->a[src_idx]; + expr->space_def = item->space->def; + expr->iTable = item->iCursor; + expr->iColumn = column; + item->colUsed |= ((Bitmask) 1) << (column >= BMS ? BMS - 1 : column); + ExprSetProperty(expr, EP_Resolved); + return expr; } /* @@ -1000,9 +994,12 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context. Leave error messages /* Convert the ORDER BY term into an integer column number iCol, * taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + struct Expr *pNew = + sql_op_expr_new(db, TK_INTEGER, NULL); + if (pNew == NULL) { + pParse->is_aborted = true; return 1; + } pNew->flags |= EP_IntValue; pNew->u.iValue = iCol; if (pItem->pExpr == pE) { @@ -1351,9 +1348,10 @@ resolveSelectStep(Walker * pWalker, Select * p) * restrict it directly). */ sql_expr_delete(db, p->pLimit, false); - p->pLimit = - sqlExprAlloc(db, TK_INTEGER, - &sqlIntTokens[1], 0); + p->pLimit = sql_expr_new(db, TK_INTEGER, + &sqlIntTokens[1], false); + if (p->pLimit == NULL) + pParse->is_aborted = true; } else { if (sqlResolveExprNames(&sNC, p->pHaving)) return WRC_Abort; diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 1b7d52b68..5bba0b5d0 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -164,8 +164,10 @@ sqlSelectNew(Parse * pParse, /* Parsing context */ pNew = &standin; } if (pEList == 0) { - pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + if (expr == NULL) + pParse->is_aborted = true; + pEList = sql_expr_list_append(pParse->db, NULL, expr); } struct session MAYBE_UNUSED *user_session; user_session = current_session(); @@ -487,7 +489,6 @@ addWhereTerm(Parse * pParse, /* Parsing context */ int isOuterJoin, /* True if this is an OUTER join */ Expr ** ppWhere) /* IN/OUT: The WHERE clause to add to */ { - sql *db = pParse->db; Expr *pE1; Expr *pE2; Expr *pEq; @@ -497,8 +498,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ assert(pSrc->a[iLeft].space != NULL); assert(pSrc->a[iRight].space != NULL); - pE1 = sqlCreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = sqlCreateColumnExpr(db, pSrc, iRight, iColRight); + pE1 = sql_column_expr_new(pParse->db, pSrc, iLeft, iColLeft); + if (pE1 == NULL) + pParse->is_aborted = true; + pE2 = sql_column_expr_new(pParse->db, pSrc, iRight, iColRight); + if (pE2 == NULL) + pParse->is_aborted = true; pEq = sqlPExpr(pParse, TK_EQ, pE1, pE2); if (pEq && isOuterJoin) { @@ -507,7 +512,11 @@ addWhereTerm(Parse * pParse, /* Parsing context */ ExprSetVVAProperty(pEq, EP_NoReduce); pEq->iRightJoinTable = (i16) pE2->iTable; } - *ppWhere = sqlExprAnd(db, *ppWhere, pEq); + if (*ppWhere != NULL || pEq != NULL) { + *ppWhere = sql_and_expr_new(pParse->db, *ppWhere, pEq); + if (*ppWhere == NULL) + pParse->is_aborted = true; + } } /* @@ -628,8 +637,13 @@ sqlProcessJoin(Parse * pParse, Select * p) if (pRight->pOn) { if (isOuter) setJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = - sqlExprAnd(pParse->db, p->pWhere, pRight->pOn); + if (p->pWhere != NULL || pRight->pOn != NULL) { + p->pWhere = + sql_and_expr_new(pParse->db, p->pWhere, + pRight->pOn); + if (p->pWhere == NULL) + pParse->is_aborted = true; + } pRight->pOn = 0; } @@ -3334,9 +3348,12 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */ break; } if (j == nOrderBy) { - Expr *pNew = sqlExpr(db, TK_INTEGER, 0); - if (pNew == 0) + struct Expr *pNew = + sql_op_expr_new(db, TK_INTEGER, NULL); + if (pNew == NULL) { + pParse->is_aborted = true; return SQL_NOMEM; + } pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sql_expr_list_append(pParse->db, @@ -4209,17 +4226,22 @@ flattenSubquery(Parse * pParse, /* Parsing context */ assert(pParent->pHaving == 0); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; - pParent->pHaving = sqlExprAnd(db, - sqlExprDup(db, - pSub->pHaving, - 0), - pParent->pHaving); + struct Expr *sub_having = sqlExprDup(db, pSub->pHaving, 0); + if (sub_having != NULL || pParent->pHaving != NULL) { + pParent->pHaving = + sql_and_expr_new(db, sub_having, + pParent->pHaving); + if (pParent->pHaving == NULL) + pParse->is_aborted = true; + } assert(pParent->pGroupBy == 0); pParent->pGroupBy = sql_expr_list_dup(db, pSub->pGroupBy, 0); - } else { + } else if (pWhere != NULL || pParent->pWhere != NULL) { pParent->pWhere = - sqlExprAnd(db, pWhere, pParent->pWhere); + sql_and_expr_new(db, pWhere, pParent->pWhere); + if (pParent->pWhere == NULL) + pParse->is_aborted = true; } substSelect(pParse, pParent, iParent, pSub->pEList, 0); @@ -4324,8 +4346,13 @@ pushDownWhereTerms(Parse * pParse, /* Parse context (for malloc() and error repo while (pSubq) { pNew = sqlExprDup(pParse->db, pWhere, 0); pNew = substExpr(pParse, pNew, iCursor, pSubq->pEList); - pSubq->pWhere = - sqlExprAnd(pParse->db, pSubq->pWhere, pNew); + if (pSubq->pWhere != NULL || pNew != NULL) { + pSubq->pWhere = + sql_and_expr_new(pParse->db, + pSubq->pWhere, pNew); + if (pSubq->pWhere == NULL) + pParse->is_aborted = true; + } pSubq = pSubq->pPrior; } } @@ -4506,8 +4533,10 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; - p->pEList = sql_expr_list_append(pParse->db, NULL, - sqlExpr(db, TK_ASTERISK, 0)); + struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + if (expr == NULL) + pParse->is_aborted = true; + p->pEList = sql_expr_list_append(pParse->db, NULL, expr); p->op = TK_SELECT; p->pWhere = 0; pNew->pGroupBy = 0; @@ -4991,18 +5020,23 @@ selectExpander(Walker * pWalker, Select * p) continue; } } - pRight = - sqlExpr(db, TK_ID, - zName); + pRight = sql_op_expr_new(db, + TK_ID, zName); + if (pRight == NULL) + pParse->is_aborted = true; zColname = zName; zToFree = 0; if (longNames || pTabList->nSrc > 1) { Expr *pLeft; - pLeft = - sqlExpr(db, + pLeft = sql_op_expr_new( + db, TK_ID, zTabName); + if (pLeft == NULL) { + pParse-> + is_aborted = true; + } pExpr = sqlPExpr(pParse, TK_DOT, diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index d3ffa9d9b..13d0b16f8 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3223,13 +3223,73 @@ void sqlClearTempRegCache(Parse *); #ifdef SQL_DEBUG int sqlNoTempsInRange(Parse *, int, int); #endif -Expr *sqlExprAlloc(sql *, int, const Token *, int); -Expr *sqlExpr(sql *, int, const char *); -Expr *sqlExprInteger(sql *, int); + +/** + * This routine is the core allocator for Expr nodes. + * Construct a new expression node and return a pointer to it. + * Memory for this node and for the token argument is a single + * allocation obtained from sqlDbMalloc(). The calling function + * is responsible for making sure the node eventually gets freed. + * + * If dequote is true, then the token (if it exists) is dequoted. + * If dequote is false, no dequoting is performed. The dequote + * parameter is ignored if token is NULL or if the token does + * not appear to be quoted. If the quotes were of the form "..." + * (double-quotes) then the EP_DblQuoted flag is set on the + * expression node. + * + * Special case: If op==TK_INTEGER and token points to a string + * that can be translated into a 32-bit integer, then the token is + * not stored in u.zToken. Instead, the integer values is written + * into u.iValue and the EP_IntValue flag is set. No extra storage + * is allocated to hold the integer text and the dequote flag is + * ignored. + * + * @param db The database connection. + * @param op Expression opcode (TK_*). + * @param token Source token. Might be NULL. + * @param dequote True to dequote string. + * @retval Not NULL New expression object on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote); + +/** + * Allocate a new expression node from a zero-terminated token + * that has already been dequoted. + * + * @param db The database connection. + * @param op Expression opcode. + * @param name The object name string. + * @retval Not NULL Expression pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_op_expr_new(struct sql *db, int op, const char *name); + void sqlExprAttachSubtrees(sql *, Expr *, Expr *, Expr *); Expr *sqlPExpr(Parse *, int, Expr *, Expr *); void sqlPExprAddSelect(Parse *, Expr *, Select *); -Expr *sqlExprAnd(sql *, Expr *, Expr *); + +/** + * Join two expressions using an AND operator. If either + * expression is NULL, then just return the other expression. + * + * If one side or the other of the AND is known to be false, then + * instead of returning an AND expression, just return a constant + * expression with a value of false. + * + * @param db The database connection. + * @param left_expr The left-branch expresion to join. + * @param right_expr The right-branch expression to join. + * @retval Not NULL New expression root node pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_and_expr_new(struct sql *db, struct Expr *left_expr, + struct Expr *right_expr); + Expr *sqlExprFunction(Parse *, ExprList *, Token *); void sqlExprAssignVarNumber(Parse *, Expr *, u32); ExprList *sqlExprListAppendVector(Parse *, ExprList *, IdList *, Expr *); @@ -4750,7 +4810,21 @@ void sqlAppendChar(StrAccum *, int, char); char *sqlStrAccumFinish(StrAccum *); void sqlStrAccumReset(StrAccum *); void sqlSelectDestInit(SelectDest *, int, int, int); -Expr *sqlCreateColumnExpr(sql *, SrcList *, int, int); + +/* + * Allocate and return a pointer to an expression to load the + * column from datasource src_idx in SrcList src_list. + * + * @param db The database connection. + * @param src_list The source list described with FROM clause. + * @param src_idx The resource index to use in src_list. + * @param column The column index. + * @retval Not NULL Expression pointer on success. + * @retval NULL Otherwise. The diag message is set. + */ +struct Expr * +sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, + int column); int sqlExprCheckIN(Parse *, Expr *); diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index be30a40c0..036540d61 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -1372,7 +1372,13 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W continue; testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); pExpr = sqlExprDup(db, pExpr, 0); - pAndExpr = sqlExprAnd(db, pAndExpr, pExpr); + if (pAndExpr != NULL || pExpr != NULL) { + pAndExpr = + sql_and_expr_new(db, pAndExpr, + pExpr); + if (pAndExpr == NULL) + pParse->is_aborted = true; + } } if (pAndExpr) { pAndExpr = diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index 6df28ad8a..a9dec8e99 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -307,8 +307,10 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix, Expr *pPrefix; *pisComplete = c == MATCH_ALL_WILDCARD && z[cnt + 1] == 0; - pPrefix = sqlExpr(db, TK_STRING, z); - if (pPrefix) + pPrefix = sql_op_expr_new(db, TK_STRING, z); + if (pPrefix == NULL) + pParse->is_aborted = true; + else pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if (op == TK_VARIABLE) { @@ -1306,10 +1308,11 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; - - pNewExpr = sqlPExpr(pParse, TK_GT, - sqlExprDup(db, pLeft, 0), - sqlExprAlloc(db, TK_NULL, 0, 0)); + struct Expr *expr = sql_expr_new(db, TK_NULL, NULL, false); + if (expr == NULL) + pParse->is_aborted = true; + pNewExpr = sqlPExpr(pParse, TK_GT, sqlExprDup(db, pLeft, 0), + expr); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL | TERM_DYNAMIC | @@ -1502,9 +1505,11 @@ sqlWhereTabFuncArgs(Parse * pParse, /* Parsing context */ space_def->name, j); return; } - pColRef = sqlExprAlloc(pParse->db, TK_COLUMN, 0, 0); - if (pColRef == 0) + pColRef = sql_expr_new(pParse->db, TK_COLUMN, NULL, false); + if (pColRef == NULL) { + pParse->is_aborted = true; return; + } pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pColRef->space_def = space_def; -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 6/7] sql: refactor sql_expr_create to set diag 2019-03-20 11:02 ` Kirill Shcherbatov @ 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 0 replies; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 17:08 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes! Firstly, please, append that to the commit message: " After that patch there are basically 2 ways of errors processing and forwarding: - Use diag only. It works for all the places out of src/box/sql, and for some functions inside it. For example, sql_expr_new(); - Use global flags Parse.is_aborted, sql.mallocFailed. It is easy to see, that some places use both of them implicitly. For example, sql_expr_new() and every other place which uses SQLite memory allocators + diag. But it is ok until the former is removed. What is more important, is that at least one of these two methods should be consistent and finished in every functions. And match a declared behaviour. For example, it is incorrect to declare a function as setting flags on an error, but in fact set diag only. Or vice versa, that it throws a diag, but factually sets flags only. " Also see my comments below, fixes at the end of the email and on the branch. While previous commits I've fixed silently, there I have something to say. > Refactored sqlExpr routine as sql_expr_new and reworked it to set > diag message in case of memory allocation error. Also performed some > additional name refactoring in adjacent places. > This change is necessary because the sqlExpr body has a > sqlNormalizeName call that will be changed in subsequent patches. > > Part of #3931 > --- > src/box/sql/build.c | 42 ++++-- > src/box/sql/expr.c | 248 ++++++++++++++---------------------- > src/box/sql/fk_constraint.c | 190 +++++++++++++++++---------- > src/box/sql/parse.y | 51 ++++++-- > src/box/sql/resolve.c | 46 ++++--- > src/box/sql/select.c | 86 +++++++++---- > src/box/sql/sqlInt.h | 84 +++++++++++- > src/box/sql/wherecode.c | 8 +- > src/box/sql/whereexpr.c | 21 +-- > 9 files changed, 471 insertions(+), 305 deletions(-) > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index f52cfd7dd..f527928bf 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c> @@ -1383,13 +1388,19 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, > struct Expr *where = NULL; > if (idx_name != NULL) { > struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); > - if (expr != NULL) > - where = sqlExprAnd(db, expr, where); > + if (expr != NULL && (expr != NULL || where != NULL)) { 1. This is what I meant and asked you to do - *self review*. Accurate, attentive, diligent labor. But look at that condition: - You check 'expr' twice. If 'expr' != NULL first time, then the second time nothing has changed. What is more, the whole second part can be removed then; - 3 lines above 'where' is declared as NULL. And here you check if it is NULL. It is not hard to find such mistakes. It requires just one single self review, which usually removes most of such things. Exactly the same about the code below. Also, a couple of words about that pattern, that you obviously just copy-pasted throughout the patch and got pearls like above: if (a != NULL || b != NULL) { c = sql_and_expr_new(a, b); if (c != NULL) is_aborted = true } It is bulky, hard to read and can be avoided. It appeared because you forbid to pass NULL NULL into sql_and_expr_new. But why? Because in such a case it returns NULL, but there is no an error? Usually it means exactly an error, and only a couple of places require such a check. Which can be done out of sql_and_expr_new by the way. > + where = sql_and_expr_new(db, expr, where); > + if (where == NULL) > + parse->is_aborted = true; > + } > } > if (table_name != NULL) { > struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name); > - if (expr != NULL) > - where = sqlExprAnd(db, expr, where); > + if (expr != NULL && (expr != NULL || where != NULL)) { > + where = sql_and_expr_new(db, expr, where); > + if (where == NULL) > + parse->is_aborted = true; 2. Above you have an assertion that either table_name != NULL, or idx_name != NULL, or both. It means, that in a normal case 'where' != NULL at the end of the function. And if it is NULL, then is_aborted can be set. Out of these 'if's. No code duplication. > + } > } > /** > * On memory allocation error sql_table delete_from > @@ -2245,9 +2256,12 @@ sql_create_index(struct Parse *parse, struct Token *token, > struct Token prev_col; > uint32_t last_field = def->field_count - 1; > sqlTokenInit(&prev_col, def->fields[last_field].name); > - col_list = sql_expr_list_append(parse->db, NULL, > - sqlExprAlloc(db, TK_ID, > - &prev_col, 0)); > + struct Expr *expr = sql_expr_new(db, TK_ID, &prev_col, false); > + if (expr == NULL) { > + parse->is_aborted = true; > + goto exit_create_index; > + } > + col_list = sql_expr_list_append(parse->db, NULL, expr); 3. You used 'db' 5 lines above. I see, that it was in the old code, but it is not an excuse. Now it is your code, and it should be proper. > if (col_list == NULL) > goto exit_create_index; > assert(col_list->nExpr == 1); > diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c > index cc4dc6448..3b27b2d4f 100644 > --- a/src/box/sql/fk_constraint.c > +++ b/src/box/sql/fk_constraint.c > @@ -303,37 +303,36 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, > > /** > * Return an Expr object that refers to a memory register > - * corresponding to column iCol of given space. > + * corresponding to column of given space. > * > - * regBase is the first of an array of register that contains > - * the data for given space. regBase+1 holds the first column. > - * regBase+2 holds the second column, and so forth. > + * reg_base is the first of an array of register that contains > + * the data for given space. reg_base+1 holds the first column. > + * reg_base+2 holds the second column, and so forth. > * > - * @param pParse Parsing and code generating context. > + * @param parser The parsing context. > * @param def Definition of space whose content is at r[regBase]... > - * @param regBase Contents of table defined by def. > - * @param iCol Which column of space is desired. > - * @return an Expr object that refers to a memory register > - * corresponding to column iCol of given space. > + * @param reg_base Contents of table table. > + * @param column Index of table column is desired. > + * @retval Not NULL expression pointer on success. > + * @retval NULL Otherwise. 4. Sorry. But sometimes I think, that probably you are kidding. In the previous commits I after all fixed that myself in scope of 'Review fixes'. But since here I do a detailed review, not just a silent fix, I will say that again: - Start sentences from a capital letter; - Do not profane comments writing. You write them just to write anything, but it should not be so. I asked you to rewrite places like this: /** * @retval Not NULL my_struct pointer on success. */ struct my_struct * func(...) Because the information you have provided in that comment is useless. Even without the comment I see, that this function returns a my_struct pointer. The same about sql_register_expr_new - I see, that it returns struct Expr pointer. C language guarantees that. And it is not only about that place. Here it is simple. But sometimes a function can return not NULL on both error and success. And then you can not say 'it returns a struct Expr pointer'. Or vice versa - return NULL on both success and error, like sql_and_expr_new. - Explicitly say if a function sets diagnostics area message. In SQL we are in a middle of two phases: when all the functions set diag, and when no one. This concrete function sets diag on any error. > */ > -static Expr * > -space_field_register(Parse *pParse, struct space_def *def, int regBase, > - i16 iCol) > +static struct Expr * > +sql_register_expr_new(struct Parse *parser, struct space_def *def, int reg_base, > + int column)> { > - Expr *pExpr; > - sql *db = pParse->db; > - > - pExpr = sqlExpr(db, TK_REGISTER, 0); > - if (pExpr) { > - if (iCol >= 0) { > - pExpr->iTable = regBase + iCol + 1; > - pExpr->type = def->fields[iCol].type; > - } else { > - pExpr->iTable = regBase; > - pExpr->type = FIELD_TYPE_INTEGER; > - } > + struct Expr *expr = sql_op_expr_new(parser->db, TK_REGISTER, NULL); > + if (expr == NULL) { > + parser->is_aborted = true; > + return NULL; > + } > + if (column >= 0) { 5. When I first saw that check I wondered how is it possible that a column number is negative? Appeared that there is no way for that. I've added an assertion about column >= 0 and the tests passed. What is more, in all places where that function is called, it takes uint32_t. So this check and the 'else' body are not necessary. > + expr->iTable = reg_base + column + 1; > + expr->type = def->fields[column].type; > + } else { > + expr->iTable = reg_base; > + expr->type = FIELD_TYPE_INTEGER; > } > - return pExpr; > + return expr; > } > > /** > @@ -435,12 +435,18 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, > for (uint32_t i = 0; i < fk_def->field_count; i++) { > uint32_t fieldno = fk_def->links[i].parent_field; > struct Expr *pexpr = > - space_field_register(parser, def, reg_data, fieldno); > + sql_register_expr_new(parser, def, reg_data, fieldno); > fieldno = fk_def->links[i].child_field; > const char *field_name = child_space->def->fields[fieldno].name; > - struct Expr *chexpr = sqlExpr(db, TK_ID, field_name); > + struct Expr *chexpr = sql_op_expr_new(db, TK_ID, field_name); > + if (chexpr == NULL) > + parser->is_aborted = true; > struct Expr *eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); > - where = sqlExprAnd(db, where, eq); > + if (where != NULL || eq != NULL) { 6. You do not need that check here, because if eq == NULL, then pexpr and cheexpr are nulls. If they are nulls, then it was an error, and it is ok to set is_aborted. Even if it is already set. The same in many other places. Of course, after revert of sql_and_expr_new asserting on NULL NULL. > + where = sql_and_expr_new(db, where, eq); > + if (where == NULL) > + parser->is_aborted = true; > + } > } > > /*> @@ -785,14 +802,26 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, > * type and collation sequence associated with > * the parent table are used for the comparison. > */ > - struct Expr *to_col = > - sqlPExpr(pParse, TK_DOT, > - sqlExprAlloc(db, TK_ID, &t_old, 0), > - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); > - struct Expr *from_col = > - sqlExprAlloc(db, TK_ID, &t_from_col, 0); > - struct Expr *eq = sqlPExpr(pParse, TK_EQ, to_col, from_col); > - where = sqlExprAnd(db, where, eq); > + struct Expr *old_expr = NULL, *new_expr = NULL, *expr = NULL; > + old_expr = sql_expr_new(db, TK_ID, &t_old, false); > + if (old_expr == NULL) > + pParse->is_aborted = true; > + expr = sql_expr_new(db, TK_ID, &t_to_col, false); > + if (expr == NULL) > + pParse->is_aborted = true; > + struct Expr *from_col_expr = > + sql_expr_new(db, TK_ID, &t_from_col, false); > + if (from_col_expr == NULL) > + pParse->is_aborted = true; > + struct Expr *to_col_expr = > + sqlPExpr(pParse, TK_DOT, old_expr, expr); > + struct Expr *eq = > + sqlPExpr(pParse, TK_EQ, to_col_expr, from_col_expr); > + if (where != NULL || eq != NULL) { > + where = sql_and_expr_new(db, where, eq); > + if (where == NULL) > + pParse->is_aborted = true; > + } 7. Talking of this hunk and the hunk below - I do not know what to say. Even before the patch it looked cursed and corrupted, but now it is even worse. Much worse. The code padded out in 2.5 times. Also, it is in fact repeated here and below. Fully repeated with just different variable names. I've encapsulated most of these into a new function sql_expr_new_2part_id() and batched checks for expr == NULL to set aborts. > > /* > * For ON UPDATE, construct the next term of the > @@ -810,12 +839,22 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, > * no_action_needed(colN)) > */ > if (is_update) { > + old_expr = sql_expr_new(db, TK_ID, &t_old, false); > + if (old_expr == NULL) > + pParse->is_aborted = true; > + expr = sql_expr_new(db, TK_ID, &t_to_col, false); > + if (expr == NULL) > + pParse->is_aborted = true; > struct Expr *old_val = sqlPExpr(pParse, TK_DOT, > - sqlExprAlloc(db, TK_ID, &t_old, 0), > - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); > + old_expr, expr); > + new_expr = sql_expr_new(db, TK_ID, &t_new, false); > + if (new_expr == NULL) > + pParse->is_aborted = true; > + expr = sql_expr_new(db, TK_ID, &t_to_col, false); > + if (expr == NULL) > + pParse->is_aborted = true; > struct Expr *new_val = sqlPExpr(pParse, TK_DOT, > - sqlExprAlloc(db, TK_ID, &t_new, 0), > - sqlExprAlloc(db, TK_ID, &t_to_col, 0)); > + new_expr, expr); > struct Expr *old_is_null = sqlPExpr( > pParse, TK_ISNULL, > sqlExprDup(db, old_val, 0), NULL); > @@ -828,29 +867,41 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, > if (action != FKEY_ACTION_RESTRICT && > (action != FKEY_ACTION_CASCADE || is_update)) { > struct Expr *new, *d; > if (action == FKEY_ACTION_CASCADE) { > - new = sqlPExpr(pParse, TK_DOT, > - sqlExprAlloc(db, TK_ID, > - &t_new, 0), > - sqlExprAlloc(db, TK_ID, > - &t_to_col, > - 0)); > + new_expr = sql_expr_new(db, TK_ID, &t_new, > + false); > + if (new_expr == NULL) > + pParse->is_aborted = true; > + expr = sql_expr_new(db, TK_ID, &t_to_col, > + false); > + if (expr == NULL) > + pParse->is_aborted = true; > + new = sqlPExpr(pParse, TK_DOT, new_expr, expr); > } else if (action == FKEY_ACTION_SET_DEFAULT) { > d = child_fields[chcol].default_value_expr; > if (d != NULL) { > new = sqlExprDup(db, d, 0); > } else { > - new = sqlExprAlloc(db, TK_NULL, > - NULL, 0); > + new = sql_expr_new(db, TK_NULL, NULL, > + false); > + if (new == NULL) > + pParse->is_aborted = true; > } > } else { > - new = sqlExprAlloc(db, TK_NULL, NULL, 0); > + new = sql_expr_new(db, TK_NULL, NULL, false); > + if (new == NULL) > + pParse->is_aborted = true; > } 8. Exactly the same about this code. Before the patch is was not the best, but good enough because of low code duplication. But now the last branch and the branch 'action == FKEY_ACTION_SET_DEFAULT && d == NULL' are too big and still do the same. I've reworked that to remove the duplication. > list = sql_expr_list_append(db, list, new); > sqlExprListSetName(pParse, list, &t_from_col, 0);> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > index f71990dee..4243e799c 100644 > --- a/src/box/sql/parse.y > +++ b/src/box/sql/parse.y > @@ -898,15 +906,28 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} > expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} > expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} > expr(A) ::= nm(X) DOT nm(Y). { > - Expr *temp1 = sqlExprAlloc(pParse->db, TK_ID, &X, 1); > - Expr *temp2 = sqlExprAlloc(pParse->db, TK_ID, &Y, 1); > + struct Expr *temp1 = sql_expr_new(pParse->db, TK_ID, &X, true); > + if (temp1 == NULL) { > + pParse->is_aborted = true; > + return; > + } > + struct Expr *temp2 = sql_expr_new(pParse->db, TK_ID, &Y, true); > + if (temp2 == NULL) { > + sql_expr_delete(pParse->db, temp2, false); 9. temp2 is NULL. You checked it one line above. It should be temp1. > + pParse->is_aborted = true; > + return; > + }> diff --git a/src/box/sql/select.c b/src/box/sql/select.c > index 1b7d52b68..5bba0b5d0 100644 > --- a/src/box/sql/select.c > +++ b/src/box/sql/select.c > @@ -497,8 +498,12 @@ addWhereTerm(Parse * pParse, /* Parsing context */ > assert(pSrc->a[iLeft].space != NULL); > assert(pSrc->a[iRight].space != NULL); > > - pE1 = sqlCreateColumnExpr(db, pSrc, iLeft, iColLeft); > - pE2 = sqlCreateColumnExpr(db, pSrc, iRight, iColRight); > + pE1 = sql_column_expr_new(pParse->db, pSrc, iLeft, iColLeft); > + if (pE1 == NULL) > + pParse->is_aborted = true; > + pE2 = sql_column_expr_new(pParse->db, pSrc, iRight, iColRight); > + if (pE2 == NULL) > + pParse->is_aborted = true; 10. You could check for both pE1 and pE2 == NULL in one 'if' using '||'. > > pEq = sqlPExpr(pParse, TK_EQ, pE1, pE2); > if (pEq && isOuterJoin) { > @@ -628,8 +637,13 @@ sqlProcessJoin(Parse * pParse, Select * p) > if (pRight->pOn) { > if (isOuter) > setJoinExpr(pRight->pOn, pRight->iCursor); > - p->pWhere = > - sqlExprAnd(pParse->db, p->pWhere, pRight->pOn); > + if (p->pWhere != NULL || pRight->pOn != NULL) { 11. One another example why self-review is necessary. pRight->pOn is already checked for not being equal NULL 2 lines above. It makes your 'if' useless. > + p->pWhere = > + sql_and_expr_new(pParse->db, p->pWhere, > + pRight->pOn); > + if (p->pWhere == NULL) > + pParse->is_aborted = true; > + } > pRight->pOn = 0; > } > > @@ -4324,8 +4346,13 @@ pushDownWhereTerms(Parse * pParse, /* Parse context (for malloc() and error repo > while (pSubq) { > pNew = sqlExprDup(pParse->db, pWhere, 0); > pNew = substExpr(pParse, pNew, iCursor, pSubq->pEList); > - pSubq->pWhere = > - sqlExprAnd(pParse->db, pSubq->pWhere, pNew); > + if (pSubq->pWhere != NULL || pNew != NULL) { 12. If pNew is NULL, then it is an error, because pWhere is not NULL, it is guaranteed by the code above (implicitly via attributes access, for example). In such a case pNew NULL and pSubq->pWhere NULL mean an error, and it is ok to set abort. > + pSubq->pWhere = > + sql_and_expr_new(pParse->db, > + pSubq->pWhere, pNew); > + if (pSubq->pWhere == NULL) > + pParse->is_aborted = true; > + } > pSubq = pSubq->pPrior; > } > } > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index d3ffa9d9b..13d0b16f8 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3223,13 +3223,73 @@ void sqlClearTempRegCache(Parse *); > +/** > + * This routine is the core allocator for Expr nodes. > + * Construct a new expression node and return a pointer to it. > + * Memory for this node and for the token argument is a single > + * allocation obtained from sqlDbMalloc(). The calling function > + * is responsible for making sure the node eventually gets freed. > + * > + * If dequote is true, then the token (if it exists) is dequoted. > + * If dequote is false, no dequoting is performed. The dequote > + * parameter is ignored if token is NULL or if the token does > + * not appear to be quoted. If the quotes were of the form "..." > + * (double-quotes) then the EP_DblQuoted flag is set on the > + * expression node. > + * > + * Special case: If op==TK_INTEGER and token points to a string > + * that can be translated into a 32-bit integer, then the token is > + * not stored in u.zToken. Instead, the integer values is written > + * into u.iValue and the EP_IntValue flag is set. No extra storage > + * is allocated to hold the integer text and the dequote flag is > + * ignored. > + * > + * @param db The database connection. > + * @param op Expression opcode (TK_*). > + * @param token Source token. Might be NULL. > + * @param dequote True to dequote string. > + * @retval Not NULL New expression object on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct Expr * > +sql_expr_new(struct sql *db, int op, const Token *token, bool dequote); 13. After you has changed the last argument type to boolean, the function call looks too long. I've split sql_expr_new into two functions: sql_expr_new and sql_expr_new_dequoted. > + > +/** > + * Allocate a new expression node from a zero-terminated token > + * that has already been dequoted. > + * > + * @param db The database connection. > + * @param op Expression opcode. > + * @param name The object name string. > + * @retval Not NULL Expression pointer on success. > + * @retval NULL Otherwise. The diag message is set. > + */ > +struct Expr * > +sql_op_expr_new(struct sql *db, int op, const char *name); 14. That name looks strange. You want to emphasize that this function takes an 'op'. But sql_expr_new() takes 'op' as well. And what is more - uses it in the exactly same way. I've renamed it to sql_expr_new_named(). Also, I've added an alias for NULL names: sql_expr_new_anon(). > diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c > index be30a40c0..036540d61 100644 > --- a/src/box/sql/wherecode.c > +++ b/src/box/sql/wherecode.c > @@ -1372,7 +1372,13 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W > continue; > testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); > pExpr = sqlExprDup(db, pExpr, 0); > - pAndExpr = sqlExprAnd(db, pAndExpr, pExpr); > + if (pAndExpr != NULL || pExpr != NULL) { 15. If pExpr here is NULL, then it is already an error, because 2 lines above it was not NULL - it was used to check ExprHasProperty. It means, that sql_and_expr_new() NULL result always is an error and it is ok to abort. > + pAndExpr = > + sql_and_expr_new(db, pAndExpr, > + pExpr); > + if (pAndExpr == NULL) > + pParse->is_aborted = true; > + } > } > if (pAndExpr) { > pAndExpr = ========================================================================== commit fc09568fad97d9bc548adaa9e54c8fcd7a88a980 Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Tue Mar 26 11:38:07 2019 +0300 Review fixes diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 10861dd15..2e4558408 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -616,7 +616,7 @@ sqlAddPrimaryKey(Parse * pParse, /* Parsing context */ struct ExprList *list; struct Token token; sqlTokenInit(&token, space->def->fields[iCol].name); - struct Expr *expr = sql_expr_new(db, TK_ID, &token, false); + struct Expr *expr = sql_expr_new(db, TK_ID, &token); if (expr == NULL) { pParse->is_aborted = true; goto primary_key_exit; @@ -1358,12 +1358,13 @@ sql_id_eq_str_expr(struct Parse *parse, const char *col_name, const char *col_value) { struct sql *db = parse->db; - struct Expr *col_name_expr = sql_op_expr_new(db, TK_ID, col_name); + struct Expr *col_name_expr = sql_expr_new_named(db, TK_ID, col_name); if (col_name_expr == NULL) { parse->is_aborted = true; return NULL; } - struct Expr *col_value_expr = sql_op_expr_new(db, TK_STRING, col_value); + struct Expr *col_value_expr = + sql_expr_new_named(db, TK_STRING, col_value); if (col_value_expr == NULL) { sql_expr_delete(db, col_name_expr, false); parse->is_aborted = true; @@ -1385,23 +1386,17 @@ vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name, return; } src_list->a[0].zName = sqlDbStrDup(db, stat_table_name); - struct Expr *where = NULL; + struct Expr *expr, *where = NULL; if (idx_name != NULL) { - struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name); - if (expr != NULL && (expr != NULL || where != NULL)) { - where = sql_and_expr_new(db, expr, where); - if (where == NULL) - parse->is_aborted = true; - } + expr = sql_id_eq_str_expr(parse, "idx", idx_name); + where = sql_and_expr_new(db, expr, where); } if (table_name != NULL) { - struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", table_name); - if (expr != NULL && (expr != NULL || where != NULL)) { - where = sql_and_expr_new(db, expr, where); - if (where == NULL) - parse->is_aborted = true; - } + expr = sql_id_eq_str_expr(parse, "tbl", table_name); + where = sql_and_expr_new(db, expr, where); } + if (where == NULL) + parse->is_aborted = true; /** * On memory allocation error sql_table delete_from * releases memory for its own. @@ -2256,12 +2251,12 @@ sql_create_index(struct Parse *parse, struct Token *token, struct Token prev_col; uint32_t last_field = def->field_count - 1; sqlTokenInit(&prev_col, def->fields[last_field].name); - struct Expr *expr = sql_expr_new(db, TK_ID, &prev_col, false); + struct Expr *expr = sql_expr_new(db, TK_ID, &prev_col); if (expr == NULL) { parse->is_aborted = true; goto exit_create_index; } - col_list = sql_expr_list_append(parse->db, NULL, expr); + col_list = sql_expr_list_append(db, NULL, expr); if (col_list == NULL) goto exit_create_index; assert(col_list->nExpr == 1); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index dad4ce3a6..9c8d9fdf4 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -151,8 +151,12 @@ sqlExprAddCollateToken(Parse * pParse, /* Parsing context */ { if (pCollName->n == 0) return pExpr; - struct Expr *new_expr = - sql_expr_new(pParse->db, TK_COLLATE, pCollName, dequote); + struct Expr *new_expr; + struct sql *db = pParse->db; + if (dequote) + new_expr = sql_expr_new_dequoted(db, TK_COLLATE, pCollName); + else + new_expr = sql_expr_new(db, TK_COLLATE, pCollName); if (new_expr == NULL) { pParse->is_aborted = true; return pExpr; @@ -876,7 +880,7 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) #endif /* SQL_MAX_EXPR_DEPTH>0 */ struct Expr * -sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) +sql_expr_new(struct sql *db, int op, const struct Token *token) { int extra_sz = 0; int val = 0; @@ -911,25 +915,23 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) assert(token->z != NULL || token->n == 0); memcpy(expr->u.zToken, token->z, token->n); expr->u.zToken[token->n] = '\0'; - if (dequote) { - if (expr->u.zToken[0] == '"') - expr->flags |= EP_DblQuoted; - if (expr->op == TK_ID || expr->op == TK_COLLATE || - expr->op == TK_FUNCTION) - sqlNormalizeName(expr->u.zToken); - else - sqlDequote(expr->u.zToken); - } } return expr; } struct Expr * -sql_op_expr_new(struct sql *db, int op, const char *name) +sql_expr_new_dequoted(struct sql *db, int op, const struct Token *token) { - struct Token name_token; - sqlTokenInit(&name_token, (char *)name); - return sql_expr_new(db, op, &name_token, false); + struct Expr *e = sql_expr_new(db, op, token); + if (e == NULL || (e->flags & EP_IntValue) != 0 || e->u.zToken == NULL) + return e; + if (e->u.zToken[0] == '"') + e->flags |= EP_DblQuoted; + if (e->op == TK_ID || e->op == TK_COLLATE || e->op == TK_FUNCTION) + sqlNormalizeName(e->u.zToken); + else + sqlDequote(e->u.zToken); + return e; } /* @@ -979,8 +981,6 @@ sqlPExpr(Parse * pParse, /* Parsing context */ * Take advantage of short-circuit false * optimization for AND. */ - if (pLeft == NULL && pRight == NULL) - return NULL; p = sql_and_expr_new(pParse->db, pLeft, pRight); if (p == NULL) pParse->is_aborted = true; @@ -1057,17 +1057,15 @@ sql_and_expr_new(struct sql *db, struct Expr *left_expr, struct Expr *right_expr) { if (left_expr == NULL) { - assert(right_expr != NULL); return right_expr; } else if (right_expr == NULL) { - assert(left_expr != NULL); return left_expr; } else if (exprAlwaysFalse(left_expr) || exprAlwaysFalse(right_expr)) { sql_expr_delete(db, left_expr, false); sql_expr_delete(db, right_expr, false); - return sql_expr_new(db, TK_INTEGER, &sqlIntTokens[0], false); + return sql_expr_new(db, TK_INTEGER, &sqlIntTokens[0]); } else { - struct Expr *new_expr = sql_expr_new(db, TK_AND, NULL, false); + struct Expr *new_expr = sql_expr_new_anon(db, TK_AND); sqlExprAttachSubtrees(db, new_expr, left_expr, right_expr); return new_expr; } @@ -1082,7 +1080,7 @@ sqlExprFunction(Parse * pParse, ExprList * pList, Token * pToken) { struct sql *db = pParse->db; assert(pToken != NULL); - struct Expr *new_expr = sql_expr_new(db, TK_FUNCTION, pToken, true); + struct Expr *new_expr = sql_expr_new_dequoted(db, TK_FUNCTION, pToken); if (new_expr == NULL) { sql_expr_list_delete(db, pList); pParse->is_aborted = true; @@ -2903,7 +2901,7 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */ if (pSel->pLimit == NULL) { pSel->pLimit = sql_expr_new(pParse->db, TK_INTEGER, - &sqlIntTokens[1], false); + &sqlIntTokens[1]); if (pSel->pLimit == NULL) { pParse->is_aborted = true; } else { diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index 3b27b2d4f..8151c66e5 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -302,36 +302,28 @@ fk_constraint_lookup_parent(struct Parse *parse_context, struct space *parent, } /** - * Return an Expr object that refers to a memory register - * corresponding to column of given space. - * - * reg_base is the first of an array of register that contains - * the data for given space. reg_base+1 holds the first column. - * reg_base+2 holds the second column, and so forth. - * - * @param parser The parsing context. - * @param def Definition of space whose content is at r[regBase]... - * @param reg_base Contents of table table. - * @param column Index of table column is desired. - * @retval Not NULL expression pointer on success. - * @retval NULL Otherwise. + * Build an expression that refers to a memory register + * corresponding to @a column of given space. + * + * @param db SQL context. + * @param def Definition of space whose content starts from + * @a reg_base register. + * @param reg_base Index of a first element in an array of + * registers, containing data of a space. Register + * reg_base + i holds an i-th column, i >= 1. + * @param column Index of a first table column to point at. + * @retval Not NULL Success. An expression representing register. + * @retval NULL Error. A diag message is set. */ static struct Expr * -sql_register_expr_new(struct Parse *parser, struct space_def *def, int reg_base, - int column) +sql_expr_new_register(struct sql *db, struct space_def *def, int reg_base, + uint32_t column) { - struct Expr *expr = sql_op_expr_new(parser->db, TK_REGISTER, NULL); - if (expr == NULL) { - parser->is_aborted = true; + struct Expr *expr = sql_expr_new_anon(db, TK_REGISTER); + if (expr == NULL) return NULL; - } - if (column >= 0) { - expr->iTable = reg_base + column + 1; - expr->type = def->fields[column].type; - } else { - expr->iTable = reg_base; - expr->type = FIELD_TYPE_INTEGER; - } + expr->iTable = reg_base + column + 1; + expr->type = def->fields[column].type; return expr; } @@ -346,10 +338,10 @@ sql_register_expr_new(struct Parse *parser, struct space_def *def, int reg_base, * @retval NULL on error. */ static struct Expr * -sql_column_cursor_expr_create(struct sql *db, struct space_def *def, +sql_expr_new_column_by_cursor(struct sql *db, struct space_def *def, int cursor, int column) { - struct Expr *expr = sql_op_expr_new(db, TK_COLUMN, NULL); + struct Expr *expr = sql_expr_new_anon(db, TK_COLUMN); if (expr == NULL) return NULL; expr->space_def = def; @@ -435,18 +427,14 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; struct Expr *pexpr = - sql_register_expr_new(parser, def, reg_data, fieldno); + sql_expr_new_register(db, def, reg_data, fieldno); fieldno = fk_def->links[i].child_field; const char *field_name = child_space->def->fields[fieldno].name; - struct Expr *chexpr = sql_op_expr_new(db, TK_ID, field_name); - if (chexpr == NULL) - parser->is_aborted = true; + struct Expr *chexpr = sql_expr_new_named(db, TK_ID, field_name); struct Expr *eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - if (where != NULL || eq != NULL) { - where = sql_and_expr_new(db, where, eq); - if (where == NULL) - parser->is_aborted = true; - } + where = sql_and_expr_new(db, where, eq); + if (where == NULL || chexpr == NULL || pexpr == NULL) + parser->is_aborted = true; } /* @@ -462,26 +450,20 @@ fk_constraint_scan_children(struct Parse *parser, struct SrcList *src, struct Expr *expr = NULL, *pexpr, *chexpr, *eq; for (uint32_t i = 0; i < fk_def->field_count; i++) { uint32_t fieldno = fk_def->links[i].parent_field; - pexpr = sql_register_expr_new(parser, def, reg_data, + pexpr = sql_expr_new_register(db, def, reg_data, fieldno); int cursor = src->a[0].iCursor; - chexpr = sql_column_cursor_expr_create(db, def, cursor, + chexpr = sql_expr_new_column_by_cursor(db, def, cursor, fieldno); - if (chexpr == NULL) - parser->is_aborted = true; eq = sqlPExpr(parser, TK_EQ, pexpr, chexpr); - if (expr != NULL || eq != NULL) { - expr = sql_and_expr_new(db, expr, eq); - if (expr == NULL) - parser->is_aborted = true; - } - } - struct Expr *pNe = sqlPExpr(parser, TK_NOT, expr, 0); - if (where != NULL || pNe != NULL) { - where = sql_and_expr_new(db, where, pNe); - if (where == NULL) + expr = sql_and_expr_new(db, expr, eq); + if (expr == NULL || chexpr == NULL || pexpr == NULL) parser->is_aborted = true; } + struct Expr *pNe = sqlPExpr(parser, TK_NOT, expr, 0); + where = sql_and_expr_new(db, where, pNe); + if (where == NULL) + parser->is_aborted = true; } /* Resolve the references in the WHERE clause. */ @@ -720,6 +702,28 @@ fk_constraint_is_required(struct space *space, const int *changes) return false; } +/** + * Create a new expression representing two-part path + * '<main>.<sub>'. + * @param parser SQL Parser. + * @param main First and main part of a result path. For example, + * a table name. + * @param sub Second and last part of a result path. For example, + * a column name. + * @retval Not NULL Success. An expression with two-part path. + * @retval NULL Error. A diag message is set. + */ +static inline struct Expr * +sql_expr_new_2part_id(struct Parse *parser, const struct Token *main, + const struct Token *sub) +{ + struct Expr *emain = sql_expr_new(parser->db, TK_ID, main); + struct Expr *esub = sql_expr_new(parser->db, TK_ID, sub); + if (emain == NULL || esub == NULL) + parser->is_aborted = true; + return sqlPExpr(parser, TK_DOT, emain, esub); +} + /** * This function is called when an UPDATE or DELETE operation is * being compiled on table pTab, which is the parent table of @@ -802,27 +806,13 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * type and collation sequence associated with * the parent table are used for the comparison. */ - struct Expr *old_expr = NULL, *new_expr = NULL, *expr = NULL; - old_expr = sql_expr_new(db, TK_ID, &t_old, false); - if (old_expr == NULL) - pParse->is_aborted = true; - expr = sql_expr_new(db, TK_ID, &t_to_col, false); - if (expr == NULL) - pParse->is_aborted = true; - struct Expr *from_col_expr = - sql_expr_new(db, TK_ID, &t_from_col, false); - if (from_col_expr == NULL) + struct Expr *new, *old = + sql_expr_new_2part_id(pParse, &t_old, &t_to_col); + struct Expr *from = sql_expr_new(db, TK_ID, &t_from_col); + struct Expr *eq = sqlPExpr(pParse, TK_EQ, old, from); + where = sql_and_expr_new(db, where, eq); + if (where == NULL || from == NULL) pParse->is_aborted = true; - struct Expr *to_col_expr = - sqlPExpr(pParse, TK_DOT, old_expr, expr); - struct Expr *eq = - sqlPExpr(pParse, TK_EQ, to_col_expr, from_col_expr); - if (where != NULL || eq != NULL) { - where = sql_and_expr_new(db, where, eq); - if (where == NULL) - pParse->is_aborted = true; - } - /* * For ON UPDATE, construct the next term of the * WHEN clause, which should return false in case @@ -839,67 +829,36 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, * no_action_needed(colN)) */ if (is_update) { - old_expr = sql_expr_new(db, TK_ID, &t_old, false); - if (old_expr == NULL) - pParse->is_aborted = true; - expr = sql_expr_new(db, TK_ID, &t_to_col, false); - if (expr == NULL) - pParse->is_aborted = true; - struct Expr *old_val = sqlPExpr(pParse, TK_DOT, - old_expr, expr); - new_expr = sql_expr_new(db, TK_ID, &t_new, false); - if (new_expr == NULL) - pParse->is_aborted = true; - expr = sql_expr_new(db, TK_ID, &t_to_col, false); - if (expr == NULL) - pParse->is_aborted = true; - struct Expr *new_val = sqlPExpr(pParse, TK_DOT, - new_expr, expr); - struct Expr *old_is_null = sqlPExpr( - pParse, TK_ISNULL, - sqlExprDup(db, old_val, 0), NULL); - eq = sqlPExpr(pParse, TK_EQ, old_val, - sqlExprDup(db, new_val, 0)); + old = sql_expr_new_2part_id(pParse, &t_old, &t_to_col); + new = sql_expr_new_2part_id(pParse, &t_new, &t_to_col); + struct Expr *old_is_null = + sqlPExpr(pParse, TK_ISNULL, + sqlExprDup(db, old, 0), NULL); + eq = sqlPExpr(pParse, TK_EQ, old, + sqlExprDup(db, new, 0)); struct Expr *new_non_null = - sqlPExpr(pParse, TK_NOTNULL, new_val, NULL); + sqlPExpr(pParse, TK_NOTNULL, new, NULL); struct Expr *non_null_eq = sqlPExpr(pParse, TK_AND, new_non_null, eq); struct Expr *no_action_needed = sqlPExpr(pParse, TK_OR, old_is_null, non_null_eq); - if (when != NULL || no_action_needed != NULL) { - when = sql_and_expr_new(db, when, - no_action_needed); - if (when == NULL) - pParse->is_aborted = true; - } + when = sql_and_expr_new(db, when, no_action_needed); + if (when == NULL) + pParse->is_aborted = true; } if (action != FKEY_ACTION_RESTRICT && (action != FKEY_ACTION_CASCADE || is_update)) { - struct Expr *new, *d; + struct Expr *d = child_fields[chcol].default_value_expr; if (action == FKEY_ACTION_CASCADE) { - new_expr = sql_expr_new(db, TK_ID, &t_new, - false); - if (new_expr == NULL) - pParse->is_aborted = true; - expr = sql_expr_new(db, TK_ID, &t_to_col, - false); - if (expr == NULL) - pParse->is_aborted = true; - new = sqlPExpr(pParse, TK_DOT, new_expr, expr); - } else if (action == FKEY_ACTION_SET_DEFAULT) { - d = child_fields[chcol].default_value_expr; - if (d != NULL) { - new = sqlExprDup(db, d, 0); - } else { - new = sql_expr_new(db, TK_NULL, NULL, - false); - if (new == NULL) - pParse->is_aborted = true; - } + new = sql_expr_new_2part_id(pParse, &t_new, + &t_to_col); + } else if (action == FKEY_ACTION_SET_DEFAULT && + d != NULL) { + new = sqlExprDup(db, d, 0); } else { - new = sql_expr_new(db, TK_NULL, NULL, false); + new = sql_expr_new_anon(db, TK_NULL); if (new == NULL) pParse->is_aborted = true; } @@ -915,9 +874,8 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, struct Token err; err.z = space_name; err.n = name_len; - struct Expr *r = - sql_op_expr_new(db, TK_RAISE, - "FOREIGN KEY constraint failed"); + struct Expr *r = sql_expr_new_named(db, TK_RAISE, "FOREIGN "\ + "KEY constraint failed"); if (r == NULL) pParse->is_aborted = true; else diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 4243e799c..c749cf1bd 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -536,7 +536,7 @@ selcollist(A) ::= sclp(A) expr(X) as(Y). { sqlExprListSetSpan(pParse,A,&X); } selcollist(A) ::= sclp(A) STAR. { - struct Expr *p = sql_op_expr_new(pParse->db, TK_ASTERISK, NULL); + struct Expr *p = sql_expr_new_anon(pParse->db, TK_ASTERISK); if (p == NULL) { pParse->is_aborted = true; return; @@ -544,7 +544,7 @@ selcollist(A) ::= sclp(A) STAR. { A = sql_expr_list_append(pParse->db, A, p); } selcollist(A) ::= sclp(A) nm(X) DOT STAR. { - struct Expr *pLeft = sql_expr_new(pParse->db, TK_ID, &X, true); + struct Expr *pLeft = sql_expr_new_dequoted(pParse->db, TK_ID, &X); if (pLeft == NULL) { pParse->is_aborted = true; return; @@ -906,14 +906,14 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { - struct Expr *temp1 = sql_expr_new(pParse->db, TK_ID, &X, true); + struct Expr *temp1 = sql_expr_new_dequoted(pParse->db, TK_ID, &X); if (temp1 == NULL) { pParse->is_aborted = true; return; } - struct Expr *temp2 = sql_expr_new(pParse->db, TK_ID, &Y, true); + struct Expr *temp2 = sql_expr_new_dequoted(pParse->db, TK_ID, &Y); if (temp2 == NULL) { - sql_expr_delete(pParse->db, temp2, false); + sql_expr_delete(pParse->db, temp1, false); pParse->is_aborted = true; return; } @@ -923,7 +923,7 @@ expr(A) ::= nm(X) DOT nm(Y). { term(A) ::= FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} term(A) ::= INTEGER(X). { - A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &X, true); + A.pExpr = sql_expr_new_dequoted(pParse->db, TK_INTEGER, &X); if (A.pExpr == NULL) { pParse->is_aborted = true; return; @@ -963,7 +963,7 @@ expr(A) ::= expr(A) COLLATE id(C). { %ifndef SQL_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sql_expr_new(pParse->db, TK_CAST, NULL, true); + A.pExpr = sql_expr_new_dequoted(pParse->db, TK_CAST, NULL); if (A.pExpr == NULL) { pParse->is_aborted = true; return; @@ -1161,7 +1161,7 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] { ** regardless of the value of expr1. */ sql_expr_delete(pParse->db, A.pExpr, false); - A.pExpr = sql_expr_new(pParse->db, TK_INTEGER, &sqlIntTokens[N], true); + A.pExpr = sql_expr_new_dequoted(pParse->db, TK_INTEGER, &sqlIntTokens[N]); if (A.pExpr == NULL) { pParse->is_aborted = true; return; @@ -1506,7 +1506,7 @@ expr(A) ::= RAISE(X) LP IGNORE RP(Y). { } expr(A) ::= RAISE(X) LP raisetype(T) COMMA STRING(Z) RP(Y). { spanSet(&A,&X,&Y); /*A-overwrites-X*/ - A.pExpr = sql_expr_new(pParse->db, TK_RAISE, &Z, true); + A.pExpr = sql_expr_new_dequoted(pParse->db, TK_RAISE, &Z); if(A.pExpr == NULL) { pParse->is_aborted = true; return; diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index eb74d024c..075e1fab4 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -488,10 +488,10 @@ lookupName(Parse * pParse, /* The parsing context */ } struct Expr * -sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, +sql_expr_new_column(struct sql *db, struct SrcList *src_list, int src_idx, int column) { - struct Expr *expr = sql_expr_new(db, TK_COLUMN, NULL, false); + struct Expr *expr = sql_expr_new_anon(db, TK_COLUMN); if (expr == NULL) return NULL; struct SrcList_item *item = &src_list->a[src_idx]; @@ -995,7 +995,7 @@ resolveCompoundOrderBy(Parse * pParse, /* Parsing context. Leave error messages * taking care to preserve the COLLATE clause if it exists */ struct Expr *pNew = - sql_op_expr_new(db, TK_INTEGER, NULL); + sql_expr_new_anon(db, TK_INTEGER); if (pNew == NULL) { pParse->is_aborted = true; return 1; @@ -1349,7 +1349,7 @@ resolveSelectStep(Walker * pWalker, Select * p) */ sql_expr_delete(db, p->pLimit, false); p->pLimit = sql_expr_new(db, TK_INTEGER, - &sqlIntTokens[1], false); + &sqlIntTokens[1]); if (p->pLimit == NULL) pParse->is_aborted = true; } else { diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 5bba0b5d0..6d115182d 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -164,10 +164,10 @@ sqlSelectNew(Parse * pParse, /* Parsing context */ pNew = &standin; } if (pEList == 0) { - struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + struct Expr *expr = sql_expr_new_anon(db, TK_ASTERISK); if (expr == NULL) pParse->is_aborted = true; - pEList = sql_expr_list_append(pParse->db, NULL, expr); + pEList = sql_expr_list_append(db, NULL, expr); } struct session MAYBE_UNUSED *user_session; user_session = current_session(); @@ -489,8 +489,7 @@ addWhereTerm(Parse * pParse, /* Parsing context */ int isOuterJoin, /* True if this is an OUTER join */ Expr ** ppWhere) /* IN/OUT: The WHERE clause to add to */ { - Expr *pE1; - Expr *pE2; + struct sql *db = pParse->db; Expr *pEq; assert(iLeft < iRight); @@ -498,13 +497,10 @@ addWhereTerm(Parse * pParse, /* Parsing context */ assert(pSrc->a[iLeft].space != NULL); assert(pSrc->a[iRight].space != NULL); - pE1 = sql_column_expr_new(pParse->db, pSrc, iLeft, iColLeft); - if (pE1 == NULL) + struct Expr *pE1 = sql_expr_new_column(db, pSrc, iLeft, iColLeft); + struct Expr *pE2 = sql_expr_new_column(db, pSrc, iRight, iColRight); + if (pE1 == NULL || pE2 == NULL) pParse->is_aborted = true; - pE2 = sql_column_expr_new(pParse->db, pSrc, iRight, iColRight); - if (pE2 == NULL) - pParse->is_aborted = true; - pEq = sqlPExpr(pParse, TK_EQ, pE1, pE2); if (pEq && isOuterJoin) { ExprSetProperty(pEq, EP_FromJoin); @@ -512,11 +508,9 @@ addWhereTerm(Parse * pParse, /* Parsing context */ ExprSetVVAProperty(pEq, EP_NoReduce); pEq->iRightJoinTable = (i16) pE2->iTable; } - if (*ppWhere != NULL || pEq != NULL) { - *ppWhere = sql_and_expr_new(pParse->db, *ppWhere, pEq); - if (*ppWhere == NULL) - pParse->is_aborted = true; - } + *ppWhere = sql_and_expr_new(db, *ppWhere, pEq); + if (*ppWhere == NULL) + pParse->is_aborted = true; } /* @@ -637,13 +631,10 @@ sqlProcessJoin(Parse * pParse, Select * p) if (pRight->pOn) { if (isOuter) setJoinExpr(pRight->pOn, pRight->iCursor); - if (p->pWhere != NULL || pRight->pOn != NULL) { - p->pWhere = - sql_and_expr_new(pParse->db, p->pWhere, - pRight->pOn); - if (p->pWhere == NULL) - pParse->is_aborted = true; - } + p->pWhere = sql_and_expr_new(pParse->db, p->pWhere, + pRight->pOn); + if (p->pWhere == NULL) + pParse->is_aborted = true; pRight->pOn = 0; } @@ -3349,10 +3340,10 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */ } if (j == nOrderBy) { struct Expr *pNew = - sql_op_expr_new(db, TK_INTEGER, NULL); + sql_expr_new_anon(db, TK_INTEGER); if (pNew == NULL) { pParse->is_aborted = true; - return SQL_NOMEM; + return 1; } pNew->flags |= EP_IntValue; pNew->u.iValue = i; @@ -4226,7 +4217,8 @@ flattenSubquery(Parse * pParse, /* Parsing context */ assert(pParent->pHaving == 0); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; - struct Expr *sub_having = sqlExprDup(db, pSub->pHaving, 0); + struct Expr *sub_having = + sqlExprDup(db, pSub->pHaving, 0); if (sub_having != NULL || pParent->pHaving != NULL) { pParent->pHaving = sql_and_expr_new(db, sub_having, @@ -4346,13 +4338,10 @@ pushDownWhereTerms(Parse * pParse, /* Parse context (for malloc() and error repo while (pSubq) { pNew = sqlExprDup(pParse->db, pWhere, 0); pNew = substExpr(pParse, pNew, iCursor, pSubq->pEList); - if (pSubq->pWhere != NULL || pNew != NULL) { - pSubq->pWhere = - sql_and_expr_new(pParse->db, + pSubq->pWhere = sql_and_expr_new(pParse->db, pSubq->pWhere, pNew); - if (pSubq->pWhere == NULL) - pParse->is_aborted = true; - } + if (pSubq->pWhere == NULL) + pParse->is_aborted = true; pSubq = pSubq->pPrior; } } @@ -4533,7 +4522,7 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; - struct Expr *expr = sql_op_expr_new(db, TK_ASTERISK, NULL); + struct Expr *expr = sql_expr_new_anon(db, TK_ASTERISK); if (expr == NULL) pParse->is_aborted = true; p->pEList = sql_expr_list_append(pParse->db, NULL, expr); @@ -5020,7 +5009,7 @@ selectExpander(Walker * pWalker, Select * p) continue; } } - pRight = sql_op_expr_new(db, + pRight = sql_expr_new_named(db, TK_ID, zName); if (pRight == NULL) pParse->is_aborted = true; @@ -5029,7 +5018,7 @@ selectExpander(Walker * pWalker, Select * p) if (longNames || pTabList->nSrc > 1) { Expr *pLeft; - pLeft = sql_op_expr_new( + pLeft = sql_expr_new_named( db, TK_ID, zTabName); diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 5a7a67f2d..7182f58fd 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3225,48 +3225,54 @@ int sqlNoTempsInRange(Parse *, int, int); #endif /** - * This routine is the core allocator for Expr nodes. - * Construct a new expression node and return a pointer to it. - * Memory for this node and for the token argument is a single - * allocation obtained from sqlDbMalloc(). The calling function - * is responsible for making sure the node eventually gets freed. - * - * If dequote is true, then the token (if it exists) is dequoted. - * If dequote is false, no dequoting is performed. The dequote - * parameter is ignored if token is NULL or if the token does - * not appear to be quoted. If the quotes were of the form "..." - * (double-quotes) then the EP_DblQuoted flag is set on the - * expression node. + * Construct a new expression. Memory for this node and for the + * token argument is a single allocation obtained from + * sqlDbMalloc(). The calling function is responsible for making + * sure the node eventually gets freed. * * Special case: If op==TK_INTEGER and token points to a string * that can be translated into a 32-bit integer, then the token is * not stored in u.zToken. Instead, the integer values is written * into u.iValue and the EP_IntValue flag is set. No extra storage - * is allocated to hold the integer text and the dequote flag is - * ignored. + * is allocated to hold the integer text. * * @param db The database connection. * @param op Expression opcode (TK_*). * @param token Source token. Might be NULL. - * @param dequote True to dequote string. * @retval Not NULL New expression object on success. * @retval NULL Otherwise. The diag message is set. */ struct Expr * -sql_expr_new(struct sql *db, int op, const Token *token, bool dequote); +sql_expr_new(struct sql *db, int op, const struct Token *token); /** - * Allocate a new expression node from a zero-terminated token - * that has already been dequoted. - * - * @param db The database connection. - * @param op Expression opcode. - * @param name The object name string. - * @retval Not NULL Expression pointer on success. - * @retval NULL Otherwise. The diag message is set. + * The same as @sa sql_expr_new, but normalizes name, stored in + * @a token. Quotes are removed if they are presented. */ struct Expr * -sql_op_expr_new(struct sql *db, int op, const char *name); +sql_expr_new_dequoted(struct sql *db, int op, const struct Token *token); + +/** + * The same as @a sql_expr_new, but takes const char instead of + * Token. Just sugar to do not touch tokens in many places. + */ +static inline struct Expr * +sql_expr_new_named(struct sql *db, int op, const char *name) +{ + struct Token name_token; + sqlTokenInit(&name_token, (char *)name); + return sql_expr_new(db, op, &name_token); +} + +/** + * The same as @a sql_expr_new, but a result expression has no + * name. + */ +static inline struct Expr * +sql_expr_new_anon(struct sql *db, int op) +{ + return sql_expr_new_named(db, op, NULL); +} void sqlExprAttachSubtrees(sql *, Expr *, Expr *, Expr *); Expr *sqlPExpr(Parse *, int, Expr *, Expr *); @@ -3284,7 +3290,8 @@ void sqlPExprAddSelect(Parse *, Expr *, Select *); * @param left_expr The left-branch expresion to join. * @param right_expr The right-branch expression to join. * @retval Not NULL New expression root node pointer on success. - * @retval NULL Otherwise. The diag message is set. + * @retval NULL Error. A diag message is set. + * @retval NULL Not an error. Both arguments were NULL. */ struct Expr * sql_and_expr_new(struct sql *db, struct Expr *left_expr, @@ -4810,18 +4817,18 @@ void sqlStrAccumReset(StrAccum *); void sqlSelectDestInit(SelectDest *, int, int, int); /* - * Allocate and return a pointer to an expression to load the - * column from datasource src_idx in SrcList src_list. + * Create an expression to load @a column from datasource + * @a src_idx in @a src_list. * * @param db The database connection. * @param src_list The source list described with FROM clause. * @param src_idx The resource index to use in src_list. * @param column The column index. - * @retval Not NULL Expression pointer on success. - * @retval NULL Otherwise. The diag message is set. + * @retval Not NULL Success. An expression to load @a column. + * @retval NULL Error. A diag message is set. */ struct Expr * -sql_column_expr_new(struct sql *db, struct SrcList *src_list, int src_idx, +sql_expr_new_column(struct sql *db, struct SrcList *src_list, int src_idx, int column); int sqlExprCheckIN(Parse *, Expr *); diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 036540d61..a453fe979 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -1372,13 +1372,10 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W continue; testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); pExpr = sqlExprDup(db, pExpr, 0); - if (pAndExpr != NULL || pExpr != NULL) { - pAndExpr = - sql_and_expr_new(db, pAndExpr, - pExpr); - if (pAndExpr == NULL) - pParse->is_aborted = true; - } + pAndExpr = sql_and_expr_new(db, pAndExpr, + pExpr); + if (pAndExpr == NULL) + pParse->is_aborted = true; } if (pAndExpr) { pAndExpr = diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index a9dec8e99..d22a4e0f4 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -307,7 +307,7 @@ like_optimization_is_valid(Parse *pParse, Expr *pExpr, Expr **ppPrefix, Expr *pPrefix; *pisComplete = c == MATCH_ALL_WILDCARD && z[cnt + 1] == 0; - pPrefix = sql_op_expr_new(db, TK_STRING, z); + pPrefix = sql_expr_new_named(db, TK_STRING, z); if (pPrefix == NULL) pParse->is_aborted = true; else @@ -1308,7 +1308,7 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; - struct Expr *expr = sql_expr_new(db, TK_NULL, NULL, false); + struct Expr *expr = sql_expr_new_anon(db, TK_NULL); if (expr == NULL) pParse->is_aborted = true; pNewExpr = sqlPExpr(pParse, TK_GT, sqlExprDup(db, pLeft, 0), @@ -1505,7 +1505,7 @@ sqlWhereTabFuncArgs(Parse * pParse, /* Parsing context */ space_def->name, j); return; } - pColRef = sql_expr_new(pParse->db, TK_COLUMN, NULL, false); + pColRef = sql_expr_new_anon(pParse->db, TK_COLUMN); if (pColRef == NULL) { pParse->is_aborted = true; return; ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (5 preceding siblings ...) 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 6/7] sql: refactor sql_expr_create " Kirill Shcherbatov @ 2019-02-27 11:13 ` Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-18 19:33 ` [tarantool-patches] Re: [PATCH v2 0/7] " Vladislav Shpilevoy 2019-03-27 14:06 ` Kirill Yukhin 8 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-02-27 11:13 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy; +Cc: Kirill Shcherbatov Introduced a new sql_normalize_name routine performing SQL name conversion to case-normal form via unicode character folding. For example, ß is converted to SS. The result is similar to SQL UPPER function. Closes #3931 --- src/box/lua/lua_sql.c | 11 +++-- src/box/sql/build.c | 36 +++++++++----- src/box/sql/expr.c | 71 ++++++++++++++++++--------- src/box/sql/parse.y | 26 ++++++++-- src/box/sql/select.c | 20 ++++++-- src/box/sql/sqlInt.h | 26 +++++++++- src/box/sql/trigger.c | 18 +++++-- src/box/sql/util.c | 42 ++++++++++------ test/sql-tap/identifier_case.test.lua | 12 +++-- 9 files changed, 188 insertions(+), 74 deletions(-) diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index f5a7b7819..c27ca818e 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -176,13 +176,16 @@ lbox_sql_create_function(struct lua_State *L) } size_t name_len; const char *name = lua_tolstring(L, 1, &name_len); + int normalized_name_len = sql_normalize_name(NULL, 0, name, name_len); + if (normalized_name_len < 0) + return luaT_error(L); char *normalized_name = (char *) region_alloc(&fiber()->gc, - name_len + 1); + normalized_name_len + 1); if (normalized_name == NULL) return luaL_error(L, "out of memory"); - memcpy(normalized_name, name, name_len); - normalized_name[name_len] = '\0'; - sqlNormalizeName(normalized_name); + if (sql_normalize_name(normalized_name, normalized_name_len + 1, name, + name_len) < 0) + return luaT_error(L); struct lua_sql_func_info *func_info = (struct lua_sql_func_info *) malloc(sizeof(*func_info)); if (func_info == NULL) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 24f20836b..d167a5714 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -236,13 +236,20 @@ char * sql_name_from_token(struct sql *db, struct Token *name_token) { assert(name_token != NULL && name_token->z != NULL); - char *name = sqlDbStrNDup(db, (char *)name_token->z, name_token->n); + int name_len = + sql_normalize_name(NULL, 0, name_token->z, name_token->n); + if (name_len < 0) + return NULL; + char *name = sqlDbMallocRawNN(db, name_len + 1); if (name == NULL) { - diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", - "name"); + diag_set(OutOfMemory, name_len + 1, "sqlDbMallocRawNN", "name"); + return NULL; + } + if (sql_normalize_name(name, name_len + 1, name_token->z, + name_token->n) < 0) { + sqlDbFree(db, name); return NULL; } - sqlNormalizeName(name); return name; } @@ -441,17 +448,16 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) if (sql_field_retrieve(pParse, def, def->field_count) == NULL) return; struct region *region = &pParse->region; - z = region_alloc(region, pName->n + 1); + int name_len = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_len < 0) + goto tarantool_error; + z = region_alloc(region, name_len + 1); if (z == NULL) { - diag_set(OutOfMemory, pName->n + 1, - "region_alloc", "z"); - pParse->rc = SQL_TARANTOOL_ERROR; - pParse->nErr++; - return; + diag_set(OutOfMemory, name_len + 1, "region_alloc", "z"); + goto tarantool_error; } - memcpy(z, pName->z, pName->n); - z[pName->n] = 0; - sqlNormalizeName(z); + if (sql_normalize_name(z, name_len + 1, pName->z, pName->n) < 0) + goto tarantool_error; for (uint32_t i = 0; i < def->field_count; i++) { if (strcmp(z, def->fields[i].name) == 0) { diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE, z); @@ -472,6 +478,10 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) column_def->type = type_def->type; def->field_count++; pParse->constraintName.n = 0; + return; +tarantool_error: + pParse->rc = SQL_TARANTOOL_ERROR; + pParse->nErr++; } void diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index dd5e2c28d..c03873c02 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -863,7 +863,16 @@ sql_expr_create(struct sql *db, int op, const Token *token, bool dequote) if (token != NULL) { if (op != TK_INTEGER || token->z == NULL || sqlGetInt32(token->z, &val) == 0) { - extra_sz = token->n + 1; + if (op == TK_ID || op == TK_COLLATE || + op == TK_FUNCTION) { + extra_sz = sql_normalize_name(NULL, 0, token->z, + token->n); + if (extra_sz < 0) + return NULL; + } else { + extra_sz = token->n; + } + extra_sz += 1; assert(val >= 0); } } @@ -889,15 +898,20 @@ sql_expr_create(struct sql *db, int op, const Token *token, bool dequote) } else { expr->u.zToken = (char *)&expr[1]; assert(token->z != NULL || token->n == 0); - memcpy(expr->u.zToken, token->z, token->n); - expr->u.zToken[token->n] = '\0'; - if (dequote) { - if (expr->u.zToken[0] == '"') - expr->flags |= EP_DblQuoted; - if (expr->op == TK_ID || expr->op == TK_COLLATE || - expr->op == TK_FUNCTION) - sqlNormalizeName(expr->u.zToken); - else + if (dequote && expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (dequote && + (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION)) { + if (sql_normalize_name(expr->u.zToken, extra_sz, + token->z, token->n) < 0) { + sqlDbFree(db, expr); + return NULL; + } + } else { + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) sqlDequote(expr->u.zToken); } } @@ -1778,18 +1792,31 @@ sqlExprListSetName(Parse * pParse, /* Parsing context */ ) { assert(pList != 0 || pParse->db->mallocFailed != 0); - if (pList) { - struct ExprList_item *pItem; - assert(pList->nExpr > 0); - pItem = &pList->a[pList->nExpr - 1]; - assert(pItem->zName == 0); - pItem->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); - if (dequote) - sqlNormalizeName(pItem->zName); - /* n = 0 in case of select * */ - if (pName->n != 0) - sqlCheckIdentifierName(pParse, pItem->zName); - } + if (pList == NULL || pName->n == 0) + return; + assert(pList->nExpr > 0); + struct ExprList_item *item = &pList->a[pList->nExpr - 1]; + assert(item->zName == NULL); + if (dequote) { + int name_len = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_len < 0) + goto tarantool_error; + item->zName = sqlDbMallocRawNN(pParse->db, name_len + 1); + if (item->zName == NULL) + return; + if (sql_normalize_name(item->zName, name_len + 1, pName->z, + pName->n) < 0) + goto tarantool_error; + } else { + item->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); + if (item->zName == NULL) + return; + } + sqlCheckIdentifierName(pParse, item->zName); + return; +tarantool_error: + pParse->rc = SQL_TARANTOOL_ERROR; + pParse->nErr++; } /* diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index d25995ec0..3204833e9 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -836,7 +836,16 @@ idlist(A) ::= nm(Y). { ** that created the expression. */ static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){ - Expr *p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); + int name_len = 0; + struct Expr *p = NULL; + if (op != TK_VARIABLE) { + name_len = sql_normalize_name(NULL, 0, t.z, t.n); + if (name_len < 0) + goto tarantool_error; + } else { + name_len = t.n; + } + p = sqlDbMallocRawNN(pParse->db, sizeof(Expr) + name_len + 1); if( p ){ memset(p, 0, sizeof(Expr)); switch (op) { @@ -869,10 +878,12 @@ idlist(A) ::= nm(Y). { p->flags = EP_Leaf; p->iAgg = -1; p->u.zToken = (char*)&p[1]; - memcpy(p->u.zToken, t.z, t.n); - p->u.zToken[t.n] = 0; - if (op != TK_VARIABLE){ - sqlNormalizeName(p->u.zToken); + if (op != TK_VARIABLE) { + if (sql_normalize_name(p->u.zToken, name_len + 1, t.z, t.n) < 0) + goto tarantool_error; + } else { + memcpy(p->u.zToken, t.z, t.n); + p->u.zToken[t.n] = 0; } #if SQL_MAX_EXPR_DEPTH>0 p->nHeight = 1; @@ -881,6 +892,11 @@ idlist(A) ::= nm(Y). { pOut->pExpr = p; pOut->zStart = t.z; pOut->zEnd = &t.z[t.n]; + return; +tarantool_error: + sqlDbFree(pParse->db, p); + pParse->rc = SQL_TARANTOOL_ERROR; + pParse->nErr++; } } diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 2ae27e6c4..7b2a38101 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -4195,10 +4195,18 @@ flattenSubquery(Parse * pParse, /* Parsing context */ pList = pParent->pEList; for (i = 0; i < pList->nExpr; i++) { if (pList->a[i].zName == 0) { - char *zName = - sqlDbStrDup(db, pList->a[i].zSpan); - sqlNormalizeName(zName); - pList->a[i].zName = zName; + char *str = pList->a[i].zSpan; + int len = strlen(str); + int name_len = + sql_normalize_name(NULL, 0, str, len); + if (name_len < 0) + goto tarantool_error; + char *name = sqlDbMallocRawNN(db, name_len + 1); + if (name != NULL && + sql_normalize_name(name, name_len + 1, str, + len) < 0) + goto tarantool_error; + pList->a[i].zName = name; } } if (pSub->pOrderBy) { @@ -4276,6 +4284,10 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } #endif + return 1; +tarantool_error: + pParse->rc = SQL_TARANTOOL_ERROR; + pParse->nErr++; return 1; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 82aad0077..026a409c4 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3218,7 +3218,31 @@ void sqlTreeViewWith(TreeView *, const With *); void sqlSetString(char **, sql *, const char *); void sqlErrorMsg(Parse *, const char *, ...); void sqlDequote(char *); -void sqlNormalizeName(char *z); + +/** + * Perform SQL name normalization: cast name to the upper-case + * (via Unicode Character Folding). Casing is locale-dependent + * and context-sensitive. The result may be longer or shorter + * than the original. The source string and the destination buffer + * must not overlap. + * For example, ß is converted to SS. + * The result is similar to SQL UPPER function. + * @param dst A buffer for the result string. The result will be + * NUL-terminated if the buffer is large enough. The + * contents is undefined in case of failure. + * @param dst_size The size of the buffer (number of bytes). + * If it is 0, then dest may be NULL and the + * function will only return the length of the + * result without writing any of the result + * string. + * @param src The original string. + * @param src_len The length of the original string. + * @retval The length of the result string, on success. + * @retval < 0 otherwise. + */ +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len); + void sqlTokenInit(Token *, char *); int sqlKeywordCode(const unsigned char *, int); int sqlRunParser(Parse *, const char *, char **); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index af62a5eff..d43c7bad4 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -278,15 +278,23 @@ sql_trigger_select_step(struct sql *db, struct Select *select) static struct TriggerStep * sql_trigger_step_allocate(struct sql *db, u8 op, struct Token *target_name) { - int size = sizeof(TriggerStep) + target_name->n + 1; - struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); + struct TriggerStep *trigger_step = NULL; + int name_len = + sql_normalize_name(NULL, 0, target_name->z, target_name->n); + if (name_len < 0) + return NULL; + trigger_step = sqlDbMallocZero(db, sizeof(TriggerStep) + name_len + 1); if (trigger_step == NULL) { - diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + diag_set(OutOfMemory, name_len + 1, "sqlDbMallocZero", + "trigger_step"); return NULL; } char *z = (char *)&trigger_step[1]; - memcpy(z, target_name->z, target_name->n); - sqlNormalizeName(z); + if (sql_normalize_name(z, name_len + 1, target_name->z, + target_name->n) < 0) { + sqlDbFree(db, trigger_step); + return NULL; + } trigger_step->zTarget = z; trigger_step->op = op; return trigger_step; diff --git a/src/box/sql/util.c b/src/box/sql/util.c index c89e2e8ab..924149d62 100644 --- a/src/box/sql/util.c +++ b/src/box/sql/util.c @@ -41,6 +41,7 @@ #if HAVE_ISNAN || SQL_HAVE_ISNAN #include <math.h> #endif +#include <unicode/ucasemap.h> /* * Routine needed to support the testcase() macro. @@ -292,23 +293,34 @@ sqlDequote(char *z) z[j] = 0; } - -void -sqlNormalizeName(char *z) +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len) { - char quote; - int i=0; - if (z == 0) - return; - quote = z[0]; - if (sqlIsquote(quote)){ - sqlDequote(z); - return; - } - while(z[i]!=0){ - z[i] = (char)sqlToupper(z[i]); - i++; + assert(src != NULL); + if (sqlIsquote(src[0])){ + if (dst_size == 0) + return src_len; + memcpy(dst, src, src_len); + dst[src_len] = '\0'; + sqlDequote(dst); + return src_len; } + UErrorCode status = U_ZERO_ERROR; + UCaseMap *case_map = ucasemap_open(NULL, 0, &status); + if (case_map == NULL) + goto error; + int len = ucasemap_utf8ToUpper(case_map, dst, dst_size, src, src_len, + &status); + ucasemap_close(case_map); + if (!U_SUCCESS(status) && + !(dst_size == 0 && status == U_BUFFER_OVERFLOW_ERROR)) + goto error; + return len; +error: + diag_set(CollationError, + "string conversion to the uppercase failed: %s", + u_errorName(status)); + return -1; } /* diff --git a/test/sql-tap/identifier_case.test.lua b/test/sql-tap/identifier_case.test.lua index 923d5e66a..aaa4cc85a 100755 --- a/test/sql-tap/identifier_case.test.lua +++ b/test/sql-tap/identifier_case.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool test = require("sqltester") -test:plan(71) +test:plan(73) local test_prefix = "identifier_case-" @@ -13,8 +13,10 @@ local data = { { 6, [[ "Table1" ]], {0} }, -- non ASCII characters case is not supported { 7, [[ русский ]], {0} }, - { 8, [[ Русский ]], {0} }, - { 9, [[ "русский" ]], {"/already exists/"} }, + { 8, [[ "русский" ]], {0} }, + { 9, [[ Großschreibweise ]], {0} }, + { 10, [[ Русский ]], {"/already exists/"} }, + { 11, [[ Grossschreibweise ]], {"/already exists/"} }, } for _, row in ipairs(data) do @@ -35,7 +37,7 @@ data = { { 5, [[ "table1" ]], {5}}, { 6, [[ "Table1" ]], {6}}, { 7, [[ русский ]], {7}}, - { 8, [[ Русский ]], {8}}, + { 8, [[ "русский" ]], {8}}, } for _, row in ipairs(data) do @@ -66,7 +68,7 @@ test:do_test( function () return test:drop_all_tables() end, - 3) + 4) data = { { 1, [[ columnn ]], {0} }, -- 2.20.1 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 7/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov @ 2019-03-07 17:34 ` Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-07 17:34 UTC (permalink / raw) To: Kirill Shcherbatov, tarantool-patches On 27/02/2019 14:13, Kirill Shcherbatov wrote: > Introduced a new sql_normalize_name routine performing SQL name > conversion to case-normal form via unicode character folding. > For example, ß is converted to SS. The result is similar to SQL > UPPER function. > > Closes #3931 First of all, I do not see a test on failed normalization. Please, add. See 2 comments below. > --- > src/box/lua/lua_sql.c | 11 +++-- > src/box/sql/build.c | 36 +++++++++----- > src/box/sql/expr.c | 71 ++++++++++++++++++--------- > src/box/sql/parse.y | 26 ++++++++-- > src/box/sql/select.c | 20 ++++++-- > src/box/sql/sqlInt.h | 26 +++++++++- > src/box/sql/trigger.c | 18 +++++-- > src/box/sql/util.c | 42 ++++++++++------ > test/sql-tap/identifier_case.test.lua | 12 +++-- > 9 files changed, 188 insertions(+), 74 deletions(-) > > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index 24f20836b..d167a5714 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -472,6 +478,10 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) > column_def->type = type_def->type; > def->field_count++; > pParse->constraintName.n = 0; > + return; > +tarantool_error: > + pParse->rc = SQL_TARANTOOL_ERROR; > + pParse->nErr++; 1. You have sql_parser_error, and you used it already in some other places. Fix it, please, in all touched places. > } > > void > diff --git a/test/sql-tap/identifier_case.test.lua b/test/sql-tap/identifier_case.test.lua > index 923d5e66a..aaa4cc85a 100755 > --- a/test/sql-tap/identifier_case.test.lua > +++ b/test/sql-tap/identifier_case.test.lua > @@ -66,7 +68,7 @@ test:do_test( > function () > return test:drop_all_tables() > end, > - 3) > + 4) 2. Why? > > data = { > { 1, [[ columnn ]], {0} }, > -- > 2.20.1 > ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy @ 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-11 15:04 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy > First of all, I do not see a test on failed normalization. > Please, add. I've implemented a new errinj test. > 1. You have sql_parser_error, and you used it already in > some other places. Fix it, please, in all touched places. Ok, done. > 2. Why? Because I've added a new test that have created a new additional table. ====================================================================== Introduced a new sql_normalize_name routine performing SQL name conversion to case-normal form via unicode character folding. For example, ß is converted to SS. The result is similar to SQL UPPER function. Closes #3931 --- src/box/lua/lua_sql.c | 11 +++-- src/box/sql/build.c | 35 +++++++++----- src/box/sql/expr.c | 70 ++++++++++++++++++--------- src/box/sql/parse.y | 25 ++++++++-- src/box/sql/select.c | 19 ++++++-- src/box/sql/sqlInt.h | 27 ++++++++++- src/box/sql/trigger.c | 18 +++++-- src/box/sql/util.c | 46 ++++++++++++------ src/lib/core/errinj.h | 1 + test/box/errinj.result | 20 ++++++++ test/box/errinj.test.lua | 8 +++ test/sql-tap/identifier_case.test.lua | 12 +++-- 12 files changed, 218 insertions(+), 74 deletions(-) diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index f5a7b7819..c27ca818e 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -176,13 +176,16 @@ lbox_sql_create_function(struct lua_State *L) } size_t name_len; const char *name = lua_tolstring(L, 1, &name_len); + int normalized_name_len = sql_normalize_name(NULL, 0, name, name_len); + if (normalized_name_len < 0) + return luaT_error(L); char *normalized_name = (char *) region_alloc(&fiber()->gc, - name_len + 1); + normalized_name_len + 1); if (normalized_name == NULL) return luaL_error(L, "out of memory"); - memcpy(normalized_name, name, name_len); - normalized_name[name_len] = '\0'; - sqlNormalizeName(normalized_name); + if (sql_normalize_name(normalized_name, normalized_name_len + 1, name, + name_len) < 0) + return luaT_error(L); struct lua_sql_func_info *func_info = (struct lua_sql_func_info *) malloc(sizeof(*func_info)); if (func_info == NULL) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 8eac9fdb3..7ca6555dd 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -236,13 +236,20 @@ char * sql_name_from_token(struct sql *db, struct Token *name_token) { assert(name_token != NULL && name_token->z != NULL); - char *name = sqlDbStrNDup(db, (char *)name_token->z, name_token->n); + int name_len = + sql_normalize_name(NULL, 0, name_token->z, name_token->n); + if (name_len < 0) + return NULL; + char *name = sqlDbMallocRawNN(db, name_len + 1); if (name == NULL) { - diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", - "name"); + diag_set(OutOfMemory, name_len + 1, "sqlDbMallocRawNN", "name"); + return NULL; + } + if (sql_normalize_name(name, name_len + 1, name_token->z, + name_token->n) < 0) { + sqlDbFree(db, name); return NULL; } - sqlNormalizeName(name); return name; } @@ -438,17 +445,16 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) if (sql_field_retrieve(pParse, def, def->field_count) == NULL) return; struct region *region = &pParse->region; - z = region_alloc(region, pName->n + 1); + int name_len = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_len < 0) + goto tarantool_error; + z = region_alloc(region, name_len + 1); if (z == NULL) { - diag_set(OutOfMemory, pName->n + 1, - "region_alloc", "z"); - pParse->rc = SQL_TARANTOOL_ERROR; - pParse->nErr++; - return; + diag_set(OutOfMemory, name_len + 1, "region_alloc", "z"); + goto tarantool_error; } - memcpy(z, pName->z, pName->n); - z[pName->n] = 0; - sqlNormalizeName(z); + if (sql_normalize_name(z, name_len + 1, pName->z, pName->n) < 0) + goto tarantool_error; for (uint32_t i = 0; i < def->field_count; i++) { if (strcmp(z, def->fields[i].name) == 0) { diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE, z); @@ -469,6 +475,9 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) column_def->type = type_def->type; def->field_count++; pParse->constraintName.n = 0; + return; +tarantool_error: + sql_parser_error(pParse); } void diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index f5b2926c8..24a051e04 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -863,7 +863,16 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) if (token != NULL) { if (op != TK_INTEGER || token->z == NULL || sqlGetInt32(token->z, &val) == 0) { - extra_sz = token->n + 1; + if (op == TK_ID || op == TK_COLLATE || + op == TK_FUNCTION) { + extra_sz = sql_normalize_name(NULL, 0, token->z, + token->n); + if (extra_sz < 0) + return NULL; + } else { + extra_sz = token->n; + } + extra_sz += 1; assert(val >= 0); } } @@ -889,15 +898,20 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) } else { expr->u.zToken = (char *)&expr[1]; assert(token->z != NULL || token->n == 0); - memcpy(expr->u.zToken, token->z, token->n); - expr->u.zToken[token->n] = '\0'; - if (dequote) { - if (expr->u.zToken[0] == '"') - expr->flags |= EP_DblQuoted; - if (expr->op == TK_ID || expr->op == TK_COLLATE || - expr->op == TK_FUNCTION) - sqlNormalizeName(expr->u.zToken); - else + if (dequote && expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (dequote && + (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION)) { + if (sql_normalize_name(expr->u.zToken, extra_sz, + token->z, token->n) < 0) { + sqlDbFree(db, expr); + return NULL; + } + } else { + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) sqlDequote(expr->u.zToken); } } @@ -1776,18 +1790,30 @@ sqlExprListSetName(Parse * pParse, /* Parsing context */ ) { assert(pList != 0 || pParse->db->mallocFailed != 0); - if (pList) { - struct ExprList_item *pItem; - assert(pList->nExpr > 0); - pItem = &pList->a[pList->nExpr - 1]; - assert(pItem->zName == 0); - pItem->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); - if (dequote) - sqlNormalizeName(pItem->zName); - /* n = 0 in case of select * */ - if (pName->n != 0) - sqlCheckIdentifierName(pParse, pItem->zName); - } + if (pList == NULL || pName->n == 0) + return; + assert(pList->nExpr > 0); + struct ExprList_item *item = &pList->a[pList->nExpr - 1]; + assert(item->zName == NULL); + if (dequote) { + int name_len = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_len < 0) + goto tarantool_error; + item->zName = sqlDbMallocRawNN(pParse->db, name_len + 1); + if (item->zName == NULL) + return; + if (sql_normalize_name(item->zName, name_len + 1, pName->z, + pName->n) < 0) + goto tarantool_error; + } else { + item->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); + if (item->zName == NULL) + return; + } + sqlCheckIdentifierName(pParse, item->zName); + return; +tarantool_error: + sql_parser_error(pParse); } /* diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 1a5882dda..c8488ec10 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -836,7 +836,16 @@ idlist(A) ::= nm(Y). { ** that created the expression. */ static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){ - Expr *p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); + int name_len = 0; + struct Expr *p = NULL; + if (op != TK_VARIABLE) { + name_len = sql_normalize_name(NULL, 0, t.z, t.n); + if (name_len < 0) + goto tarantool_error; + } else { + name_len = t.n; + } + p = sqlDbMallocRawNN(pParse->db, sizeof(Expr) + name_len + 1); if( p ){ memset(p, 0, sizeof(Expr)); switch (op) { @@ -869,10 +878,12 @@ idlist(A) ::= nm(Y). { p->flags = EP_Leaf; p->iAgg = -1; p->u.zToken = (char*)&p[1]; - memcpy(p->u.zToken, t.z, t.n); - p->u.zToken[t.n] = 0; - if (op != TK_VARIABLE){ - sqlNormalizeName(p->u.zToken); + if (op != TK_VARIABLE) { + if (sql_normalize_name(p->u.zToken, name_len + 1, t.z, t.n) < 0) + goto tarantool_error; + } else { + memcpy(p->u.zToken, t.z, t.n); + p->u.zToken[t.n] = 0; } #if SQL_MAX_EXPR_DEPTH>0 p->nHeight = 1; @@ -881,6 +892,10 @@ idlist(A) ::= nm(Y). { pOut->pExpr = p; pOut->zStart = t.z; pOut->zEnd = &t.z[t.n]; + return; +tarantool_error: + sqlDbFree(pParse->db, p); + sql_parser_error(pParse); } } diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 5434a2ab0..aa83593b1 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -4193,10 +4193,18 @@ flattenSubquery(Parse * pParse, /* Parsing context */ pList = pParent->pEList; for (i = 0; i < pList->nExpr; i++) { if (pList->a[i].zName == 0) { - char *zName = - sqlDbStrDup(db, pList->a[i].zSpan); - sqlNormalizeName(zName); - pList->a[i].zName = zName; + char *str = pList->a[i].zSpan; + int len = strlen(str); + int name_len = + sql_normalize_name(NULL, 0, str, len); + if (name_len < 0) + goto tarantool_error; + char *name = sqlDbMallocRawNN(db, name_len + 1); + if (name != NULL && + sql_normalize_name(name, name_len + 1, str, + len) < 0) + goto tarantool_error; + pList->a[i].zName = name; } } if (pSub->pOrderBy) { @@ -4273,6 +4281,9 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } #endif + return 1; +tarantool_error: + sql_parser_error(pParse); return 1; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 4401ea8b7..e3fa4024f 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3182,7 +3182,32 @@ void sqlTreeViewWith(TreeView *, const With *); void sqlSetString(char **, sql *, const char *); void sqlErrorMsg(Parse *, const char *, ...); void sqlDequote(char *); -void sqlNormalizeName(char *z); + +/** + * Perform SQL name normalization: cast name to the upper-case + * (via Unicode Character Folding). Casing is locale-dependent + * and context-sensitive. The result may be longer or shorter + * than the original. The source string and the destination buffer + * must not overlap. + * For example, ß is converted to SS. + * The result is similar to SQL UPPER function. + * + * @param dst A buffer for the result string. The result will be + * NUL-terminated if the buffer is large enough. The + * contents is undefined in case of failure. + * @param dst_size The size of the buffer (number of bytes). + * If it is 0, then dest may be NULL and the + * function will only return the length of the + * result without writing any of the result + * string. + * @param src The original string. + * @param src_len The length of the original string. + * @retval The length of the result string, on success. + * @retval < 0 Otherwise. The diag message is set. + */ +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len); + void sqlTokenInit(Token *, char *); int sqlKeywordCode(const unsigned char *, int); int sqlRunParser(Parse *, const char *, char **); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 96c3a4a2f..5fbf96a10 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -280,15 +280,23 @@ sql_trigger_select_step(struct sql *db, struct Select *select) static struct TriggerStep * sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) { - int size = sizeof(TriggerStep) + target_name->n + 1; - struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); + struct TriggerStep *trigger_step = NULL; + int name_len = + sql_normalize_name(NULL, 0, target_name->z, target_name->n); + if (name_len < 0) + return NULL; + trigger_step = sqlDbMallocZero(db, sizeof(TriggerStep) + name_len + 1); if (trigger_step == NULL) { - diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + diag_set(OutOfMemory, name_len + 1, "sqlDbMallocZero", + "trigger_step"); return NULL; } char *z = (char *)&trigger_step[1]; - memcpy(z, target_name->z, target_name->n); - sqlNormalizeName(z); + if (sql_normalize_name(z, name_len + 1, target_name->z, + target_name->n) < 0) { + sqlDbFree(db, trigger_step); + return NULL; + } trigger_step->zTarget = z; trigger_step->op = op; return trigger_step; diff --git a/src/box/sql/util.c b/src/box/sql/util.c index c89e2e8ab..60133758a 100644 --- a/src/box/sql/util.c +++ b/src/box/sql/util.c @@ -41,6 +41,8 @@ #if HAVE_ISNAN || SQL_HAVE_ISNAN #include <math.h> #endif +#include <unicode/ucasemap.h> +#include "errinj.h" /* * Routine needed to support the testcase() macro. @@ -292,23 +294,37 @@ sqlDequote(char *z) z[j] = 0; } - -void -sqlNormalizeName(char *z) +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len) { - char quote; - int i=0; - if (z == 0) - return; - quote = z[0]; - if (sqlIsquote(quote)){ - sqlDequote(z); - return; - } - while(z[i]!=0){ - z[i] = (char)sqlToupper(z[i]); - i++; + assert(src != NULL); + if (sqlIsquote(src[0])){ + if (dst_size == 0) + return src_len; + memcpy(dst, src, src_len); + dst[src_len] = '\0'; + sqlDequote(dst); + return src_len; } + UErrorCode status = U_ZERO_ERROR; + ERROR_INJECT(ERRINJ_SQL_NAME_NORMALIZATION, { + status = U_MEMORY_ALLOCATION_ERROR; + goto error; + }); + UCaseMap *case_map = ucasemap_open(NULL, 0, &status); + if (case_map == NULL) + goto error; + int len = ucasemap_utf8ToUpper(case_map, dst, dst_size, src, src_len, + &status); + ucasemap_close(case_map); + assert(U_SUCCESS(status) || + (dst_size == 0 && status == U_BUFFER_OVERFLOW_ERROR)); + return len; +error: + diag_set(CollationError, + "string conversion to the uppercase failed: %s", + u_errorName(status)); + return -1; } /* diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h index 41783cc74..c823d3597 100644 --- a/src/lib/core/errinj.h +++ b/src/lib/core/errinj.h @@ -125,6 +125,7 @@ struct errinj { _(ERRINJ_VY_COMPACTION_DELAY, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_TUPLE_FORMAT_COUNT, ERRINJ_INT, {.iparam = -1}) \ _(ERRINJ_MEMTX_DELAY_GC, ERRINJ_BOOL, {.bparam = false}) \ + _(ERRINJ_SQL_NAME_NORMALIZATION, ERRINJ_BOOL, {.bparam = false}) \ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index 8e76b21b3..5bdb71d7b 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -22,6 +22,8 @@ errinj.info() state: false ERRINJ_SNAP_WRITE_ROW_TIMEOUT: state: 0 + ERRINJ_SQL_NAME_NORMALIZATION: + state: false ERRINJ_VY_SCHED_TIMEOUT: state: 0 ERRINJ_WAL_WRITE_PARTIAL: @@ -1672,3 +1674,21 @@ fio = require('fio') box.space.test:drop() --- ... +-- +-- gh-3931: Store regular identifiers in case-normal form +-- +errinj = box.error.injection +--- +... +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) +--- +- ok +... +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") +--- +- error: 'string conversion to the uppercase failed: U_MEMORY_ALLOCATION_ERROR' +... +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) +--- +- ok +... diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua index c876f9afb..9e8095295 100644 --- a/test/box/errinj.test.lua +++ b/test/box/errinj.test.lua @@ -587,3 +587,11 @@ fio = require('fio') #fio.glob(fio.pathjoin(box.cfg.vinyl_dir, box.space.test.id, 0, '*.index.inprogress')) == 0 box.space.test:drop() + +-- +-- gh-3931: Store regular identifiers in case-normal form +-- +errinj = box.error.injection +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) diff --git a/test/sql-tap/identifier_case.test.lua b/test/sql-tap/identifier_case.test.lua index 923d5e66a..aaa4cc85a 100755 --- a/test/sql-tap/identifier_case.test.lua +++ b/test/sql-tap/identifier_case.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool test = require("sqltester") -test:plan(71) +test:plan(73) local test_prefix = "identifier_case-" @@ -13,8 +13,10 @@ local data = { { 6, [[ "Table1" ]], {0} }, -- non ASCII characters case is not supported { 7, [[ русский ]], {0} }, - { 8, [[ Русский ]], {0} }, - { 9, [[ "русский" ]], {"/already exists/"} }, + { 8, [[ "русский" ]], {0} }, + { 9, [[ Großschreibweise ]], {0} }, + { 10, [[ Русский ]], {"/already exists/"} }, + { 11, [[ Grossschreibweise ]], {"/already exists/"} }, } for _, row in ipairs(data) do @@ -35,7 +37,7 @@ data = { { 5, [[ "table1" ]], {5}}, { 6, [[ "Table1" ]], {6}}, { 7, [[ русский ]], {7}}, - { 8, [[ Русский ]], {8}}, + { 8, [[ "русский" ]], {8}}, } for _, row in ipairs(data) do @@ -66,7 +68,7 @@ test:do_test( function () return test:drop_all_tables() end, - 3) + 4) data = { { 1, [[ columnn ]], {0} }, -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-03-11 15:04 ` Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 0 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes! See 3 comments below. On 11/03/2019 18:04, Kirill Shcherbatov wrote: >> First of all, I do not see a test on failed normalization. >> Please, add. > I've implemented a new errinj test. > >> 1. You have sql_parser_error, and you used it already in >> some other places. Fix it, please, in all touched places. > > Ok, done. > >> 2. Why? > > Because I've added a new test that have created a new additional table. 1. As I asked multiple times - please, do not put my comments out of the context. I see my question 'Why?', but I do not see what does it refer to. > diff --git a/src/box/sql/util.c b/src/box/sql/util.c > index c89e2e8ab..60133758a 100644 > --- a/src/box/sql/util.c > +++ b/src/box/sql/util.c > @@ -292,23 +294,37 @@ sqlDequote(char *z) > z[j] = 0; > } > > - > -void > -sqlNormalizeName(char *z) > +int > +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len) > { > - char quote; > - int i=0; > - if (z == 0) > - return; > - quote = z[0]; > - if (sqlIsquote(quote)){ > - sqlDequote(z); > - return; > - } > - while(z[i]!=0){ > - z[i] = (char)sqlToupper(z[i]); > - i++; > + assert(src != NULL); > + if (sqlIsquote(src[0])){ > + if (dst_size == 0) > + return src_len; > + memcpy(dst, src, src_len); > + dst[src_len] = '\0'; > + sqlDequote(dst); > + return src_len; 2. In the comment you said that sql_normalize_name returns a length of result string, but here it is false. sqlDequote can trim some symbols and the result length is less. The code works only thanks to the fact, that the result of sql_normalize_name is never used as a length, but rather as a buffer size. Please, either fix the return value, returning a real string length, or change the comment, saying what that function returns + its usage places, where you store its result into 'name_len' variables (they should be renamed to bsize or something). > diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua > index c876f9afb..9e8095295 100644 > --- a/test/box/errinj.test.lua > +++ b/test/box/errinj.test.lua > @@ -587,3 +587,11 @@ fio = require('fio') > #fio.glob(fio.pathjoin(box.cfg.vinyl_dir, box.space.test.id, 0, '*.index.inprogress')) == 0 > > box.space.test:drop() > + > +-- > +-- gh-3931: Store regular identifiers in case-normal form > +-- > +errinj = box.error.injection > +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) > +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") > +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) 3. We have sql/errinj.test.lua for that. Below you can find a couple of general comments, which I discovered on the top commit, but probably some of them were introduced in the previous ones: 1) delete.c:72: 'where' leaks. It was duplicated on line 68, and you do not free it before return; 2) fk_constraint.c:641: why not to return? Here OOM has happened, obviously, and there is nothing to cleanup; ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-03-18 19:33 ` Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy >>> 1. You have sql_parser_error, and you used it already in >>> some other places. Fix it, please, in all touched places. >> >> Ok, done. >> >>> 2. Why? >> >> Because I've added a new test that have created a new additional table. > > 1. As I asked multiple times - please, do not put my comments > out of the context. I see my question 'Why?', but I do not see > what does it refer to. - { 8, [[ РуÑÑкий ]], {0} }, - { 9, [[ "руÑÑкий" ]], {"/already exists/"} }, + { 8, [[ "руÑÑкий" ]], {0} }, + { 9, [[ Großschreibweise ]], {0} }, + { 10, [[ РуÑÑкий ]], {"/already exists/"} }, + { 11, [[ Grossschreibweise ]], {"/already exists/"} }, } for _, row in ipairs(data) do @@ -35,7 +37,7 @@ data = { { 5, [[ "table1" ]], {5}}, { 6, [[ "Table1" ]], {6}}, { 7, [[ руÑÑкий ]], {7}}, - { 8, [[ РуÑÑкий ]], {8}}, + { 8, [[ "руÑÑкий" ]], {8}}, } for _, row in ipairs(data) do @@ -66,7 +68,7 @@ test:do_test( function () return test:drop_all_tables() end, - 3) + 4) >> Because I've added a new test that have created a new additional table. >> diff --git a/src/box/sql/util.c b/src/box/sql/util.c >> index c89e2e8ab..60133758a 100644 >> --- a/src/box/sql/util.c >> +++ b/src/box/sql/util.c >> @@ -292,23 +294,37 @@ sqlDequote(char *z) >> z[j] = 0; >> } >> >> - >> -void >> -sqlNormalizeName(char *z) >> +int >> +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len) >> { >> - char quote; >> - int i=0; >> - if (z == 0) >> - return; >> - quote = z[0]; >> - if (sqlIsquote(quote)){ >> - sqlDequote(z); >> - return; >> - } >> - while(z[i]!=0){ >> - z[i] = (char)sqlToupper(z[i]); >> - i++; >> + assert(src != NULL); >> + if (sqlIsquote(src[0])){ >> + if (dst_size == 0) >> + return src_len; >> + memcpy(dst, src, src_len); >> + dst[src_len] = '\0'; >> + sqlDequote(dst); >> + return src_len; > > 2. In the comment you said that sql_normalize_name returns a length > of result string, but here it is false. sqlDequote can trim some > symbols and the result length is less. The code works only thanks to > the fact, that the result of sql_normalize_name is never used as a > length, but rather as a buffer size. Please, either fix the return value, > returning a real string length, or change the comment, saying what > that function returns + its usage places, where you store its result > into 'name_len' variables (they should be renamed to bsize or something). I've reworked this routine to always return the size of required buffer and updated it's comment. + * @param dst_size The size of the buffer (number of bytes). + * If it is 0, then dest may be NULL and the + * function will only return the corresponding + * dst_size - this size of buffer that may fit + * result. + * @param src The original string. + * @param src_len The length of the original string. + * @retval The count of bytes written(or need to be written) on + * success. + * @retval < 0 Otherwise. The diag message is set. + */ >> +-- >> +-- gh-3931: Store regular identifiers in case-normal form >> +-- >> +errinj = box.error.injection >> +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) >> +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") >> +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) > > 3. We have sql/errinj.test.lua for that. Moved to sql/errinj.test.lua. > Below you can find a couple of general comments, which I discovered on the > top commit, but probably some of them were introduced in the previous > ones: > > 1) delete.c:72: 'where' leaks. It was duplicated on line 68, and you do > not free it before return; struct SrcList *from = sql_src_list_append(db, NULL, NULL); if (from == NULL) { parse->is_aborted = true; return; } where = sqlExprDup(db, where, 0); > 2) fk_constraint.c:641: why not to return? Here OOM has happened, obviously, > and there is nothing to cleanup; Ok, don't mind return. ============================================= Introduced a new sql_normalize_name routine performing SQL name conversion to case-normal form via unicode character folding. For example, ß is converted to SS. The result is similar to SQL UPPER function. Closes #3931 --- src/box/lua/lua_sql.c | 11 +++-- src/box/sql/build.c | 32 +++++++++---- src/box/sql/expr.c | 67 ++++++++++++++++++--------- src/box/sql/parse.y | 25 ++++++++-- src/box/sql/select.c | 19 ++++++-- src/box/sql/sqlInt.h | 28 ++++++++++- src/box/sql/trigger.c | 14 ++++-- src/box/sql/util.c | 46 ++++++++++++------ src/lib/core/errinj.h | 1 + test/box/errinj.result | 2 + test/sql-tap/identifier_case.test.lua | 12 +++-- test/sql/errinj.result | 18 +++++++ test/sql/errinj.test.lua | 8 ++++ 13 files changed, 215 insertions(+), 68 deletions(-) diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index f5a7b7819..2b3a595af 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -176,13 +176,16 @@ lbox_sql_create_function(struct lua_State *L) } size_t name_len; const char *name = lua_tolstring(L, 1, &name_len); + int normalized_name_sz = sql_normalize_name(NULL, 0, name, name_len); + if (normalized_name_sz < 0) + return luaT_error(L); char *normalized_name = (char *) region_alloc(&fiber()->gc, - name_len + 1); + normalized_name_sz + 1); if (normalized_name == NULL) return luaL_error(L, "out of memory"); - memcpy(normalized_name, name, name_len); - normalized_name[name_len] = '\0'; - sqlNormalizeName(normalized_name); + if (sql_normalize_name(normalized_name, normalized_name_sz, name, + name_len) < 0) + return luaT_error(L); struct lua_sql_func_info *func_info = (struct lua_sql_func_info *) malloc(sizeof(*func_info)); if (func_info == NULL) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index f527928bf..a5c62a7ee 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -233,13 +233,20 @@ char * sql_name_from_token(struct sql *db, struct Token *name_token) { assert(name_token != NULL && name_token->z != NULL); - char *name = sqlDbStrNDup(db, name_token->z, name_token->n); + int name_sz = + sql_normalize_name(NULL, 0, name_token->z, name_token->n); + if (name_sz < 0) + return NULL; + char *name = sqlDbMallocRawNN(db, name_sz); if (name == NULL) { - diag_set(OutOfMemory, name_token->n + 1, "sqlDbStrNDup", - "name"); + diag_set(OutOfMemory, name_sz, "sqlDbMallocRawNN", "name"); + return NULL; + } + if (sql_normalize_name(name, name_sz, name_token->z, + name_token->n) < 0) { + sqlDbFree(db, name); return NULL; } - sqlNormalizeName(name); return name; } @@ -434,16 +441,21 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) if (sql_field_retrieve(pParse, def, def->field_count) == NULL) return; struct region *region = &pParse->region; - z = region_alloc(region, pName->n + 1); + int name_sz = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_sz < 0) { + pParse->is_aborted = true; + return; + } + z = region_alloc(region, name_sz); if (z == NULL) { - diag_set(OutOfMemory, pName->n + 1, - "region_alloc", "z"); + diag_set(OutOfMemory, name_sz, "region_alloc", "z"); + pParse->is_aborted = true; + return; + } + if (sql_normalize_name(z, name_sz, pName->z, pName->n) < 0) { pParse->is_aborted = true; return; } - memcpy(z, pName->z, pName->n); - z[pName->n] = 0; - sqlNormalizeName(z); for (uint32_t i = 0; i < def->field_count; i++) { if (strcmp(z, def->fields[i].name) == 0) { diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE, z); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index dad4ce3a6..55ca100c1 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -883,7 +883,15 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) if (token != NULL) { if (op != TK_INTEGER || token->z == NULL || sqlGetInt32(token->z, &val) == 0) { - extra_sz = token->n + 1; + if (op == TK_ID || op == TK_COLLATE || + op == TK_FUNCTION) { + extra_sz = sql_normalize_name(NULL, 0, token->z, + token->n); + if (extra_sz < 0) + return NULL; + } else { + extra_sz = token->n + 1; + } assert(val >= 0); } } @@ -909,15 +917,20 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) } else { expr->u.zToken = (char *)&expr[1]; assert(token->z != NULL || token->n == 0); - memcpy(expr->u.zToken, token->z, token->n); - expr->u.zToken[token->n] = '\0'; - if (dequote) { - if (expr->u.zToken[0] == '"') - expr->flags |= EP_DblQuoted; - if (expr->op == TK_ID || expr->op == TK_COLLATE || - expr->op == TK_FUNCTION) - sqlNormalizeName(expr->u.zToken); - else + if (dequote && expr->u.zToken[0] == '"') + expr->flags |= EP_DblQuoted; + if (dequote && + (expr->op == TK_ID || expr->op == TK_COLLATE || + expr->op == TK_FUNCTION)) { + if (sql_normalize_name(expr->u.zToken, extra_sz, + token->z, token->n) < 0) { + sqlDbFree(db, expr); + return NULL; + } + } else { + memcpy(expr->u.zToken, token->z, token->n); + expr->u.zToken[token->n] = '\0'; + if (dequote) sqlDequote(expr->u.zToken); } } @@ -1816,18 +1829,30 @@ sqlExprListSetName(Parse * pParse, /* Parsing context */ ) { assert(pList != 0 || pParse->db->mallocFailed != 0); - if (pList) { - struct ExprList_item *pItem; - assert(pList->nExpr > 0); - pItem = &pList->a[pList->nExpr - 1]; - assert(pItem->zName == 0); - pItem->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); - if (dequote) - sqlNormalizeName(pItem->zName); - /* n = 0 in case of select * */ - if (pName->n != 0) - sqlCheckIdentifierName(pParse, pItem->zName); + if (pList == NULL || pName->n == 0) + return; + assert(pList->nExpr > 0); + struct ExprList_item *item = &pList->a[pList->nExpr - 1]; + assert(item->zName == NULL); + if (dequote) { + int name_sz = sql_normalize_name(NULL, 0, pName->z, pName->n); + if (name_sz < 0) + goto tarantool_error; + item->zName = sqlDbMallocRawNN(pParse->db, name_sz); + if (item->zName == NULL) + return; + if (sql_normalize_name(item->zName, name_sz, pName->z, + pName->n) < 0) + goto tarantool_error; + } else { + item->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); + if (item->zName == NULL) + return; } + sqlCheckIdentifierName(pParse, item->zName); + return; +tarantool_error: + pParse->is_aborted = true; } /* diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index c48cbd6f9..540d0e077 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -851,7 +851,16 @@ idlist(A) ::= nm(Y). { ** that created the expression. */ static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){ - Expr *p = sqlDbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); + int name_sz = 0; + struct Expr *p = NULL; + if (op != TK_VARIABLE) { + name_sz = sql_normalize_name(NULL, 0, t.z, t.n); + if (name_sz < 0) + goto tarantool_error; + } else { + name_sz = t.n + 1; + } + p = sqlDbMallocRawNN(pParse->db, sizeof(Expr) + name_sz); if( p ){ memset(p, 0, sizeof(Expr)); switch (op) { @@ -884,10 +893,12 @@ idlist(A) ::= nm(Y). { p->flags = EP_Leaf; p->iAgg = -1; p->u.zToken = (char*)&p[1]; - memcpy(p->u.zToken, t.z, t.n); - p->u.zToken[t.n] = 0; - if (op != TK_VARIABLE){ - sqlNormalizeName(p->u.zToken); + if (op != TK_VARIABLE) { + if (sql_normalize_name(p->u.zToken, name_sz, t.z, t.n) < 0) + goto tarantool_error; + } else { + memcpy(p->u.zToken, t.z, t.n); + p->u.zToken[t.n] = 0; } #if SQL_MAX_EXPR_DEPTH>0 p->nHeight = 1; @@ -896,6 +907,10 @@ idlist(A) ::= nm(Y). { pOut->pExpr = p; pOut->zStart = t.z; pOut->zEnd = &t.z[t.n]; + return; +tarantool_error: + sqlDbFree(pParse->db, p); + pParse->is_aborted = true; } } diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 5bba0b5d0..d4a5a9ec3 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -4194,10 +4194,18 @@ flattenSubquery(Parse * pParse, /* Parsing context */ pList = pParent->pEList; for (i = 0; i < pList->nExpr; i++) { if (pList->a[i].zName == 0) { - char *zName = - sqlDbStrDup(db, pList->a[i].zSpan); - sqlNormalizeName(zName); - pList->a[i].zName = zName; + char *str = pList->a[i].zSpan; + int len = strlen(str); + int name_sz = + sql_normalize_name(NULL, 0, str, len); + if (name_sz < 0) + goto tarantool_error; + char *name = sqlDbMallocRawNN(db, name_sz); + if (name != NULL && + sql_normalize_name(name, name_sz, str, + len) < 0) + goto tarantool_error; + pList->a[i].zName = name; } } if (pSub->pOrderBy) { @@ -4274,6 +4282,9 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } #endif + return 1; +tarantool_error: + pParse->is_aborted = true; return 1; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 13d0b16f8..120eedfa6 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3195,7 +3195,33 @@ void sqlTreeViewWith(TreeView *, const With *); void sqlSetString(char **, sql *, const char *); void sqlErrorMsg(Parse *, const char *, ...); void sqlDequote(char *); -void sqlNormalizeName(char *z); + +/** + * Perform SQL name normalization: cast name to the upper-case + * (via Unicode Character Folding). Casing is locale-dependent + * and context-sensitive. The result may be longer or shorter + * than the original. The source string and the destination buffer + * must not overlap. + * For example, ß is converted to SS. + * The result is similar to SQL UPPER function. + * + * @param dst A buffer for the result string. The result will be + * NUL-terminated if the buffer is large enough. The + * contents is undefined in case of failure. + * @param dst_size The size of the buffer (number of bytes). + * If it is 0, then dest may be NULL and the + * function will only return the corresponding + * dst_size - this size of buffer that may fit + * result. + * @param src The original string. + * @param src_len The length of the original string. + * @retval The count of bytes written(or need to be written) on + * success. + * @retval < 0 Otherwise. The diag message is set. + */ +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len); + void sqlTokenInit(Token *, char *); int sqlKeywordCode(const unsigned char *, int); int sqlRunParser(Parse *, const char *); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 544ba2a5a..16d0f7fe6 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -279,15 +279,23 @@ sql_trigger_select_step(struct sql *db, struct Select *select) static struct TriggerStep * sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) { + int name_sz = + sql_normalize_name(NULL, 0, target_name->z, target_name->n); + if (name_sz < 0) + return NULL; int size = sizeof(struct TriggerStep) + target_name->n + 1; struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); if (trigger_step == NULL) { - diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); + diag_set(OutOfMemory, name_sz, "sqlDbMallocZero", + "trigger_step"); return NULL; } char *z = (char *)&trigger_step[1]; - memcpy(z, target_name->z, target_name->n); - sqlNormalizeName(z); + if (sql_normalize_name(z, name_sz, target_name->z, + target_name->n) < 0) { + sqlDbFree(db, trigger_step); + return NULL; + } trigger_step->zTarget = z; trigger_step->op = op; return trigger_step; diff --git a/src/box/sql/util.c b/src/box/sql/util.c index d9bb2afda..ea8283406 100644 --- a/src/box/sql/util.c +++ b/src/box/sql/util.c @@ -41,6 +41,8 @@ #if HAVE_ISNAN || SQL_HAVE_ISNAN #include <math.h> #endif +#include <unicode/ucasemap.h> +#include "errinj.h" /* * Routine needed to support the testcase() macro. @@ -285,23 +287,37 @@ sqlDequote(char *z) z[j] = 0; } - -void -sqlNormalizeName(char *z) +int +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len) { - char quote; - int i=0; - if (z == 0) - return; - quote = z[0]; - if (sqlIsquote(quote)){ - sqlDequote(z); - return; - } - while(z[i]!=0){ - z[i] = (char)sqlToupper(z[i]); - i++; + assert(src != NULL); + if (sqlIsquote(src[0])){ + if (dst_size == 0) + return src_len + 1; + memcpy(dst, src, src_len); + dst[src_len] = '\0'; + sqlDequote(dst); + return src_len + 1; } + UErrorCode status = U_ZERO_ERROR; + ERROR_INJECT(ERRINJ_SQL_NAME_NORMALIZATION, { + status = U_MEMORY_ALLOCATION_ERROR; + goto error; + }); + UCaseMap *case_map = ucasemap_open(NULL, 0, &status); + if (case_map == NULL) + goto error; + int len = ucasemap_utf8ToUpper(case_map, dst, dst_size, src, src_len, + &status); + ucasemap_close(case_map); + assert(U_SUCCESS(status) || + (dst_size == 0 && status == U_BUFFER_OVERFLOW_ERROR)); + return len + 1; +error: + diag_set(CollationError, + "string conversion to the uppercase failed: %s", + u_errorName(status)); + return -1; } /* diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h index 41783cc74..c823d3597 100644 --- a/src/lib/core/errinj.h +++ b/src/lib/core/errinj.h @@ -125,6 +125,7 @@ struct errinj { _(ERRINJ_VY_COMPACTION_DELAY, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_TUPLE_FORMAT_COUNT, ERRINJ_INT, {.iparam = -1}) \ _(ERRINJ_MEMTX_DELAY_GC, ERRINJ_BOOL, {.bparam = false}) \ + _(ERRINJ_SQL_NAME_NORMALIZATION, ERRINJ_BOOL, {.bparam = false}) \ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index 8e76b21b3..b657234e1 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -22,6 +22,8 @@ errinj.info() state: false ERRINJ_SNAP_WRITE_ROW_TIMEOUT: state: 0 + ERRINJ_SQL_NAME_NORMALIZATION: + state: false ERRINJ_VY_SCHED_TIMEOUT: state: 0 ERRINJ_WAL_WRITE_PARTIAL: diff --git a/test/sql-tap/identifier_case.test.lua b/test/sql-tap/identifier_case.test.lua index 74c7ce2fb..9c800dd2c 100755 --- a/test/sql-tap/identifier_case.test.lua +++ b/test/sql-tap/identifier_case.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool test = require("sqltester") -test:plan(71) +test:plan(73) local test_prefix = "identifier_case-" @@ -13,8 +13,10 @@ local data = { { 6, [[ "Table1" ]], {0} }, -- non ASCII characters case is not supported { 7, [[ русский ]], {0} }, - { 8, [[ Русский ]], {0} }, - { 9, [[ "русский" ]], {"/already exists/"} }, + { 8, [[ "русский" ]], {0} }, + { 9, [[ Großschreibweise ]], {0} }, + { 10, [[ Русский ]], {"/already exists/"} }, + { 11, [[ Grossschreibweise ]], {"/already exists/"} }, } for _, row in ipairs(data) do @@ -35,7 +37,7 @@ data = { { 5, [[ "table1" ]], {5}}, { 6, [[ "Table1" ]], {6}}, { 7, [[ русский ]], {7}}, - { 8, [[ Русский ]], {8}}, + { 8, [[ "русский" ]], {8}}, } for _, row in ipairs(data) do @@ -66,7 +68,7 @@ test:do_test( function () return test:drop_all_tables() end, - 3) + 4) data = { { 1, [[ columnn ]], {0} }, diff --git a/test/sql/errinj.result b/test/sql/errinj.result index a1e7cc4a3..c974ab714 100644 --- a/test/sql/errinj.result +++ b/test/sql/errinj.result @@ -388,3 +388,21 @@ errinj.set("ERRINJ_WAL_DELAY", false) --- - ok ... +-- +-- gh-3931: Store regular identifiers in case-normal form +-- +errinj = box.error.injection +--- +... +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) +--- +- ok +... +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") +--- +- error: 'string conversion to the uppercase failed: U_MEMORY_ALLOCATION_ERROR' +... +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) +--- +- ok +... diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua index d8833feb4..f9e7a3c49 100644 --- a/test/sql/errinj.test.lua +++ b/test/sql/errinj.test.lua @@ -139,3 +139,11 @@ box.sql.execute("INSERT INTO t VALUES (2);") box.sql.execute("UPDATE t SET id = 2;") -- Finish drop space. errinj.set("ERRINJ_WAL_DELAY", false) + +-- +-- gh-3931: Store regular identifiers in case-normal form +-- +errinj = box.error.injection +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", true) +box.sql.execute("CREATE TABLE hello (id INT primary key,x INT,y INT);") +errinj.set("ERRINJ_SQL_NAME_NORMALIZATION", false) -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 7/7] sql: store regular identifiers in case-normal form 2019-03-20 11:02 ` Kirill Shcherbatov @ 2019-03-26 17:08 ` Vladislav Shpilevoy 0 siblings, 0 replies; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 17:08 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Thanks for the fixes! See my 5 comments below, review fixes at the end of the email, and on the branch. After you've received this bunch of reviews, I ask you ... not, even beg you not to hurry. Read the comments, the fixes, squash if you agree. Slowly and accurately fix the other places with which you do not agree. If such places exist, then send the patchset as v2. This thread is already dead and unreadable. If you agree with all the comments, then ask Kirill Y. what to do next - send it to Nikita, or directly to Kirill Y. > Introduced a new sql_normalize_name routine performing SQL name > conversion to case-normal form via unicode character folding. > For example, ß is converted to SS. The result is similar to SQL > UPPER function. > > Closes #3931 > --- > src/box/lua/lua_sql.c | 11 +++-- > src/box/sql/build.c | 32 +++++++++---- > src/box/sql/expr.c | 67 ++++++++++++++++++--------- > src/box/sql/parse.y | 25 ++++++++-- > src/box/sql/select.c | 19 ++++++-- > src/box/sql/sqlInt.h | 28 ++++++++++- > src/box/sql/trigger.c | 14 ++++-- > src/box/sql/util.c | 46 ++++++++++++------ > src/lib/core/errinj.h | 1 + > test/box/errinj.result | 2 + > test/sql-tap/identifier_case.test.lua | 12 +++-- > test/sql/errinj.result | 18 +++++++ > test/sql/errinj.test.lua | 8 ++++ > 13 files changed, 215 insertions(+), 68 deletions(-) > > diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c > index f5a7b7819..2b3a595af 100644 > --- a/src/box/lua/lua_sql.c > +++ b/src/box/lua/lua_sql.c > @@ -176,13 +176,16 @@ lbox_sql_create_function(struct lua_State *L) > } > size_t name_len; > const char *name = lua_tolstring(L, 1, &name_len); > + int normalized_name_sz = sql_normalize_name(NULL, 0, name, name_len); > + if (normalized_name_sz < 0) > + return luaT_error(L); > char *normalized_name = (char *) region_alloc(&fiber()->gc, > - name_len + 1); > + normalized_name_sz + 1); > if (normalized_name == NULL) > return luaL_error(L, "out of memory"); > - memcpy(normalized_name, name, name_len); > - normalized_name[name_len] = '\0'; > - sqlNormalizeName(normalized_name); > + if (sql_normalize_name(normalized_name, normalized_name_sz, name, > + name_len) < 0) > + return luaT_error(L); 1. When code duplicates not as 1-2 lines, but as a ~10-15 lines, it is almost always very bad. After your patch you have 2 completely equal places where you allocate a normalized name on region. And 3 places on sql malloc. I've put that code into functions and used them. > struct lua_sql_func_info *func_info = > (struct lua_sql_func_info *) malloc(sizeof(*func_info)); > if (func_info == NULL) > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c > index dad4ce3a6..55ca100c1 100644 > --- a/src/box/sql/expr.c > +++ b/src/box/sql/expr.c > @@ -883,7 +883,15 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) > if (token != NULL) { > if (op != TK_INTEGER || token->z == NULL || > sqlGetInt32(token->z, &val) == 0) { > - extra_sz = token->n + 1; > + if (op == TK_ID || op == TK_COLLATE || > + op == TK_FUNCTION) { > + extra_sz = sql_normalize_name(NULL, 0, token->z, > + token->n); > + if (extra_sz < 0) > + return NULL; > + } else { > + extra_sz = token->n + 1; > + } > assert(val >= 0); > } > } > @@ -909,15 +917,20 @@ sql_expr_new(struct sql *db, int op, const Token *token, bool dequote) > } else { > expr->u.zToken = (char *)&expr[1]; > assert(token->z != NULL || token->n == 0); > - memcpy(expr->u.zToken, token->z, token->n); > - expr->u.zToken[token->n] = '\0'; > - if (dequote) { > - if (expr->u.zToken[0] == '"') > - expr->flags |= EP_DblQuoted; > - if (expr->op == TK_ID || expr->op == TK_COLLATE || > - expr->op == TK_FUNCTION) > - sqlNormalizeName(expr->u.zToken); > - else > + if (dequote && expr->u.zToken[0] == '"') > + expr->flags |= EP_DblQuoted; > + if (dequote && > + (expr->op == TK_ID || expr->op == TK_COLLATE || > + expr->op == TK_FUNCTION)) { > + if (sql_normalize_name(expr->u.zToken, extra_sz, > + token->z, token->n) < 0) { > + sqlDbFree(db, expr); > + return NULL; > + } > + } else { > + memcpy(expr->u.zToken, token->z, token->n); > + expr->u.zToken[token->n] = '\0'; > + if (dequote) > sqlDequote(expr->u.zToken); > } 2. Two hunks above make sql_expr_new overcomplicated. You check for 'dequote' 3 times. And at the same time you do not check it when you decide whether to increase extra_sz in the hunk above. In fixes for the previous commit I split sql_expr_new into sql_expr_new and sql_expr_new_dequoted - it solves the problem here as well. > } > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index 13d0b16f8..120eedfa6 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3195,7 +3195,33 @@ void sqlTreeViewWith(TreeView *, const With *); > void sqlSetString(char **, sql *, const char *); > void sqlErrorMsg(Parse *, const char *, ...); > void sqlDequote(char *); > -void sqlNormalizeName(char *z); > + > +/** > + * Perform SQL name normalization: cast name to the upper-case > + * (via Unicode Character Folding). Casing is locale-dependent > + * and context-sensitive. The result may be longer or shorter > + * than the original. The source string and the destination buffer > + * must not overlap. > + * For example, ß is converted to SS. > + * The result is similar to SQL UPPER function. > + * > + * @param dst A buffer for the result string. The result will be > + * NUL-terminated if the buffer is large enough. The > + * contents is undefined in case of failure. 3. I understand that you just copy-pasted comment from ICU docs, but usually we do not write 'NUL-terminated'. We use either NULL, or 0. > + * @param dst_size The size of the buffer (number of bytes). > + * If it is 0, then dest may be NULL and the > + * function will only return the corresponding > + * dst_size - this size of buffer that may fit > + * result. 4. Here you decided not to copy though here the original comment would be correct. In your version you said, that in case of too small buffer you return dst_size. But it is wrong. What would be a sense of such a function, when on a too small buffer it returns the same too small size? In fact it returns a new size, not presented among the arguments. > + * @param src The original string. > + * @param src_len The length of the original string. > + * @retval The count of bytes written(or need to be written) on > + * success. > + * @retval < 0 Otherwise. The diag message is set. > + */ > +int > +sql_normalize_name(char *dst, int dst_size, const char *src, int src_len); > + > void sqlTokenInit(Token *, char *); > int sqlKeywordCode(const unsigned char *, int); > int sqlRunParser(Parse *, const char *); > diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c > index 544ba2a5a..16d0f7fe6 100644 > --- a/src/box/sql/trigger.c > +++ b/src/box/sql/trigger.c > @@ -279,15 +279,23 @@ sql_trigger_select_step(struct sql *db, struct Select *select) > static struct TriggerStep * > sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) > { > + int name_sz = > + sql_normalize_name(NULL, 0, target_name->z, target_name->n); > + if (name_sz < 0) > + return NULL; > int size = sizeof(struct TriggerStep) + target_name->n + 1; > struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); 5. What frustrated me the most in the patch is that place. Because it is easy visible on a very first self- even not a review, but just screening. You calculated name_sz, which can be bigger than target_name->n + 1, but left the old size as it was. Below it could lead to a bad memory access. > if (trigger_step == NULL) { > - diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); > + diag_set(OutOfMemory, name_sz, "sqlDbMallocZero", > + "trigger_step"); > return NULL; > } > char *z = (char *)&trigger_step[1]; > - memcpy(z, target_name->z, target_name->n); > - sqlNormalizeName(z); > + if (sql_normalize_name(z, name_sz, target_name->z, > + target_name->n) < 0) { > + sqlDbFree(db, trigger_step); > + return NULL; > + } > trigger_step->zTarget = z; > trigger_step->op = op; > return trigger_step; ========================================================================= commit 1dfc7de80f8de06348d5b0f035d705918c2e2dfa Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Tue Mar 26 19:03:20 2019 +0300 Review fixes diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c index 2b3a595af..3d0047e16 100644 --- a/src/box/lua/lua_sql.c +++ b/src/box/lua/lua_sql.c @@ -176,15 +176,9 @@ lbox_sql_create_function(struct lua_State *L) } size_t name_len; const char *name = lua_tolstring(L, 1, &name_len); - int normalized_name_sz = sql_normalize_name(NULL, 0, name, name_len); - if (normalized_name_sz < 0) - return luaT_error(L); - char *normalized_name = (char *) region_alloc(&fiber()->gc, - normalized_name_sz + 1); + char *normalized_name = + sql_normalized_name_region_new(&fiber()->gc, name, name_len); if (normalized_name == NULL) - return luaL_error(L, "out of memory"); - if (sql_normalize_name(normalized_name, normalized_name_sz, name, - name_len) < 0) return luaT_error(L); struct lua_sql_func_info *func_info = (struct lua_sql_func_info *) malloc(sizeof(*func_info)); diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 76801cd94..9c288b427 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -229,27 +229,6 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column) return false; } -char * -sql_name_from_token(struct sql *db, struct Token *name_token) -{ - assert(name_token != NULL && name_token->z != NULL); - int name_sz = - sql_normalize_name(NULL, 0, name_token->z, name_token->n); - if (name_sz < 0) - return NULL; - char *name = sqlDbMallocRawNN(db, name_sz); - if (name == NULL) { - diag_set(OutOfMemory, name_sz, "sqlDbMallocRawNN", "name"); - return NULL; - } - if (sql_normalize_name(name, name_sz, name_token->z, - name_token->n) < 0) { - sqlDbFree(db, name); - return NULL; - } - return name; -} - /* * This routine is used to check if the UTF-8 string zName is a legal * unqualified name for an identifier. @@ -441,18 +420,8 @@ sqlAddColumn(Parse * pParse, Token * pName, struct type_def *type_def) if (sql_field_retrieve(pParse, def, def->field_count) == NULL) return; struct region *region = &pParse->region; - int name_sz = sql_normalize_name(NULL, 0, pName->z, pName->n); - if (name_sz < 0) { - pParse->is_aborted = true; - return; - } - z = region_alloc(region, name_sz); + z = sql_normalized_name_region_new(region, pName->z, pName->n); if (z == NULL) { - diag_set(OutOfMemory, name_sz, "region_alloc", "z"); - pParse->is_aborted = true; - return; - } - if (sql_normalize_name(z, name_sz, pName->z, pName->n) < 0) { pParse->is_aborted = true; return; } diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 4d5464617..660110660 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -879,65 +879,113 @@ sqlExprSetHeightAndFlags(Parse * pParse, Expr * p) #define exprSetHeight(y) #endif /* SQL_MAX_EXPR_DEPTH>0 */ -struct Expr * -sql_expr_new(struct sql *db, int op, const struct Token *token) +/** + * Allocate a new empty expression object with reserved extra + * memory. + * @param db SQL context. + * @param op Expression value type. + * @param extra_size Extra size, needed to be allocated together + * with the expression. + * @retval Not NULL Success. An empty expression. + * @retval NULL Error. A diag message is set. + */ +static struct Expr * +sql_expr_new_empty(struct sql *db, int op, int extra_size) { - int extra_sz = 0; - int val = 0; - if (token != NULL) { - if (op != TK_INTEGER || token->z == NULL || - sqlGetInt32(token->z, &val) == 0) { - if (op == TK_ID || op == TK_COLLATE || - op == TK_FUNCTION) { - extra_sz = sql_normalize_name(NULL, 0, token->z, - token->n); - if (extra_sz < 0) - return NULL; - } else { - extra_sz = token->n + 1; - } - assert(val >= 0); - } - } - struct Expr *expr = sqlDbMallocRawNN(db, sizeof(*expr) + extra_sz); - if (expr == NULL) { - diag_set(OutOfMemory, sizeof(*expr), "sqlDbMallocRawNN", - "expr"); + struct Expr *e = sqlDbMallocRawNN(db, sizeof(*e) + extra_size); + if (e == NULL) { + diag_set(OutOfMemory, sizeof(*e), "sqlDbMallocRawNN", "e"); return NULL; } - - memset(expr, 0, sizeof(*expr)); - expr->op = (u8)op; - expr->iAgg = -1; + memset(e, 0, sizeof(*e)); + e->op = (u8)op; + e->iAgg = -1; #if SQL_MAX_EXPR_DEPTH > 0 - expr->nHeight = 1; + e->nHeight = 1; #endif - if (token == NULL) - return expr; + return e; +} - if (extra_sz == 0) { - expr->flags |= EP_IntValue; - expr->u.iValue = val; - } else { - expr->u.zToken = (char *)&expr[1]; - assert(token->z != NULL || token->n == 0); - memcpy(expr->u.zToken, token->z, token->n); - expr->u.zToken[token->n] = '\0'; +/** + * Try to convert a token of a specified type to integer. + * @param op Token type. + * @param token Token itself. + * @param[out] res Result integer. + * @retval 0 Success. @A res stores a result. + * @retval -1 Error. Can not be converted. No diag. + */ +static inline int +sql_expr_token_to_int(int op, const struct Token *token, int *res) +{ + if (op == TK_INTEGER && token->z != NULL && + sqlGetInt32(token->z, res) > 0) + return 0; + return -1; +} + +/** Create an expression of a constant integer. */ +static inline struct Expr * +sql_expr_new_int(struct sql *db, int value) +{ + struct Expr *e = sql_expr_new_empty(db, TK_INTEGER, 0); + if (e != NULL) { + e->flags |= EP_IntValue; + e->u.iValue = value; } - return expr; + return e; +} + +struct Expr * +sql_expr_new(struct sql *db, int op, const struct Token *token) +{ + int extra_sz = 0; + if (token != NULL) { + int val; + if (sql_expr_token_to_int(op, token, &val) == 0) + return sql_expr_new_int(db, val); + extra_sz = token->n + 1; + } + struct Expr *e = sql_expr_new_empty(db, op, extra_sz); + if (e == NULL || token == NULL) + return e; + e->u.zToken = (char *) &e[1]; + assert(token->z != NULL || token->n == 0); + memcpy(e->u.zToken, token->z, token->n); + e->u.zToken[token->n] = '\0'; + return e; } struct Expr * sql_expr_new_dequoted(struct sql *db, int op, const struct Token *token) { - struct Expr *e = sql_expr_new(db, op, token); - if (e == NULL || (e->flags & EP_IntValue) != 0 || e->u.zToken == NULL) + int extra_size = 0; + bool is_name = false; + if (token != NULL) { + int val; + assert(token->z != NULL || token->n == 0); + if (sql_expr_token_to_int(op, token, &val) == 0) + return sql_expr_new_int(db, val); + is_name = op == TK_ID || op == TK_COLLATE || op == TK_FUNCTION; + if (is_name) { + extra_size = sql_normalize_name(NULL, 0, token->z, + token->n); + if (extra_size < 0) + return NULL; + } else { + extra_size = token->n + 1; + } + } + struct Expr *e = sql_expr_new_empty(db, op, extra_size); + if (e == NULL || token == NULL || token->n == 0) return e; - if (e->u.zToken[0] == '"') + e->u.zToken = (char *) &e[1]; + if (token->z[0] == '"') e->flags |= EP_DblQuoted; - if (e->op != TK_ID && e->op != TK_COLLATE && e->op != TK_FUNCTION) { + if (! is_name) { + memcpy(e->u.zToken, token->z, token->n); + e->u.zToken[token->n] = '\0'; sqlDequote(e->u.zToken); - } else if (sql_normalize_name(e->u.zToken, strlen(e->u.zToken), token->z, + } else if (sql_normalize_name(e->u.zToken, extra_size, token->z, token->n) < 0) { sql_expr_delete(db, e, false); return NULL; @@ -1824,31 +1872,22 @@ sqlExprListSetName(Parse * pParse, /* Parsing context */ int dequote /* True to cause the name to be dequoted */ ) { - assert(pList != 0 || pParse->db->mallocFailed != 0); + struct sql *db = pParse->db; + assert(pList != 0 || db->mallocFailed != 0); if (pList == NULL || pName->n == 0) return; assert(pList->nExpr > 0); struct ExprList_item *item = &pList->a[pList->nExpr - 1]; assert(item->zName == NULL); if (dequote) { - int name_sz = sql_normalize_name(NULL, 0, pName->z, pName->n); - if (name_sz < 0) - goto tarantool_error; - item->zName = sqlDbMallocRawNN(pParse->db, name_sz); + item->zName = sql_normalized_name_db_new(db, pName->z, pName->n); if (item->zName == NULL) - return; - if (sql_normalize_name(item->zName, name_sz, pName->z, - pName->n) < 0) - goto tarantool_error; + pParse->is_aborted = true; } else { - item->zName = sqlDbStrNDup(pParse->db, pName->z, pName->n); - if (item->zName == NULL) - return; + item->zName = sqlDbStrNDup(db, pName->z, pName->n); } - sqlCheckIdentifierName(pParse, item->zName); - return; -tarantool_error: - pParse->is_aborted = true; + if (item->zName != NULL) + sqlCheckIdentifierName(pParse, item->zName); } /* diff --git a/src/box/sql/select.c b/src/box/sql/select.c index bc52451aa..c7e1879ca 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -4187,15 +4187,11 @@ flattenSubquery(Parse * pParse, /* Parsing context */ if (pList->a[i].zName == 0) { char *str = pList->a[i].zSpan; int len = strlen(str); - int name_sz = - sql_normalize_name(NULL, 0, str, len); - if (name_sz < 0) - goto tarantool_error; - char *name = sqlDbMallocRawNN(db, name_sz); - if (name != NULL && - sql_normalize_name(name, name_sz, str, - len) < 0) - goto tarantool_error; + char *name = + sql_normalized_name_db_new(db, str, + len); + if (name == NULL) + pParse->is_aborted = true; pList->a[i].zName = name; } } @@ -4274,9 +4270,6 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } #endif - return 1; -tarantool_error: - pParse->is_aborted = true; return 1; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 74c3a98be..168b4b47f 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3206,13 +3206,12 @@ void sqlDequote(char *); * The result is similar to SQL UPPER function. * * @param dst A buffer for the result string. The result will be - * NUL-terminated if the buffer is large enough. The - * contents is undefined in case of failure. - * @param dst_size The size of the buffer (number of bytes). - * If it is 0, then dest may be NULL and the - * function will only return the corresponding - * dst_size - this size of buffer that may fit - * result. + * 0-terminated if the buffer is large enough. The contents + * is undefined in case of failure. + * @param dst_size The size of the buffer (number of bytes). If it + * is 0, then dest may be NULL and the function will only + * return the length of the result without writing any of + * the result string * @param src The original string. * @param src_len The length of the original string. * @retval The count of bytes written(or need to be written) on @@ -3222,6 +3221,31 @@ void sqlDequote(char *); int sql_normalize_name(char *dst, int dst_size, const char *src, int src_len); +/** + * Duplicate a normalized version of @a name onto an sqlMalloc. + * For normalization rules @sa sql_normalize_name(). + * @param db SQL context. + * @param name Source string. + * @param len Length of @a name. + * @retval Not NULL Success. A normalized string is returned. + * @retval NULL Error. A diag message is set. + */ +char * +sql_normalized_name_db_new(struct sql *db, const char *name, int len); + +/** + * Duplicate a normalized version of @a name onto a region @a r. + * For normalization rules @sa sql_normalize_name(). + * @param r Region allocator. + * @param name Source string. + * @param len Length of @a name. + * @retval Not NULL Success. A normalized string is returned. + * @retval NULL Error. A diag message is set. Region is not + * truncated back. + */ +char * +sql_normalized_name_region_new(struct region *r, const char *name, int len); + void sqlTokenInit(Token *, char *); int sqlKeywordCode(const unsigned char *, int); int sqlRunParser(Parse *, const char *); @@ -3768,12 +3792,16 @@ void sqlExprIfFalse(Parse *, Expr *, int, int); * string is \000 terminated and is persistent. * * @param db The database connection. - * @param name_token The source token with text. + * @param t The source token with text. * @retval Not NULL Formatted name on new memory. * @retval NULL Error. Diag message is set. */ -char * -sql_name_from_token(struct sql *db, struct Token *name_token); +static inline char * +sql_name_from_token(struct sql *db, struct Token *t) +{ + assert(t != NULL && t->z != NULL); + return sql_normalized_name_db_new(db, t->z, t->n); +} int sqlExprCompare(Expr *, Expr *, int); int sqlExprListCompare(ExprList *, ExprList *, int); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 681bd55e4..ec3b3e172 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -279,19 +279,18 @@ sql_trigger_select_step(struct sql *db, struct Select *select) static struct TriggerStep * sql_trigger_step_new(struct sql *db, u8 op, struct Token *target_name) { - int name_sz = + int name_size = sql_normalize_name(NULL, 0, target_name->z, target_name->n); - if (name_sz < 0) + if (name_size < 0) return NULL; - int size = sizeof(struct TriggerStep) + target_name->n + 1; + int size = sizeof(struct TriggerStep) + name_size; struct TriggerStep *trigger_step = sqlDbMallocZero(db, size); if (trigger_step == NULL) { - diag_set(OutOfMemory, name_sz, "sqlDbMallocZero", - "trigger_step"); + diag_set(OutOfMemory, size, "sqlDbMallocZero", "trigger_step"); return NULL; } char *z = (char *)&trigger_step[1]; - if (sql_normalize_name(z, name_sz, target_name->z, + if (sql_normalize_name(z, name_size, target_name->z, target_name->n) < 0) { sqlDbFree(db, trigger_step); return NULL; diff --git a/src/box/sql/util.c b/src/box/sql/util.c index 85da88ab6..bca32ad9a 100644 --- a/src/box/sql/util.c +++ b/src/box/sql/util.c @@ -320,6 +320,40 @@ error: return -1; } +char * +sql_normalized_name_db_new(struct sql *db, const char *name, int len) +{ + int size = sql_normalize_name(NULL, 0, name, len); + if (size < 0) + return NULL; + char *res = sqlDbMallocRawNN(db, size); + if (res == NULL) { + diag_set(OutOfMemory, size, "sqlDbMallocRawNN", "res"); + return NULL; + } + if (sql_normalize_name(res, size, name, len) < 0) { + sqlDbFree(db, res); + return NULL; + } + return res; +} + +char * +sql_normalized_name_region_new(struct region *r, const char *name, int len) +{ + int size = sql_normalize_name(NULL, 0, name, len); + if (size < 0) + return NULL; + char *res = (char *) region_alloc(r, size); + if (res == NULL) { + diag_set(OutOfMemory, size, "region_alloc", "res"); + return NULL; + } + if (sql_normalize_name(res, size, name, len) < 0) + return NULL; + return res; +} + /* * Generate a Token object from a string */ ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 0/7] sql: store regular identifiers in case-normal form 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (6 preceding siblings ...) 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 7/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov @ 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-27 14:06 ` Kirill Yukhin 8 siblings, 1 reply; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-18 19:33 UTC (permalink / raw) To: Kirill Shcherbatov, tarantool-patches I see, that you've added a new patch, but decided not to send it. I've done that. Below you can find my 2 comments on it. > commit 47f6bd7a079c8a5af73abe88d9abc24e4bf95f75 > Author: Kirill Shcherbatov <kshcherbatov@tarantool.org> > Date: Mon Mar 11 14:33:57 2019 +0300 > > sql: rework sqlIdListAppend to set diag > > Refactored sqlIdListAppend routine as sql_id_list_append and > reworked it to use diag_set in case of memory allocation error. > This change is necessary because the sql_id_list_append body has > a sqlNameFromToken call that will be changed in subsequent > patches. > > This patch refers to a series of preparatory patches that provide > the use of Tarantool errors in the call tree that includes > sqlNormalizeName, since this call can later return errors. > > This patch is not self-sufficient, its sqlNameFromToken call > remained to be non-Tarantool (for now). It means, that if > sqlNameFromToken fails in sql_id_list_append there is no > diag message created. > > Part of #3931 > > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y > index 122fda2f0..8a8f1b82f 100644 > --- a/src/box/sql/parse.y > +++ b/src/box/sql/parse.y > @@ -797,10 +797,17 @@ insert_cmd(A) ::= REPLACE. {A = ON_CONFLICT_ACTION_REPLACE;} > > idlist_opt(A) ::= . {A = 0;} > idlist_opt(A) ::= LP idlist(X) RP. {A = X;} > -idlist(A) ::= idlist(A) COMMA nm(Y). > - {A = sqlIdListAppend(pParse->db,A,&Y);} > -idlist(A) ::= nm(Y). > - {A = sqlIdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} > +idlist(A) ::= idlist(A) COMMA nm(Y). { > + A = sql_id_list_append(pParse->db,A,&Y); > + if (A == NULL) > + sql_parser_error(pParse); > +} > +idlist(A) ::= nm(Y). { > + /* A-overwrites-Y. */ > + A = sql_id_list_append(pParse->db,0,&Y); > + if (A == NULL) > + sql_parser_error(pParse); 1. Why do not you return after an error? > +} > > /////////////////////////// Expression Processing ///////////////////////////// > // > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h > index 2df1830d6..56ae90a95 100644 > --- a/src/box/sql/sqlInt.h > +++ b/src/box/sql/sqlInt.h > @@ -3388,7 +3388,20 @@ sql_drop_table(struct Parse *, struct SrcList *, bool, bool); > void sqlInsert(Parse *, SrcList *, Select *, IdList *, > enum on_conflict_action); > void *sqlArrayAllocate(sql *, void *, int, int *, int *); > -IdList *sqlIdListAppend(sql *, IdList *, Token *); > + > +/** > + * Append a new element to the given IdList. Create a new IdList > + * if need be. > + * > + * @param db The database connection. > + * @param list The pointer to existent Id list if exists. > + * @param name_token The token containing name. > + * @retval Not NULL IdList pointer is returned on success. > + * @retval NULL otherwise. Diag message is set. > + */ > +struct IdList * > +sql_id_list_append(struct sql *db, struct IdList *pList, struct Token *pToken); 2. The arg names are old, and do not match the implementation and the comment. > + > int sqlIdListIndex(IdList *, const char *); > > /** ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 0/7] sql: store regular identifiers in case-normal form 2019-03-18 19:33 ` [tarantool-patches] Re: [PATCH v2 0/7] " Vladislav Shpilevoy @ 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:09 ` Vladislav Shpilevoy 0 siblings, 1 reply; 41+ messages in thread From: Kirill Shcherbatov @ 2019-03-20 11:02 UTC (permalink / raw) To: tarantool-patches, Vladislav Shpilevoy >> +idlist(A) ::= nm(Y). { >> + /* A-overwrites-Y. */ >> + A = sql_id_list_append(pParse->db,0,&Y); >> + if (A == NULL) >> + sql_parser_error(pParse); > > 1. Why do not you return after an error? Same as in previous patches. I don't mind return here. >> +/** >> + * Append a new element to the given IdList. Create a new IdList >> + * if need be. >> + * >> + * @param db The database connection. >> + * @param list The pointer to existent Id list if exists. >> + * @param name_token The token containing name. >> + * @retval Not NULL IdList pointer is returned on success. >> + * @retval NULL otherwise. Diag message is set. >> + */ >> +struct IdList * >> +sql_id_list_append(struct sql *db, struct IdList *pList, struct Token *pToken); > > 2. The arg names are old, and do not match the implementation > and the comment. Fixed. ==================================================== Refactored sqlSrcListAppend routine as sql_src_list_append and reworked it to use diag_set in case of memory allocation error. This change is necessary because the sql_src_list_append body has a sqlNameFromToken call that will be changed in subsequent patches. This patch refers to a series of preparatory patches that provide the use of Tarantool errors in the call tree that includes sqlNormalizeName, since this call can later return errors. This patch is not self-sufficient, its sqlNameFromToken call remained to be non-Tarantool (for now). It means, that if sqlNameFromToken fails in sql_src_list_append there is no diag message created. Part of #3931 --- src/box/sql/build.c | 70 +++++++++---------------------------- src/box/sql/delete.c | 16 +++++---- src/box/sql/fk_constraint.c | 19 +++++----- src/box/sql/parse.y | 24 ++++++++++--- src/box/sql/select.c | 8 ++--- src/box/sql/sqlInt.h | 20 ++++++++++- src/box/sql/trigger.c | 11 +++--- 7 files changed, 85 insertions(+), 83 deletions(-) diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 9f065e7b4..ef38503da 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2630,64 +2630,26 @@ sql_src_list_new(struct sql *db) return src_list; } -/* - * Append a new table name to the given SrcList. Create a new SrcList if - * need be. A new entry is created in the SrcList even if pTable is NULL. - * - * A SrcList is returned, or NULL if there is an OOM error. The returned - * SrcList might be the same as the SrcList that was input or it might be - * a new one. If an OOM error does occurs, then the prior value of pList - * that is input to this routine is automatically freed. - * - * If pDatabase is not null, it means that the table has an optional - * database name prefix. Like this: "database.table". The pDatabase - * points to the table name and the pTable points to the database name. - * The SrcList.a[].zName field is filled with the table name which might - * come from pTable (if pDatabase is NULL) or from pDatabase. - * SrcList.a[].zDatabase is filled with the database name from pTable, - * or with NULL if no database is specified. - * - * In other words, if call like this: - * - * sqlSrcListAppend(D,A,B,0); - * - * Then B is a table name and the database name is unspecified. If called - * like this: - * - * sqlSrcListAppend(D,A,B,C); - * - * Then C is the table name and B is the database name. If C is defined - * then so is B. In other words, we never have a case where: - * - * sqlSrcListAppend(D,A,0,C); - * - * Both pTable and pDatabase are assumed to be quoted. They are dequoted - * before being added to the SrcList. - */ -SrcList * -sqlSrcListAppend(sql * db, /* Connection to notify of malloc failures */ - SrcList * pList, /* Append to this SrcList. NULL creates a new SrcList */ - Token * pTable /* Table to append */ - ) +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token) { - struct SrcList_item *pItem; - assert(db != 0); - if (pList == 0) { - pList = sql_src_list_new(db); - if (pList == 0) - return 0; + if (list == NULL) { + list = sql_src_list_new(db); + if (list == NULL) + return NULL; } else { struct SrcList *new_list = - sql_src_list_enlarge(db, pList, 1, pList->nSrc); + sql_src_list_enlarge(db, list, 1, list->nSrc); if (new_list == NULL) { - sqlSrcListDelete(db, pList); + sqlSrcListDelete(db, list); return NULL; } - pList = new_list; + list = new_list; } - pItem = &pList->a[pList->nSrc - 1]; - pItem->zName = sqlNameFromToken(db, pTable); - return pList; + struct SrcList_item *item = &list->a[list->nSrc - 1]; + item->zName = sqlNameFromToken(db, name_token); + return list; } /* @@ -2779,10 +2741,12 @@ sqlSrcListAppendFromTerm(Parse * pParse, /* Parsing context */ pParse->is_aborted = true; goto append_from_error; } - p = sqlSrcListAppend(db, p, pTable); - if (p == 0 || NEVER(p->nSrc == 0)) { + p = sql_src_list_append(db, p, pTable); + if (p == NULL) { + pParse->is_aborted = true; goto append_from_error; } + assert(p->nSrc != 0); pItem = &p->a[p->nSrc - 1]; assert(pAlias != 0); if (pAlias->n) { diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index f4d0334f4..4bc63346c 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -70,14 +70,16 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where, int cursor) { struct sql *db = parse->db; - where = sqlExprDup(db, where, 0); - struct SrcList *from = sqlSrcListAppend(db, NULL, NULL); - if (from != NULL) { - assert(from->nSrc == 1); - from->a[0].zName = sqlDbStrDup(db, name); - assert(from->a[0].pOn == NULL); - assert(from->a[0].pUsing == NULL); + struct SrcList *from = sql_src_list_append(db, NULL, NULL); + if (from == NULL) { + parse->is_aborted = true; + return; } + where = sqlExprDup(db, where, 0); + assert(from->nSrc == 1); + from->a[0].zName = sqlDbStrDup(db, name); + assert(from->a[0].pOn == NULL); + assert(from->a[0].pUsing == NULL); struct Select *select = sqlSelectNew(parse, NULL, from, where, NULL, NULL, NULL, 0, NULL, NULL); struct SelectDest dest; diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c index 4066b1cf1..cc4dc6448 100644 --- a/src/box/sql/fk_constraint.c +++ b/src/box/sql/fk_constraint.c @@ -618,9 +618,11 @@ fk_constraint_emit_check(struct Parse *parser, struct space *space, int reg_old, * table. We need the child table as a SrcList for * sqlWhereBegin(). */ - struct SrcList *src = sqlSrcListAppend(db, NULL, NULL); - if (src == NULL) - continue; + struct SrcList *src = sql_src_list_append(db, NULL, NULL); + if (src == NULL) { + parser->is_aborted = true; + return; + } struct SrcList_item *item = src->a; struct space *child = space_by_id(fk->def->child_id); assert(child != NULL); @@ -866,11 +868,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def, "constraint failed"); if (r != NULL) r->on_conflict_action = ON_CONFLICT_ACTION_ABORT; - select = sqlSelectNew(pParse, - sql_expr_list_append(db, NULL, r), - sqlSrcListAppend(db, NULL, &err), - where, NULL, NULL, NULL, 0, NULL, - NULL); + struct SrcList *src_list = sql_src_list_append(db, NULL, &err); + if (src_list == NULL) + pParse->is_aborted = true; + select = sqlSelectNew(pParse, sql_expr_list_append(db, NULL, r), + src_list, where, NULL, NULL, NULL, 0, + NULL, NULL); where = NULL; } diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index b27651c3b..ead71dfc0 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -616,8 +616,14 @@ seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP %type fullname {SrcList*} %destructor fullname {sqlSrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X). - {A = sqlSrcListAppend(pParse->db,0,&X); /*A-overwrites-X*/} +fullname(A) ::= nm(X). { + /*A-overwrites-X. */ + A = sql_src_list_append(pParse->db,0,&X); + if (A == NULL) { + pParse->is_aborted = true; + return; + } +} %type joinop {int} join_nm(A) ::= id(A). @@ -1160,7 +1166,11 @@ expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] { A.zEnd = &E.z[E.n]; } expr(A) ::= expr(A) in_op(N) nm(Y) paren_exprlist(E). [IN] { - SrcList *pSrc = sqlSrcListAppend(pParse->db, 0,&Y); + struct SrcList *pSrc = sql_src_list_append(pParse->db, 0,&Y); + if (pSrc == NULL) { + pParse->is_aborted = true; + return; + } Select *pSelect = sqlSelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); if( E ) sqlSrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A.pExpr = sqlPExpr(pParse, TK_IN, A.pExpr, 0); @@ -1230,8 +1240,12 @@ paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;} // cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) ON nm(Y) LP sortlist(Z) RP. { - sql_create_index(pParse, &X, sqlSrcListAppend(pParse->db,0,&Y), Z, &S, - SORT_ORDER_ASC, NE, U); + struct SrcList *src_list = sql_src_list_append(pParse->db,0,&Y); + if (src_list == NULL) { + pParse->is_aborted = true; + return; + } + sql_create_index(pParse, &X, src_list, Z, &S, SORT_ORDER_ASC, NE, U); } %type uniqueflag {int} diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 9ebd65bad..1b7d52b68 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -256,7 +256,7 @@ findRightmost(Select * p) /** - * Work the same as sqlSrcListAppend(), but before adding to + * Work the same as sql_src_list_append(), but before adding to * list provide check on name duplicates: only values with unique * names are appended. Moreover, names of tables are not * normalized: it is parser's business and in struct Select they @@ -4118,9 +4118,9 @@ flattenSubquery(Parse * pParse, /* Parsing context */ } else { assert(pParent != p); /* 2nd and subsequent times through the loop */ pSrc = pParent->pSrc = - sqlSrcListAppend(db, 0, 0); - if (pSrc == 0) { - assert(db->mallocFailed); + sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + pParse->is_aborted = true; break; } } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 2b31f8b19..b6c89893a 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3451,7 +3451,25 @@ sql_src_list_enlarge(struct sql *db, struct SrcList *src_list, int new_slots, struct SrcList * sql_src_list_new(struct sql *db); -SrcList *sqlSrcListAppend(sql *, SrcList *, Token *); +/** + * Append a new table name to the given list. Create a new + * SrcList if need be. A new entry is created in the list even + * if name_token is NULL. + * + * @param db The database connection. + * @param list Append to this SrcList. NULL creates a new SrcList. + * @param name_token Token representing table name. + * @retval Not NULL SrcList pointer is returned. The returned + * SrcList might be the same as the list that was input + * or it might be a new one. + * @retval NULL Otherwise. The diag message is set. The prior + * value of list that is input to this routine is + * automatically freed. + */ +struct SrcList * +sql_src_list_append(struct sql *db, struct SrcList *list, + struct Token *name_token); + SrcList *sqlSrcListAppendFromTerm(Parse *, SrcList *, Token *, Token *, Select *, Expr *, IdList *); void sqlSrcListIndexedBy(Parse *, SrcList *, Token *); diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index 7eacd33d4..b1f5033c4 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -587,12 +587,13 @@ targetSrcList(Parse * pParse, /* The parsing context */ sql *db = pParse->db; SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlSrcListAppend(db, 0, 0); - if (pSrc) { - assert(pSrc->nSrc > 0); - pSrc->a[pSrc->nSrc - 1].zName = - sqlDbStrDup(db, pStep->zTarget); + pSrc = sql_src_list_append(db, 0, 0); + if (pSrc == NULL) { + pParse->is_aborted = true; + return NULL; } + assert(pSrc->nSrc > 0); + pSrc->a[pSrc->nSrc - 1].zName = sqlDbStrDup(db, pStep->zTarget); return pSrc; } -- 2.21.0 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 0/7] sql: store regular identifiers in case-normal form 2019-03-20 11:02 ` Kirill Shcherbatov @ 2019-03-26 17:09 ` Vladislav Shpilevoy 0 siblings, 0 replies; 41+ messages in thread From: Vladislav Shpilevoy @ 2019-03-26 17:09 UTC (permalink / raw) To: tarantool-patches, Kirill Shcherbatov Hi! Thanks for the fixes! Please, look at the diff below and on the branch. ============================================================= commit d41b8abf83f31e8ce2ae1792a211c096e48e39e1 Author: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Mon Mar 25 15:49:09 2019 +0300 Review fix diff --git a/src/box/sql/build.c b/src/box/sql/build.c index dae582d1f..7bd99feb6 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2521,21 +2521,18 @@ sql_id_list_append(struct sql *db, struct IdList *list, struct Token *name_token) { if (list == NULL && - (list = sqlDbMallocZero(db, sizeof(struct IdList))) == NULL) { - diag_set(OutOfMemory, sizeof(struct IdList), "sqlDbMallocZero", - "list"); + (list = sqlDbMallocZero(db, sizeof(*list))) == NULL) { + diag_set(OutOfMemory, sizeof(*list), "sqlDbMallocZero", "list"); return NULL; } int i; list->a = sqlArrayAllocate(db, list->a, sizeof(list->a[0]), &list->nId, &i); - if (i < 0) - goto error; - list->a[i].zName = sqlNameFromToken(db, name_token); - if (list->a[i].zName == NULL) - goto error; - return list; -error: + if (i >= 0) { + list->a[i].zName = sqlNameFromToken(db, name_token); + if (list->a[i].zName != NULL) + return list; + } sqlIdListDelete(db, list); return NULL; } diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index 192929747..998a601c2 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -3419,8 +3419,8 @@ void *sqlArrayAllocate(sql *, void *, int, int *, int *); * @param db The database connection. * @param list The pointer to existent Id list if exists. * @param name_token The token containing name. - * @retval Not NULL IdList pointer is returned on success. - * @retval NULL Otherwise. Diag message is set. + * @retval Not NULL A new list or updated @a list. + * @retval NULL Error. Diag message is set. */ struct IdList * sql_id_list_append(struct sql *db, struct IdList *list, ^ permalink raw reply [flat|nested] 41+ messages in thread
* [tarantool-patches] Re: [PATCH v2 0/7] sql: store regular identifiers in case-normal form 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov ` (7 preceding siblings ...) 2019-03-18 19:33 ` [tarantool-patches] Re: [PATCH v2 0/7] " Vladislav Shpilevoy @ 2019-03-27 14:06 ` Kirill Yukhin 8 siblings, 0 replies; 41+ messages in thread From: Kirill Yukhin @ 2019-03-27 14:06 UTC (permalink / raw) To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov Hello, On 27 Feb 14:13, Kirill Shcherbatov wrote: > Perform SQL name normalization: cast name to the upper-case > (via Unicode Character Folding). Casing is locale-dependent > and context-sensitive. The result may be longer or shorter > than the original. For example, ß is converted to SS. > The result is similar to SQL UPPER function. > > Performed extensive code refactoring to pass parser instance in > routines that use sql_normalize_name function. This makes > possible to raise an error in case of normalizing failure. > > Changes in version 2: > - Setup parser error externally > > Branch: http://github.com/tarantool/tarantool/tree/kshch/gh-3991-fix-names-normalization > Issue: https://github.com/tarantool/tarantool/issues/3931 I've committed your patchset into master and 2.1 branches. -- Regards, Kirill Yukhin ^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2019-03-27 14:06 UTC | newest] Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-02-27 11:13 [tarantool-patches] [PATCH v2 0/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 1/7] sql: refactor sql_alloc_src_list to set diag Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 2/7] sql: rework sql_src_list_enlarge " Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 3/7] sql: refactor sql_src_list_append " Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 2019-03-26 18:07 ` Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 4/7] sql: refactor sql_name_from_token " Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 5/7] sql: refactor sql_trigger_step_allocate " Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 6/7] sql: refactor sql_expr_create " Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 2019-02-27 11:13 ` [tarantool-patches] [PATCH v2 7/7] sql: store regular identifiers in case-normal form Kirill Shcherbatov 2019-03-07 17:34 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-11 15:04 ` Kirill Shcherbatov 2019-03-18 19:33 ` Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:08 ` Vladislav Shpilevoy 2019-03-18 19:33 ` [tarantool-patches] Re: [PATCH v2 0/7] " Vladislav Shpilevoy 2019-03-20 11:02 ` Kirill Shcherbatov 2019-03-26 17:09 ` Vladislav Shpilevoy 2019-03-27 14:06 ` Kirill Yukhin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox