From: Mergen Imeev via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: kyukhin@tarantool.org, v.ioffe@tarantool.org Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH v1 2/9] sql: rework SQL built-in functions hash table Date: Thu, 19 Aug 2021 15:02:58 +0300 [thread overview] Message-ID: <70f490650d65b2a14dcda4dfe8a9d5a37df2c7b8.1629374448.git.imeevma@gmail.com> (raw) In-Reply-To: <cover.1629374448.git.imeevma@gmail.com> Currently, the SQL built-in hash table contains one already defined implementation for each SQL built-in function. This is rather inconvenient because some built-in SQL functions can accept arguments of more than one type, and the type of the result can depend on the types of the arguments. In addition, the number of arguments can be variable for some built-in SQL functions. For these reasons, we are forced to check the number of arguments and their type at runtime. To make it possible to check types of arguments and their number during parsing, the hash table has been modified so that functions can now have more than one implementation. Part of #6105 --- src/box/sql/func.c | 941 ++++++++++-------------------------------- src/box/sql/resolve.c | 44 +- src/box/sql/sqlInt.h | 9 + 3 files changed, 255 insertions(+), 739 deletions(-) diff --git a/src/box/sql/func.c b/src/box/sql/func.c index 90359a23a..76c8831f4 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -51,6 +51,7 @@ #include "assoc.h" static struct mh_strnptr_t *built_in_functions = NULL; +static struct func_sql_builtin **functions; static const unsigned char * mem_as_ustr(struct Mem *mem) @@ -1898,689 +1899,172 @@ sql_builtin_stub(sql_context *ctx, int argc, sql_value **argv) } /** - * A sequence of SQL builtins definitions in - * lexicographic order. + * A structure that defines the relationship between a function and its + * implementations. */ -static struct { + struct sql_func_dictionary { + /** Name of the function. */ + const char *name; + /** The minimum number of arguments for all implementations. */ + int32_t argc_min; + /** The maximum number of arguments for all implementations. */ + int32_t argc_max; + /** Additional informations about the function. */ + uint32_t flags; /** - * Name is used to find corresponding entry in array - * sql_builtins applying binary search. + * True if the function is deterministic (can give only one result with + * the given arguments). */ + bool is_deterministic; + /** Count of function's implementations. */ + uint32_t count; + /** Array of function implementations. */ + struct func_sql_builtin **functions; +}; + +static struct sql_func_dictionary dictionaries[] = { + {"ABS", 1, 1, 0, true, 0, NULL}, + {"AVG", 1, 1, SQL_FUNC_AGG, false, 0, NULL}, + {"CHAR", 0, SQL_MAX_FUNCTION_ARG, 0, true, 0, NULL}, + {"CHARACTER_LENGTH", 1, 1, 0, true, 0, NULL}, + {"CHAR_LENGTH", 1, 1, 0, true, 0, NULL}, + {"COALESCE", 2, SQL_MAX_FUNCTION_ARG, SQL_FUNC_COALESCE, true, 0, NULL}, + {"COUNT", 0, 1, SQL_FUNC_AGG, false, 0, NULL}, + {"GREATEST", 2, SQL_MAX_FUNCTION_ARG, SQL_FUNC_MAX | SQL_FUNC_NEEDCOLL, + true, 0, NULL}, + {"GROUP_CONCAT", 1, 2, SQL_FUNC_AGG, false, 0, NULL}, + {"HEX", 1, 1, 0, true, 0, NULL}, + {"IFNULL", 2, 2, SQL_FUNC_COALESCE, true, 0, NULL}, + {"LEAST", 2, SQL_MAX_FUNCTION_ARG, SQL_FUNC_MIN | SQL_FUNC_NEEDCOLL, + true, 0, NULL}, + {"LENGTH", 1, 1, SQL_FUNC_LENGTH, true, 0, NULL}, + {"LIKE", 2, 3, SQL_FUNC_LIKE | SQL_FUNC_NEEDCOLL, true, 0, NULL}, + {"LIKELIHOOD", 2, 2, SQL_FUNC_UNLIKELY, true, 0, NULL}, + {"LIKELY", 1, 1, SQL_FUNC_UNLIKELY, true, 0, NULL}, + {"LOWER", 1, 1, SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, true, 0, + NULL}, + {"MAX", 1, 1, SQL_FUNC_MAX | SQL_FUNC_AGG | SQL_FUNC_NEEDCOLL, false, 0, + NULL}, + {"MIN", 1, 1, SQL_FUNC_MIN | SQL_FUNC_AGG | SQL_FUNC_NEEDCOLL, false, 0, + NULL}, + {"NULLIF", 2, 2, SQL_FUNC_NEEDCOLL, true, 0, NULL}, + {"POSITION", 2, 2, SQL_FUNC_NEEDCOLL, true, 0, NULL}, + {"PRINTF", 0, SQL_MAX_FUNCTION_ARG, 0, true, 0, NULL}, + {"QUOTE", 1, 1, 0, true, 0, NULL}, + {"RANDOM", 0, 0, 0, false, 0, NULL}, + {"RANDOMBLOB", 1, 1, 0, false, 0, NULL}, + {"REPLACE", 3, 3, SQL_FUNC_DERIVEDCOLL, true, 0, NULL}, + {"ROUND", 1, 2, 0, true, 0, NULL}, + {"ROW_COUNT", 0, 0, 0, true, 0, NULL}, + {"SOUNDEX", 1, 1, 0, true, 0, NULL}, + {"SUBSTR", 2, 3, SQL_FUNC_DERIVEDCOLL, true, 0, NULL}, + {"SUM", 1, 1, SQL_FUNC_AGG, false, 0, NULL}, + {"TOTAL", 1, 1, SQL_FUNC_AGG, false, 0, NULL}, + {"TRIM", 2, 3, SQL_FUNC_DERIVEDCOLL, true, 0, NULL}, + {"TYPEOF", 1, 1, SQL_FUNC_TYPEOF, true, 0, NULL}, + {"UNICODE", 1, 1, 0, true, 0, NULL}, + {"UNLIKELY", 1, 1, SQL_FUNC_UNLIKELY, true, 0, NULL}, + {"UPPER", 1, 1, SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, true, 0, + NULL}, + {"UUID", 0, 1, 0, false, 0, NULL}, + {"VERSION", 0, 0, 0, true, 0, NULL}, + {"ZEROBLOB", 1, 1, 0, true, 0, NULL}, +}; + +/** + * The structure that defines the implementation of the function. These + * definitions are used during initialization to create all descibed + * implementations of all built-in SQL functions. + */ +struct sql_func_definition { + /** Name of the function. */ const char *name; - /** Members below are related to struct func_sql_builtin. */ - uint16_t flags; + /** The number of arguments of the implementation. */ + int32_t argc; + /** + * Types of implementation arguments. Only the first three arguments are + * described, but this should be sufficient, since all built-in SQL + * functions either have up to three arguments, or the number of their + * arguments is not limited here (but limited globally). If a function + * has an unlimited number of arguments, all arguments are of the same + * type. + */ + enum field_type argt[3]; + /** Type of the result of the implementation. */ + enum field_type result; + /** Call implementation with given arguments. */ void (*call)(sql_context *ctx, int argc, sql_value **argv); + /** Call finalization function for this implementation. */ void (*finalize)(sql_context *ctx); - /** Members below are related to struct func_def. */ - bool is_deterministic; - int param_count; - enum field_type returns; - enum func_aggregate aggregate; - bool export_to_sql; -} sql_builtins[] = { - {.name = "ABS", - .param_count = 1, - .returns = FIELD_TYPE_NUMBER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = absFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "AVG", - .param_count = 1, - .returns = FIELD_TYPE_NUMBER, - .is_deterministic = false, - .aggregate = FUNC_AGGREGATE_GROUP, - .flags = 0, - .call = sum_step, - .finalize = avgFinalize, - .export_to_sql = true, - }, { - .name = "CEIL", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "CEILING", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "CHAR", - .param_count = -1, - .returns = FIELD_TYPE_STRING, - .is_deterministic = true, - .aggregate = FUNC_AGGREGATE_NONE, - .flags = 0, - .call = charFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "CHARACTER_LENGTH", - .param_count = 1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = lengthFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "CHAR_LENGTH", - .param_count = 1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = lengthFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "COALESCE", - .param_count = -1, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_COALESCE, - .call = sql_builtin_stub, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "COUNT", - .param_count = -1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = 0, - .call = countStep, - .finalize = countFinalize, - .export_to_sql = true, - }, { - .name = "CURRENT_DATE", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "CURRENT_TIME", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "CURRENT_TIMESTAMP", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "DATE", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "DATETIME", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "EVERY", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "EXISTS", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "EXP", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "EXTRACT", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "FLOOR", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "GREATER", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "GREATEST", - .param_count = -1, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX, - .call = minmaxFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "GROUP_CONCAT", - .param_count = -1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = 0, - .call = groupConcatStep, - .finalize = groupConcatFinalize, - .export_to_sql = true, - }, { - .name = "HEX", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = hexFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "IFNULL", - .param_count = 2, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_COALESCE, - .call = sql_builtin_stub, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "JULIANDAY", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "LEAST", - .param_count = -1, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN, - .call = minmaxFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "LENGTH", - .param_count = 1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_LENGTH, - .call = lengthFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "LESSER", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "LIKE", - .param_count = -1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_LIKE, - .call = likeFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "LIKELIHOOD", - .param_count = 2, - .returns = FIELD_TYPE_BOOLEAN, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_UNLIKELY, - .call = sql_builtin_stub, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "LIKELY", - .param_count = 1, - .returns = FIELD_TYPE_BOOLEAN, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_UNLIKELY, - .call = sql_builtin_stub, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "LN", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "LOWER", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, - .call = LowerICUFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "MAX", - .param_count = 1, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MAX, - .call = minmaxStep, - .finalize = minMaxFinalize, - .export_to_sql = true, - }, { - .name = "MIN", - .param_count = 1, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = SQL_FUNC_NEEDCOLL | SQL_FUNC_MIN, - .call = minmaxStep, - .finalize = minMaxFinalize, - .export_to_sql = true, - }, { - .name = "MOD", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "NULLIF", - .param_count = 2, - .returns = FIELD_TYPE_SCALAR, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_NEEDCOLL, - .call = nullifFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "OCTET_LENGTH", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "POSITION", - .param_count = 2, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_NEEDCOLL, - .call = position_func, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "POWER", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "PRINTF", - .param_count = -1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = printfFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "QUOTE", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = quoteFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "RANDOM", - .param_count = 0, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .call = randomFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "RANDOMBLOB", - .param_count = 1, - .returns = FIELD_TYPE_VARBINARY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .call = randomBlob, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "REPLACE", - .param_count = 3, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_DERIVEDCOLL, - .call = replaceFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "ROUND", - .param_count = -1, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = roundFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "ROW_COUNT", - .param_count = 0, - .returns = FIELD_TYPE_INTEGER, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = sql_row_count, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "SOME", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "SOUNDEX", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = soundexFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "SQRT", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "STRFTIME", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "SUBSTR", - .param_count = -1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_DERIVEDCOLL, - .call = substrFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "SUM", - .param_count = 1, - .returns = FIELD_TYPE_NUMBER, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = 0, - .call = sum_step, - .finalize = sumFinalize, - .export_to_sql = true, - }, { - .name = "TIME", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "TOTAL", - .param_count = 1, - .returns = FIELD_TYPE_NUMBER, - .aggregate = FUNC_AGGREGATE_GROUP, - .is_deterministic = false, - .flags = 0, - .call = sum_step, - .finalize = totalFinalize, - .export_to_sql = true, - }, { - .name = "TRIM", - .param_count = -1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_DERIVEDCOLL, - .call = trim_func, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "TYPEOF", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_TYPEOF, - .call = typeofFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "UNICODE", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = unicodeFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "UNLIKELY", - .param_count = 1, - .returns = FIELD_TYPE_BOOLEAN, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_UNLIKELY, - .call = sql_builtin_stub, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "UPPER", - .param_count = 1, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = SQL_FUNC_DERIVEDCOLL | SQL_FUNC_NEEDCOLL, - .call = UpperICUFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "UUID", - .param_count = -1, - .returns = FIELD_TYPE_UUID, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .call = sql_func_uuid, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "VERSION", - .param_count = 0, - .returns = FIELD_TYPE_STRING, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = sql_func_version, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "ZEROBLOB", - .param_count = 1, - .returns = FIELD_TYPE_VARBINARY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = true, - .flags = 0, - .call = zeroblobFunc, - .finalize = NULL, - .export_to_sql = true, - }, { - .name = "_sql_stat_get", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "_sql_stat_init", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, { - .name = "_sql_stat_push", - .call = sql_builtin_stub, - .export_to_sql = false, - .param_count = -1, - .returns = FIELD_TYPE_ANY, - .aggregate = FUNC_AGGREGATE_NONE, - .is_deterministic = false, - .flags = 0, - .finalize = NULL, - }, }; -static struct func * +/** + * Array of function implementation definitions. All implementations of the same + * function should be defined in succession. + */ +static struct sql_func_definition definitions[] = { + {"ABS", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_NUMBER, absFunc, NULL}, + {"AVG", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_NUMBER, sum_step, avgFinalize}, + {"CHAR", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, charFunc, NULL}, + {"CHAR_LENGTH", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, lengthFunc, + NULL}, + {"COALESCE", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, sql_builtin_stub, + NULL}, + {"COUNT", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, countStep, + countFinalize}, + {"GREATEST", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, minmaxFunc, NULL}, + {"GROUP_CONCAT", -1, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, + FIELD_TYPE_STRING, groupConcatStep, groupConcatFinalize}, + {"HEX", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, hexFunc, NULL}, + {"IFNULL", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, + sql_builtin_stub, NULL}, + {"LEAST", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, minmaxFunc, NULL}, + {"LENGTH", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, lengthFunc, NULL}, + {"LIKE", -1, {FIELD_TYPE_ANY, FIELD_TYPE_ANY, FIELD_TYPE_ANY}, + FIELD_TYPE_INTEGER, likeFunc, NULL}, + {"LIKELIHOOD", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_BOOLEAN, + sql_builtin_stub, NULL}, + {"LIKELY", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_BOOLEAN, sql_builtin_stub, + NULL}, + {"LOWER", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, LowerICUFunc, NULL}, + {"MAX", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, minmaxStep, + minMaxFinalize}, + {"MIN", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, minmaxStep, + minMaxFinalize}, + {"NULLIF", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_SCALAR, + nullifFunc, NULL}, + {"POSITION", 2, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, + position_func, NULL}, + {"PRINTF", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, printfFunc, NULL}, + {"QUOTE", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, quoteFunc, NULL}, + {"RANDOM", 0, {}, FIELD_TYPE_INTEGER, randomFunc, NULL}, + {"RANDOMBLOB", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_VARBINARY, randomBlob, + NULL}, + {"REPLACE", 3, {FIELD_TYPE_ANY, FIELD_TYPE_ANY, FIELD_TYPE_ANY}, + FIELD_TYPE_STRING, replaceFunc, NULL}, + {"ROUND", -1, {FIELD_TYPE_ANY, FIELD_TYPE_ANY}, FIELD_TYPE_INTEGER, + roundFunc, NULL}, + {"ROW_COUNT", 0, {}, FIELD_TYPE_INTEGER, sql_row_count, NULL}, + {"SOUNDEX", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, soundexFunc, NULL}, + {"SUBSTR", -1, {FIELD_TYPE_ANY, FIELD_TYPE_ANY, FIELD_TYPE_ANY}, + FIELD_TYPE_STRING, substrFunc, NULL}, + {"SUM", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_NUMBER, sum_step, sumFinalize}, + {"TOTAL", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_NUMBER, sum_step, + totalFinalize}, + {"TRIM", -1, {FIELD_TYPE_ANY, FIELD_TYPE_ANY, FIELD_TYPE_ANY}, + FIELD_TYPE_STRING, trim_func, NULL}, + {"TYPEOF", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, typeofFunc, NULL}, + {"UNICODE", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, unicodeFunc, NULL}, + {"UNLIKELY", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_BOOLEAN, sql_builtin_stub, + NULL}, + {"UPPER", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_STRING, UpperICUFunc, NULL}, + {"UUID", -1, {FIELD_TYPE_ANY}, FIELD_TYPE_UUID, sql_func_uuid, NULL}, + {"VERSION", 0, {}, FIELD_TYPE_STRING, sql_func_version, NULL}, + {"ZEROBLOB", 1, {FIELD_TYPE_ANY}, FIELD_TYPE_VARBINARY, zeroblobFunc, + NULL}, +}; + +static struct sql_func_dictionary * built_in_func_get(const char *name) { uint32_t len = strlen(name); @@ -2591,14 +2075,14 @@ built_in_func_get(const char *name) } static void -built_in_func_put(struct func *func) +built_in_func_put(struct sql_func_dictionary *dict) { - const char *name = func->def->name; + const char *name = dict->name; uint32_t len = strlen(name); assert(built_in_func_get(name) == NULL); uint32_t hash = mh_strn_hash(name, len); - const struct mh_strnptr_node_t strnode = {name, len, hash, func}; + const struct mh_strnptr_node_t strnode = {name, len, hash, dict}; mh_int_t k = mh_strnptr_put(built_in_functions, &strnode, NULL, NULL); if (k == mh_end(built_in_functions)) { panic("Out of memory on insertion into SQL built-in functions " @@ -2606,23 +2090,30 @@ built_in_func_put(struct func *func) } } +static struct func * +find_built_in_func(struct Expr *expr, struct sql_func_dictionary *dict) +{ + const char *name = expr->u.zToken; + int n = expr->x.pList != NULL ? expr->x.pList->nExpr : 0; + struct func *func = &dict->functions[0]->base; + assert(func->def->exports.sql); + int param_count = func->def->param_count; + if (param_count != -1 && param_count != n) { + diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name, + tt_sprintf("%d", func->def->param_count), n); + return NULL; + } + return func; +} + struct func * sql_func_find(struct Expr *expr) { const char *name = expr->u.zToken; - int n = expr->x.pList ? expr->x.pList->nExpr : 0; - struct func *func = built_in_func_get(name); - if (func != NULL) { - assert(func->def->exports.sql); - int param_count = func->def->param_count; - if (param_count != -1 && param_count != n) { - diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name, - tt_sprintf("%d", func->def->param_count), n); - return NULL; - } - return func; - } - func = func_by_name(name, strlen(name)); + struct sql_func_dictionary *dict = built_in_func_get(name); + if (dict != NULL) + return find_built_in_func(expr, dict); + struct func *func = func_by_name(name, strlen(name)); if (func == NULL) { diag_set(ClientError, ER_NO_SUCH_FUNCTION, name); return NULL; @@ -2633,6 +2124,7 @@ sql_func_find(struct Expr *expr) name)); return NULL; } + int n = expr->x.pList != NULL ? expr->x.pList->nExpr : 0; if (func->def->param_count != n) { diag_set(ClientError, ER_FUNC_WRONG_ARG_COUNT, name, tt_sprintf("%d", func->def->param_count), n); @@ -2644,14 +2136,10 @@ sql_func_find(struct Expr *expr) uint32_t sql_func_flags(const char *name) { - struct func *func = built_in_func_get(name); - if (func == NULL) + struct sql_func_dictionary *dict = built_in_func_get(name); + if (dict == NULL) return 0; - assert(func->def->language == FUNC_LANGUAGE_SQL_BUILTIN); - uint32_t flags = ((struct func_sql_builtin *)func)->flags; - if (func->def->aggregate == FUNC_AGGREGATE_GROUP) - flags |= SQL_FUNC_AGG; - return flags; + return dict->flags; } static struct func_vtab func_sql_builtin_vtab; @@ -2662,10 +2150,16 @@ sql_built_in_functions_cache_init(void) built_in_functions = mh_strnptr_new(); if (built_in_functions == NULL) panic("Out of memory on creating SQL built-in functions hash"); - for (uint32_t i = 0; i < nelem(sql_builtins); ++i) { - const char *name = sql_builtins[i].name; - if (!sql_builtins[i].export_to_sql) - continue; + for (uint32_t i = 0; i < nelem(dictionaries); ++i) + built_in_func_put(&dictionaries[i]); + + functions = malloc(sizeof(*functions) * nelem(definitions)); + for (uint32_t i = 0; i < nelem(definitions); ++i) { + struct sql_func_definition *desc = &definitions[i]; + const char *name = desc->name; + struct sql_func_dictionary *dict = built_in_func_get(name); + assert(dict != NULL); + uint32_t len = strlen(name); uint32_t size = sizeof(struct func_def) + len + 1; struct func_def *def = malloc(size); @@ -2676,14 +2170,16 @@ sql_built_in_functions_cache_init(void) def->body = NULL; def->comment = NULL; def->setuid = true; - def->is_deterministic = sql_builtins[i].is_deterministic; + def->is_deterministic = dict->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; + assert(desc->argc != -1 || dict->argc_min != dict->argc_max); + def->param_count = desc->argc; + def->returns = desc->result; + def->aggregate = desc->finalize == NULL ? + FUNC_AGGREGATE_NONE : FUNC_AGGREGATE_GROUP; def->language = FUNC_LANGUAGE_SQL_BUILTIN; def->name_len = len; - def->exports.sql = sql_builtins[i].export_to_sql; + def->exports.sql = true; func_opts_create(&def->opts); memcpy(def->name, name, len + 1); @@ -2696,11 +2192,26 @@ sql_built_in_functions_cache_init(void) 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; - built_in_func_put(&func->base); + func->param_list = desc->argt; + func->flags = dict->flags; + func->call = desc->call; + func->finalize = desc->finalize; + functions[i] = func; + assert(dict->count == 0 || dict->functions != NULL); + if (dict->functions == NULL) + dict->functions = &functions[i]; + ++dict->count; } + /* + * Initialization of CHARACTER_LENGTH() function, which is actually + * another name for CHAR_LENGTH(). + */ + const char *name = "CHARACTER_LENGTH"; + struct sql_func_dictionary *dict = built_in_func_get(name); + name = "CHAR_LENGTH"; + struct sql_func_dictionary *dict_original = built_in_func_get(name); + dict->count = dict_original->count; + dict->functions = dict_original->functions; } void @@ -2708,15 +2219,15 @@ 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; + for (uint32_t i = 0; i < nelem(definitions); ++i) + func_delete(&functions[i]->base); + for (uint32_t i = 0; i < nelem(dictionaries); ++i) { + const char *name = dictionaries[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; - struct func *func = mh_strnptr_node(built_in_functions, k)->val; mh_strnptr_del(built_in_functions, k, NULL); - func_delete(func); } assert(mh_size(built_in_functions) == 0); mh_strnptr_delete(built_in_functions); diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 35faddab5..21fe124d7 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -598,19 +598,9 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) assert(!ExprHasProperty(pExpr, EP_xIsSelect)); zId = pExpr->u.zToken; nId = sqlStrlen30(zId); - struct func *func = sql_func_find(pExpr); - if (func == NULL) { - pParse->is_aborted = true; - pNC->nErr++; - return WRC_Abort; - } - bool is_agg = func->def->aggregate == - FUNC_AGGREGATE_GROUP; - assert(!is_agg || func->def->language == - FUNC_LANGUAGE_SQL_BUILTIN); - pExpr->type = func->def->returns; - if (sql_func_flag_is_set(func, SQL_FUNC_UNLIKELY) && - n == 2) { + uint32_t flags = sql_func_flags(zId); + bool is_agg = (flags & SQL_FUNC_AGG) != 0; + if ((flags & SQL_FUNC_UNLIKELY) != 0 && n == 2) { ExprSetProperty(pExpr, EP_Unlikely | EP_Skip); pExpr->iTable = exprProbability(pList->a[1].pExpr); @@ -623,20 +613,15 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) pNC->nErr++; return WRC_Abort; } - } else if (sql_func_flag_is_set(func, - SQL_FUNC_UNLIKELY)) { + } else if ((flags & SQL_FUNC_UNLIKELY) != 0) { ExprSetProperty(pExpr, EP_Unlikely | EP_Skip); /* * unlikely() probability is * 0.0625, likely() is 0.9375 */ - pExpr->iTable = func->def->name[0] == 'u' ? + pExpr->iTable = zId[0] == 'u' ? 8388608 : 125829120; } - assert(!func->def->is_deterministic || - (pNC->ncFlags & NC_IdxExpr) == 0); - if (func->def->is_deterministic) - ExprSetProperty(pExpr, EP_ConstFunc); if (is_agg && (pNC->ncFlags & NC_AllowAgg) == 0) { const char *err = tt_sprintf("misuse of aggregate "\ @@ -649,6 +634,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) if (is_agg) pNC->ncFlags &= ~NC_AllowAgg; sqlWalkExprList(pWalker, pList); + if (pParse->is_aborted) + break; if (is_agg) { NameContext *pNC2 = pNC; pExpr->op = TK_AGG_FUNCTION; @@ -661,16 +648,25 @@ resolveExprStep(Walker * pWalker, Expr * pExpr) pExpr->op2++; pNC2 = pNC2->pNext; } - assert(func != NULL); if (pNC2) { pNC2->ncFlags |= NC_HasAgg; - if (sql_func_flag_is_set(func, - SQL_FUNC_MIN | - SQL_FUNC_MAX)) + if ((flags & (SQL_FUNC_MIN | + SQL_FUNC_MAX)) != 0) pNC2->ncFlags |= NC_MinMaxAgg; } pNC->ncFlags |= NC_AllowAgg; } + struct func *func = sql_func_find(pExpr); + if (func == NULL) { + pParse->is_aborted = true; + pNC->nErr++; + return WRC_Abort; + } + pExpr->type = func->def->returns; + assert(!func->def->is_deterministic || + (pNC->ncFlags & NC_IdxExpr) == 0); + if (func->def->is_deterministic) + ExprSetProperty(pExpr, EP_ConstFunc); return WRC_Prune; } case TK_SELECT: diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h index e68b5297b..35dee3ec1 100644 --- a/src/box/sql/sqlInt.h +++ b/src/box/sql/sqlInt.h @@ -4355,6 +4355,15 @@ struct func_sql_builtin { struct func base; /** A bitmask of SQL flags. */ uint16_t flags; + /** + * Description of the types of implementation arguments. Up to three + * arguments are described, but this should be sufficient, since all + * built-in SQL functions either have up to three arguments, or the + * number of their arguments is not limited here (but limited globally). + * If a function has an unlimited number of arguments, all arguments are + * of the same type. + */ + enum field_type *param_list; /** * A VDBE-memory-compatible call method. * SQL built-ins don't use func base class "call" -- 2.25.1
next prev parent reply other threads:[~2021-08-19 12:03 UTC|newest] Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-08-19 12:02 [Tarantool-patches] [PATCH v1 0/9] Check types of SQL built-in functions arguments Mergen Imeev via Tarantool-patches 2021-08-19 12:02 ` [Tarantool-patches] [PATCH v1 1/9] sql: modify signature of TRIM() Mergen Imeev via Tarantool-patches 2021-08-19 12:02 ` Mergen Imeev via Tarantool-patches [this message] 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 3/9] sql: check number of arguments during parsing Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 4/9] sql: static type check for SQL built-in functions Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 5/9] sql: runtime " Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 6/9] sql: enable types checking for some functions Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 7/9] sql: fix result type of min() and max() functions Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 8/9] sql: check argument types of sum(), avg(), total() Mergen Imeev via Tarantool-patches 2021-08-19 12:03 ` [Tarantool-patches] [PATCH v1 9/9] sql: arguments check for string value functions Mergen Imeev via Tarantool-patches 2021-08-19 12:26 ` [Tarantool-patches] [PATCH v1 0/9] Check types of SQL built-in functions arguments Kirill Yukhin via Tarantool-patches 2021-08-19 15:50 ` Vitaliia Ioffe via Tarantool-patches 2021-08-19 16:16 ` Kirill Yukhin via Tarantool-patches
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=70f490650d65b2a14dcda4dfe8a9d5a37df2c7b8.1629374448.git.imeevma@gmail.com \ --to=tarantool-patches@dev.tarantool.org \ --cc=imeevma@tarantool.org \ --cc=kyukhin@tarantool.org \ --cc=v.ioffe@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH v1 2/9] sql: rework SQL built-in functions hash table' \ /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