From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: Kirill Shcherbatov <kshcherbatov@tarantool.org>, tarantool-patches@freelists.org Subject: [tarantool-patches] Re: [PATCH v7 7/7] sql: remove Checks to server Date: Thu, 24 May 2018 22:26:43 +0300 [thread overview] Message-ID: <4221becc-9d9c-852a-ccd1-6ed3618cde9f@tarantool.org> (raw) In-Reply-To: <2baee418a3799c91c5a8b92ac62e59de3456da0d.1527084287.git.kshcherbatov@tarantool.org> Thanks for the patch! See 12 comments below. And I have pushed other review fixes on the branch in a separate commit. Please, look at them and squash. On 23/05/2018 17:05, Kirill Shcherbatov wrote: > New server checks stored in space_opts. For ExprList transfering > to server data is packed in msgpack as array of maps: > [{"expr_str": "x < y", "name" : "FIRST"}, ..] > Introduced checks_array_decode aimed to unpack this > complex package. > Introduced sql_checks_update_space_def_reference to update space > references as space_def pointer changes over the time, > i.e. resolved checks refer released memory. > > Resolves #3272. > --- > src/box/alter.cc | 5 +++ > src/box/opt_def.h | 5 ++- > src/box/space_def.c | 96 +++++++++++++++++++++++++++++++++++++++ > src/box/space_def.h | 12 ++--- > src/box/sql.c | 107 +++++++++++++++++++++++++++++++++++++++++++- > src/box/sql.h | 43 ++++++++++++++++++ > src/box/sql/build.c | 65 +++++++++++++++++++-------- > src/box/sql/insert.c | 33 ++++++++------ > src/box/sql/pragma.h | 2 - > src/box/sql/resolve.c | 2 - > src/box/sql/sqliteInt.h | 1 - > test/sql-tap/check.test.lua | 10 ++--- > 12 files changed, 329 insertions(+), 52 deletions(-) > > diff --git a/src/box/alter.cc b/src/box/alter.cc > index e4780da..b379da2 100644 > --- a/src/box/alter.cc > +++ b/src/box/alter.cc > @@ -523,6 +523,11 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode, > space_def_new_xc(id, uid, exact_field_count, name, name_len, > engine_name, engine_name_len, &opts, fields, > field_count); > + if (def->opts.checks != NULL && > + sql_checks_resolve_space_def_reference(def->opts.checks, def) != 0){ > + tnt_raise(ClientError, errcode, def->name, > + box_error_message(box_error_last())); 1. You must not reset OOM error. > diff --git a/src/box/opt_def.h b/src/box/opt_def.h > index 71fef3a..91e4f0b 100644 > --- a/src/box/opt_def.h > +++ b/src/box/opt_def.h > @@ -79,11 +79,12 @@ struct opt_def { > > #define OPT_DEF_ENUM(key, enum_name, opts, field, to_enum) \ > { key, OPT_ENUM, offsetof(opts, field), sizeof(int), #enum_name, \ > - sizeof(enum enum_name), enum_name##_strs, enum_name##_MAX, {to_enum} } > + sizeof(enum enum_name), enum_name##_strs, enum_name##_MAX, \ > + { to_enum } } 2. Garbage diff. > > #define OPT_DEF_ARRAY(key, opts, field, to_array) \ > { key, OPT_ARRAY, offsetof(opts, field), sizeof(((opts *)0)->field), \ > - NULL, 0, NULL, 0, {to_array} } > + NULL, 0, NULL, 0, { (void *)to_array} } 3. Same. And I do not see this diff on the branch. Have you sent me an out of date patch? > > #define OPT_END {NULL, opt_type_MAX, 0, 0, NULL, 0, NULL, 0, {NULL}} > > diff --git a/src/box/space_def.c b/src/box/space_def.c > index 9e0e7e3..7bff86f 100644 > --- a/src/box/space_def.c > +++ b/src/box/space_def.c > @@ -33,17 +33,33 @@ > #include "diag.h" > #include "error.h" > #include "sql.h" > +#include "msgpuck.h" > + > +/** > + * Make checks from msgpack. > + * @param str pointer to array of maps > + * e.g. [{"expr_str": "x < y", "name": "ONE"}, ..]. 4. I do not like 'expr_str' name. Obviously it is string, a user can not use another type. Sounds like "much of a muchness". Lets do like defaults: just 'expr'. > + * @param len array items count. > + * @param opt pointer to store parsing result. > + * @retval not NULL Checks pointer on success. > + * @retval NULL on error. > + */ > +static int > +checks_array_decode(const char **str, uint32_t len, char *opt); > > @@ -122,6 +138,19 @@ space_def_dup(const struct space_def *src) > } > } > } > + if (src->opts.checks != NULL) { > + ret->opts.checks = > + sql_expr_list_dup(sql_get(), src->opts.checks, 0); > + if (ret->opts.checks == NULL) { > + diag_set(OutOfMemory, 0, "sql_expr_list_dup", > + "ret->opts.checks"); > + space_def_destroy_fields(ret->fields, ret->field_count); > + free(ret->opts.sql); > + free(ret); > + return NULL; > + } > + sql_checks_update_space_def_reference(ret->opts.checks, ret); > + } > tuple_dictionary_ref(ret->dict); > return ret; > } > @@ -207,6 +236,18 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count, > } > } > } > + if (opts->checks != NULL) { > + def->opts.checks = sql_expr_list_dup(sql_get(), opts->checks, 0); > + if (def->opts.checks == NULL) { > + diag_set(OutOfMemory, 0, "sql_expr_list_dup", > + "def->opts.checks"); > + space_def_destroy_fields(def->fields, def->field_count); > + free(def->opts.sql); > + free(def); > + return NULL; > + } > + sql_checks_update_space_def_reference(def->opts.checks, def); > + } 5. I have refactored this and the previous hunks (fixed tuple_dictionary leak in space_def_new). But it is bad still because of code duplicating. Looks like it is time to create a function like 'space_def_dup_opts(space_def a, opts b)', that copies `b` into `a->opts`. This function duplicates sql, checks and updates space_def references. > return def; > } > > @@ -231,3 +272,58 @@ space_def_delete(struct space_def *def) > +static int > +checks_array_decode(const char **str, uint32_t len, char *opt) > +{ > + struct ExprList *checks = NULL; > + const char **map = str; > + struct sqlite3 *db = sql_get(); > + for (unsigned i = 0; i < len; i++) { > + checks = sql_expr_list_append(db, checks, NULL); > + if (checks == NULL) { > + diag_set(OutOfMemory, 0, "sql_expr_list_append", > + "pChecks"); > + goto error; > + } > + const char *expr_name = NULL; > + const char *expr_str = NULL; > + uint32_t expr_name_len = 0; > + uint32_t expr_str_len = 0; > + uint32_t map_size = mp_decode_map(map); > + for (unsigned j = 0; j < map_size; j++) { > + if (mp_typeof(**map) != MP_STR) { > + diag_set(ClientError, ER_WRONG_INDEX_OPTIONS, 0, > + "key must be a string"); 6. It is not index and not ER_WRONG_INDEX_OPTIONS. Here you parse space options. You must do like opts_parse_key - take error code and field number as arguments. And do diag_set(ClientError, errcode, fieldno, errmessage); > + goto error; > + } > + uint32_t key_len; > + const char *key = mp_decode_str(map, &key_len); > + if (strncmp(key, "expr_str", key_len) == 0) { > + expr_str = mp_decode_str(map, &expr_str_len); > + } else if (strncmp(key, "name", key_len) == 0) { > + expr_name = mp_decode_str(map, &expr_name_len); > + } else { > + diag_set(ClientError, ER_WRONG_INDEX_OPTIONS, 0, > + "invalid MsgPack"); > + goto error; > + } 7. When you use strncmp, I can do this: box.cfg{} opts = {checks = {{exp = 'X<5'}}} format = {{name = 'X', type = 'unsigned'}} t = {513, 1, 'test', 'memtx', 0, opts, format} box.space._space:insert(t) I have used 'exp' instead of 'expr_str'. Strncmp is a useless function as I think, because it can be broken easy. Better use a_len == b_len && memcmp(a, b) == 0. > + } > + sql_check_list_item_init(checks, i, expr_name, > + expr_name_len, expr_str, > + expr_str_len); > + } > + *(void **)opt = checks; > + return 0; > +error: > + sql_expr_list_delete(db, checks); > + return -1; > +} > diff --git a/src/box/sql.c b/src/box/sql.c > index 0845e65..d62e14c 100644 > --- a/src/box/sql.c > +++ b/src/box/sql.c > @@ -1761,3 +1797,72 @@ sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable) > pTable->def->opts.temporary = false; > return 0; > } > + > +int > +sql_check_list_item_init(struct ExprList *expr_list, int idx, > + const char *expr_name, uint32_t expr_name_len, > + const char *expr_str, uint32_t expr_str_len) > +{ > + assert(idx < expr_list->nExpr); > + struct ExprList_item *item = &expr_list->a[idx]; > + memset(item, 0, sizeof(struct ExprList_item)); > + if (expr_name != NULL) { > + item->zName = sqlite3DbStrNDup(db, expr_name, expr_name_len); 8. Please, use strndup. And set diag on OOM. > + if (item->zName == NULL) > + return -1; > + } > + struct Expr *check = NULL; > + if (expr_str != NULL && > + sql_expr_compile(db, expr_str, expr_str_len, > + &check) != 0) { > + sqlite3DbFree(db, item->zName); > + return -1; > + } > + item->pExpr = check; > + return 0; > +} > + > +int > +sql_checks_resolve_space_def_reference(ExprList *expr_list, > + struct space_def *def) > +{ > + Parse parser; > + sql_parser_create(&parser, sql_get()); > + parser.parse_only = true; > + > + Table dummy_table; > + memset(&dummy_table, 0, sizeof(dummy_table)); > + dummy_table.def = def; 9. Why can not you replace pTab in struct SrcList_item with space_def? > + > + sql_resolve_self_reference(&parser, &dummy_table, NC_IsCheck, NULL, > + expr_list); > + int rc = parser.rc; > + if (rc != SQLITE_OK) > + diag_set(IllegalParams, parser.zErrMsg); > + > + sql_parser_destroy(&parser); > + > + return rc == SQLITE_OK ? 0 : -1; > +} > diff --git a/src/box/sql/build.c b/src/box/sql/build.c > index d00bb1d..f5bb54d 100644 > --- a/src/box/sql/build.c > +++ b/src/box/sql/build.c > @@ -1848,17 +1867,14 @@ sqlite3EndTable(Parse * pParse, /* Parse context */ > return; > } > > + /* > + * As rebuild creates a new ExpList tree and old_def > + * is allocated on region release old tree manually. > + */ > + struct ExprList *old_checks = p->def->opts.checks; > if (sql_table_def_rebuild(db, p) != 0) > return; 10. deleteTable leaks with the same reason: you delete space_def only when opts.is_temporary flag is set. Including checks, that are on malloc. > - > #ifndef SQLITE_OMIT_VIEW > @@ -2106,7 +2131,9 @@ sql_view_column_names(struct Parse *parse, struct Table *table) > struct Table *sel_tab = sqlite3ResultSetOfSelect(parse, select); > parse->nTab = n; > int rc = 0; > - if (table->pCheck != NULL) { > + /* Get server checks. */ > + ExprList *checks = space_checks_expr_list(table->def->id); 11. Why do you space->def->opts.checks here? I scanned sql_view_column_names, and looks like it does not require compiled check. It needs only check names and check span. Table.def has both, it is not? 12. Please, write tests on checks insertion from Lua. I know such checks will not work, but at least you should do sanity test. You should test, that a user can insert a check with no name, that a check can be compiled even if no SQL Table, that a check expr can not be number or bool and the same about name. In the same tests you can check errors on non-existing column names.
next prev parent reply other threads:[~2018-05-24 19:26 UTC|newest] Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-05-23 14:05 [tarantool-patches] [PATCH v7 0/7] " Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 1/7] sql: remove parser construct, destruct to sql.h Kirill Shcherbatov 2018-05-23 17:46 ` [tarantool-patches] " Konstantin Osipov 2018-05-24 19:26 ` Vladislav Shpilevoy 2018-05-25 12:05 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 2/7] box: introduce OPT_ARRAY opt_type to decode arrays Kirill Shcherbatov 2018-05-23 17:53 ` [tarantool-patches] " Konstantin Osipov 2018-05-24 7:32 ` Kirill Shcherbatov 2018-05-24 19:26 ` Vladislav Shpilevoy 2018-05-25 11:54 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 3/7] sql: introduce expr_len for sql_expr_compile Kirill Shcherbatov 2018-05-24 19:26 ` [tarantool-patches] " Vladislav Shpilevoy 2018-05-25 11:54 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 4/7] sql: rename sql_expr_free to sql_expr_delete Kirill Shcherbatov 2018-05-23 18:00 ` [tarantool-patches] " Konstantin Osipov 2018-05-24 19:26 ` Vladislav Shpilevoy 2018-05-25 11:54 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 5/7] sql: change sqlite3AddCheckConstraint signature Kirill Shcherbatov 2018-05-23 18:01 ` [tarantool-patches] " Konstantin Osipov 2018-05-24 19:26 ` Vladislav Shpilevoy 2018-05-25 11:53 ` Kirill Shcherbatov 2018-05-29 11:51 ` n.pettik 2018-05-30 8:32 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 6/7] sql: export funcs defined on Expr, ExprList to sql.h Kirill Shcherbatov 2018-05-23 18:15 ` [tarantool-patches] " Konstantin Osipov 2018-05-24 7:33 ` Kirill Shcherbatov 2018-05-24 19:26 ` Vladislav Shpilevoy 2018-05-25 11:53 ` Kirill Shcherbatov 2018-05-28 11:19 ` Vladislav Shpilevoy 2018-05-28 14:59 ` Kirill Shcherbatov 2018-05-23 14:05 ` [tarantool-patches] [PATCH v7 7/7] sql: remove Checks to server Kirill Shcherbatov 2018-05-24 19:26 ` Vladislav Shpilevoy [this message] 2018-05-25 11:53 ` [tarantool-patches] " Kirill Shcherbatov 2018-05-28 11:19 ` Vladislav Shpilevoy 2018-05-28 14:59 ` Kirill Shcherbatov 2018-05-28 18:50 ` Vladislav Shpilevoy 2018-05-29 11:49 ` n.pettik 2018-05-30 8:32 ` Kirill Shcherbatov 2018-05-30 10:42 ` n.pettik 2018-05-25 12:04 ` [tarantool-patches] Re: [PATCH v7 0/7] " Kirill Shcherbatov 2018-05-28 11:19 ` Vladislav Shpilevoy 2018-05-30 11:03 ` n.pettik 2018-05-31 17:44 ` Kirill Yukhin
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=4221becc-9d9c-852a-ccd1-6ed3618cde9f@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=kshcherbatov@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='[tarantool-patches] Re: [PATCH v7 7/7] sql: remove Checks to server' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox