From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 174886EC41; Thu, 19 Aug 2021 15:03:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 174886EC41 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629374638; bh=zgzNdDX9JjF5Qei4r9rkWQm3NomAOpthArxCEvMB92M=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=fVc/kJdP15gLS2Boum5lRR9/hOY0az/Ah4z1i3KIUZwCGiRzmtqLglg/gH9T3yE+x jbxnD+ZyDskrLcc/uCvOo3vuWxHkSndXuw/KLmYQY1ED7i/B3faL+HBAT2O+Npyga8 Y0U7PQS4BgOKRZZNR6qj/qNy6T6W586J45YgpIiY= Received: from smtpng2.i.mail.ru (smtpng2.i.mail.ru [94.100.179.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 6029B6EC41 for ; Thu, 19 Aug 2021 15:03:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 6029B6EC41 Received: by smtpng2.m.smailru.net with esmtpa (envelope-from ) id 1mGgl9-0000wx-0m; Thu, 19 Aug 2021 15:02:59 +0300 To: kyukhin@tarantool.org, v.ioffe@tarantool.org Cc: tarantool-patches@dev.tarantool.org Date: Thu, 19 Aug 2021 15:02:58 +0300 Message-Id: <70f490650d65b2a14dcda4dfe8a9d5a37df2c7b8.1629374448.git.imeevma@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD9D5AC6413C25DCF08CC98B8FCC5CD86F3182A05F538085040856C66EF2277D90021A5DCC54B20AB895DA83AD0BFAAF0D261064A7B5E6B0F49 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7EC03CD774A575BC5EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637835928C62272F24E8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D84D91DA264051B8A87BE44771F221817B117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB231DD303D21008E29813377AFFFEAFD269A417C69337E82CC2E827F84554CEF50127C277FBC8AE2E8BA83251EDC214901ED5E8D9A59859A8B6300D3B61E77C8D3B089D37D7C0E48F6C5571747095F342E88FB05168BE4CE3AF X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CF160826E4E1956AE0A4C61D1297D5042ECACC84F0493F8C09C2B6934AE262D3EE7EAB7254005DCED114C52B35DBB74F4E7EAB7254005DCEDA5DF9383870C0FED1E0A4E2319210D9B64D260DF9561598F01A9E91200F654B034E77992DA01BB408E8E86DC7131B365E7726E8460B7C23C X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34505665BFD4C707055B951421D187677763ABC4D9C373E79D7368D09812C04F96A15963B2F30150AF1D7E09C32AA3244C989EAD6C68BC7CB840A1B6022B35B5CD3E8609A02908F271FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojGSxK+6r6oBG0q1k7QW16KQ== X-Mailru-Sender: 689FA8AB762F7393C37E3C1AEC41BA5DC6899A2599B89233E74A38C8583D342E83D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: [Tarantool-patches] [PATCH v1 2/9] sql: rework SQL built-in functions hash table X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mergen Imeev via Tarantool-patches Reply-To: imeevma@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" 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