[Tarantool-patches] [PATCH v1 1/1] sql: remove SQL built-in functions from _func
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Mon Aug 2 23:12:29 MSK 2021
Hi! Thanks for the patch!
See 11 comments below.
On 30.07.2021 00:48, imeevma at tarantool.org wrote:
> This patch removes SQL built-in functions from _func. These functions
> could be called directly from Lua, however all they did was returnd an
1. returnd -> returned.
> error. After this patch, no SQL built-in functions can be called
> directly from LUA.
>
> Closes #6106
> ---
> https://github.com/tarantool/tarantool/issues/6106
> https://github.com/tarantool/tarantool/tree/imeevma/gh-6106-remove-sql-builtins-from-_func
> > diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
> index 97afc0b4d..d0ca3db97 100644
> --- a/src/box/lua/upgrade.lua
> +++ b/src/box/lua/upgrade.lua
> @@ -1003,19 +1003,19 @@ end
> --------------------------------------------------------------------------------
> -- Tarantool 2.9.1
> --------------------------------------------------------------------------------
> -local function sql_builtin_function_uuid()
> +local function remove_sql_builtin_functions_from_func()
> local _func = box.space._func
> local _priv = box.space._priv
> - local datetime = os.date("%Y-%m-%d %H:%M:%S")
> - local t = _func:auto_increment({ADMIN, 'UUID', 1, 'SQL_BUILTIN', '',
> - 'function', {}, 'any', 'none', 'none',
> - false, false, true, {}, setmap({}), '',
> - datetime, datetime})
> - _priv:replace{ADMIN, PUBLIC, 'function', t.id, box.priv.X}
> + for _, v in _func:pairs() do
> + if (v.language == "SQL_BUILTIN") then
2. You do not need () around the condition.
> + _priv:delete({2, 'function', v.id})
> + _func:delete({v.id})
> + end
> + end
> end
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 3772596d6..7c1616a3a 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -326,11 +326,13 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id,
> break;
> }
> if (op == TK_FUNCTION) {
> - uint32_t arg_count = p->x.pList == NULL ? 0 :
> - p->x.pList->nExpr;
> + int arg_count = p->x.pList == NULL ? 0 :
> + p->x.pList->nExpr;
> struct func *func =
> - sql_func_by_signature(p->u.zToken, arg_count);
> - if (func == NULL)
> + sql_func_by_name(p->u.zToken);
> + if (func == NULL || !func->def->exports.sql ||
3. Why don't you filter by !func->def->exports.sql in sql_func_by_name()?
Is it possible in some places to call a function not exported to SQL?
> + (func->def->param_count != -1 &&
> + func->def->param_count != arg_count))
4. Why is argument count important? Isn't SQL_FUNC_DERIVEDCOLL below
enough?
The same 2 comments in the next diff block.
> break;
> if (sql_func_flag_is_set(func, SQL_FUNC_DERIVEDCOLL) &&
> arg_count > 0) {
> @@ -3979,8 +3981,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> nFarg = pFarg ? pFarg->nExpr : 0;
> assert(!ExprHasProperty(pExpr, EP_IntValue));
> zId = pExpr->u.zToken;
> - struct func *func = sql_func_by_signature(zId, nFarg);
> - if (func == NULL) {
> + struct func *func = sql_func_by_name(zId);
> + if (func == NULL || !func->def->exports.sql ||
> + (func->def->param_count != -1 &&
> + func->def->param_count != nFarg)) {
> diag_set(ClientError, ER_NO_SUCH_FUNCTION,
> zId);
> pParse->is_aborted = true;
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index 6ca852dec..b05038e2e 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -47,6 +47,37 @@
> #include <unicode/ucol.h>
> #include "box/coll_id_cache.h"
> #include "box/schema.h"
> +#include "core/assoc.h"
5. core/ prefix is not not necessary. Also it might look better if
you group all box/ includes together.
> +#include "box/user.h"
> +
> +static struct mh_strnptr_t *built_in_functions = NULL;
> +
> +static struct func *
> +get_built_in_function(const char *name)
> +{
> + uint32_t len = strlen(name);
> + mh_int_t func = mh_strnptr_find_inp(built_in_functions, name, len);
> + if (func == mh_end(built_in_functions))
> + return NULL;
> + return (struct func *) mh_strnptr_node(built_in_functions, func)->val;
6. In the new code we do not use whitespaces after unary
operators like cast. But even better - in C you do not need
an explicit cast from void * to any other pointer.
> +}
> +
> +static int
> +built_in_functions_cache_insert(struct func *func)
7. Please, make the hash methods have the same prefix. For instance,
sql_builtin_func_get(), sql_builtin_func_put().
> +{
> + const char *name = func->def->name;
> + uint32_t len = strlen(name);
> + assert(get_built_in_function(name) == NULL);
> +
> + uint32_t hash = mh_strn_hash(name, len);
> + const struct mh_strnptr_node_t strnode = {name, len, hash, func};
> + mh_int_t k = mh_strnptr_put(built_in_functions, &strnode, NULL, NULL);
> + if (k == mh_end(built_in_functions)) {
> + diag_set(OutOfMemory, sizeof(strnode), "malloc", "strnode");
> + return -1;
> + }
> + return 0;
> +}
> @@ -2657,6 +2685,67 @@ static struct {
>
> static struct func_vtab func_sql_builtin_vtab;
>
> +int
> +sql_built_in_functions_cache_init(void)
> +{
> + built_in_functions = mh_strnptr_new();
8. It might make sense to check the result for NULL and call
panic() if it is.
> + for (uint32_t i = 0; i < nelem(sql_builtins); ++i) {
> + const char *name = sql_builtins[i].name;
> + uint32_t len = strlen(name);
> + uint32_t size = sizeof(struct func_def) + len + 1;
> + struct func_def *def = malloc(size);
9. Ditto.
> + def->fid = i;
> + def->uid = 1;
> + def->body = NULL;
> + def->comment = NULL;
> + def->setuid = true;
> + def->is_deterministic = sql_builtins[i].is_deterministic;
> + def->is_sandboxed = false;
> + def->param_count = sql_builtins[i].param_count;
> + def->returns = sql_builtins[i].returns;
> + def->aggregate = sql_builtins[i].aggregate;
> + def->language = FUNC_LANGUAGE_SQL_BUILTIN;
> + def->name_len = len;
> + def->exports.sql = sql_builtins[i].export_to_sql;
> + func_opts_create(&def->opts);
> + memcpy(def->name, name, len + 1);
> +
> + struct func_sql_builtin *func = malloc(sizeof(*func));
> + if (func == NULL) {
> + diag_set(OutOfMemory, sizeof(*func), "malloc", "func");
> + return -1;
10. The result of sql_built_in_functions_cache_init() is never checked.
I would suggest to make it void and panic on all errors.
> + }
> +
> + func->base.def = def;
> + func->base.vtab = &func_sql_builtin_vtab;
> + credentials_create_empty(&func->base.owner_credentials);
> + memset(func->base.access, 0, sizeof(func->base.access));
> +
> + func->flags = sql_builtins[i].flags;
> + func->call = sql_builtins[i].call;
> + func->finalize = sql_builtins[i].finalize;
> + if (built_in_functions_cache_insert(&func->base) != 0)
> + return -1;
> + }
> + return 0;
> +}
> +
> +void
> +sql_built_in_functions_cache_free(void)
> +{
> + if (built_in_functions == NULL)
> + return;
> + for (uint32_t i = 0; i < nelem(sql_builtins); ++i) {
> + const char *name = sql_builtins[i].name;
> + uint32_t len = strlen(name);
> + mh_int_t k = mh_strnptr_find_inp(built_in_functions, name, len);
> + if (k == mh_end(built_in_functions))
> + continue;
> + mh_strnptr_del(built_in_functions, k, NULL);
11. Perhaps a more 'canonical' way would be like
while (mh_size() > 0) { mh_del(mh_first()); ... }
Also you didn't free the function objects.
> + }
> + mh_strnptr_delete(built_in_functions);
> +}
More information about the Tarantool-patches
mailing list