[tarantool-patches] Re: [PATCH v2 10/12] sql: refactor builtins signatures with port
Konstantin Osipov
kostja at tarantool.org
Wed Jul 10 21:47:13 MSK 2019
* Kirill Shcherbatov <kshcherbatov at tarantool.org> [19/07/10 14:02]:
> Reworked SQL builtins signatures to use args and ret abstract
> ports to pass arguments and return the execution result.
> The new approach allows to support calling sql builtins functions
> from Lua in future and to use uniform func_call API in
> OP_Function opcode.
I don't know why you ignored my comment on telegram: I think this
is a non-goal and SQL functions don't have to use _func api for
calls. It is a performance issue as well, since obstructs possible JITting
of expressions. Did you discuss this with Nikita as I asked you on
telegram?
>
> Needed for #4113, #2200, #2233
> ---
> src/box/call.c | 1 +
> src/box/execute.c | 2 +
> src/box/lua/call.c | 2 +
> src/box/lua/lua_sql.c | 41 +-
> src/box/port.c | 2 +
> src/box/port.h | 19 +
> src/box/sql/analyze.c | 130 ++--
> src/box/sql/func.c | 1400 +++++++++++++++++++++++++----------------
> src/box/sql/main.c | 58 +-
> src/box/sql/printf.c | 7 +-
> src/box/sql/sqlInt.h | 101 +--
> src/box/sql/vdbe.c | 101 ++-
> src/box/sql/vdbeInt.h | 18 +-
> src/box/sql/vdbeapi.c | 163 +----
> src/box/sql/vdbemem.c | 51 +-
> src/lib/core/port.h | 31 +
> 16 files changed, 1197 insertions(+), 930 deletions(-)
>
> diff --git a/src/box/call.c b/src/box/call.c
> index ac2bf3004..b459a9839 100644
> --- a/src/box/call.c
> +++ b/src/box/call.c
> @@ -73,6 +73,7 @@ static const struct port_vtab port_msgpack_vtab = {
> .dump_lua = port_msgpack_dump_lua,
> .dump_plain = NULL,
> .get_msgpack = port_msgpack_get_msgpack,
> + .get_vdbemem = NULL,
> .destroy = NULL,
> };
>
> diff --git a/src/box/execute.c b/src/box/execute.c
> index a9ca2e67a..0dc1bc46d 100644
> --- a/src/box/execute.c
> +++ b/src/box/execute.c
> @@ -109,6 +109,8 @@ const struct port_vtab port_sql_vtab = {
> /* .dump_lua = */ port_sql_dump_lua,
> /* .dump_plain = */ NULL,
> /* .get_msgpack = */ NULL,
> + /* .get_vdbemem = */ NULL,
> + /* .get_context = */ NULL,
> /* .destroy = */ port_sql_destroy,
> };
>
> diff --git a/src/box/lua/call.c b/src/box/lua/call.c
> index 95fac4834..4297218cd 100644
> --- a/src/box/lua/call.c
> +++ b/src/box/lua/call.c
> @@ -507,6 +507,8 @@ static const struct port_vtab port_lua_vtab = {
> .dump_lua = port_lua_dump_lua,
> .dump_plain = port_lua_dump_plain,
> .get_msgpack = port_lua_get_msgpack,
> + .get_vdbemem = NULL,
> + .get_context = NULL,
> .destroy = port_lua_destroy,
> };
>
> diff --git a/src/box/lua/lua_sql.c b/src/box/lua/lua_sql.c
> index 4d90a53de..0c5797fa2 100644
> --- a/src/box/lua/lua_sql.c
> +++ b/src/box/lua/lua_sql.c
> @@ -34,6 +34,7 @@
>
> #include "box/lua/call.h"
> #include "box/sql/sqlInt.h"
> +#include "box/port.h"
> #include "box/sql/vdbeInt.h"
>
> struct lua_sql_func_info {
> @@ -47,15 +48,28 @@ struct lua_sql_func_info {
> * Lua func should be previously registered in sql
> * (see lbox_sql_create_function).
> */
> -static void
> -lua_sql_call(sql_context *pCtx, int nVal, sql_value **apVal) {
> +static int
> +lua_sql_call(struct func *func, struct port *args, struct port *ret)
> +{
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> lua_State *L = lua_newthread(tarantool_L);
> int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX);
> - struct lua_sql_func_info *func_info = sql_user_data(pCtx);
> + struct lua_sql_func_info *func_info = sql_user_data(ctx);
>
> lua_rawgeti(L, LUA_REGISTRYINDEX, func_info->func_ref);
> - for (int i = 0; i < nVal; i++) {
> - sql_value *param = apVal[i];
> + for (uint32_t i = 0; i < argc; i++) {
> + sql_value *param = (sql_value *)&argv[i];
> switch (sql_value_type(param)) {
> case MP_INT:
> luaL_pushint64(L, sql_value_int64(param));
> @@ -79,38 +93,37 @@ lua_sql_call(sql_context *pCtx, int nVal, sql_value **apVal) {
> default:
> diag_set(ClientError, ER_SQL_EXECUTE, "Unsupported "\
> "type passed to Lua");
> - pCtx->is_aborted = true;
> goto error;
> }
> }
> if (lua_pcall(L, lua_gettop(L) - 1, 1, 0) != 0){
> diag_set(ClientError, ER_SQL_EXECUTE, lua_tostring(L, -1));
> - pCtx->is_aborted = true;
> goto error;
> }
> switch(lua_type(L, -1)) {
> case LUA_TBOOLEAN:
> - sql_result_bool(pCtx, lua_toboolean(L, -1));
> + mem_set_bool(val, lua_toboolean(L, -1));
> break;
> case LUA_TNUMBER:
> - sql_result_double(pCtx, lua_tonumber(L, -1));
> + sqlVdbeMemSetDouble(val, lua_tonumber(L, -1));
> break;
> case LUA_TSTRING:
> - sql_result_text(pCtx, lua_tostring(L, -1), -1,
> - SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, lua_tostring(L, -1), -1,
> + 1, SQL_TRANSIENT) != 0)
> + return -1;
> break;
> case LUA_TNIL:
> - sql_result_null(pCtx);
> + sqlVdbeMemSetNull(val);
> break;
> default:
> diag_set(ClientError, ER_SQL_EXECUTE, "Unsupported type "\
> "passed from Lua");
> - pCtx->is_aborted = true;
> goto error;
> }
> + return 0;
> error:
> luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref);
> - return;
> + return -1;
> }
>
> static void
> diff --git a/src/box/port.c b/src/box/port.c
> index 7f552bcfe..9e4ab9453 100644
> --- a/src/box/port.c
> +++ b/src/box/port.c
> @@ -146,5 +146,7 @@ const struct port_vtab port_tuple_vtab = {
> .dump_lua = port_tuple_dump_lua,
> .dump_plain = NULL,
> .get_msgpack = NULL,
> + .get_vdbemem = NULL,
> + .get_context = NULL,
> .destroy = port_tuple_destroy,
> };
> diff --git a/src/box/port.h b/src/box/port.h
> index a7f5d81bd..f14d61189 100644
> --- a/src/box/port.h
> +++ b/src/box/port.h
> @@ -95,6 +95,25 @@ static_assert(sizeof(struct port_msgpack) <= sizeof(struct port),
> void
> port_msgpack_create(struct port *port, const char *data, uint32_t data_sz);
>
> +struct sql_value;
> +struct sql_context;
> +
> +/** Port implementation used with vdbe memory variables. */
> +struct port_vdbemem {
> + const struct port_vtab *vtab;
> + struct sql_context *ctx;
> + struct sql_value *mem;
> + uint32_t size;
> +};
> +
> +static_assert(sizeof(struct port_vdbemem) <= sizeof(struct port),
> + "sizeof(struct port_vdbemem) must be <= sizeof(struct port)");
> +
> +/** Initialize a port to dump data in sql vdbe memory. */
> +void
> +port_vdbemem_create(struct port *base, struct sql_value *mem, uint32_t size,
> + struct sql_context *ctx);
> +
> /** Port for storing the result of a Lua CALL/EVAL. */
> struct port_lua {
> const struct port_vtab *vtab;
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 512904cc1..7764b48c5 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -109,6 +109,7 @@
> #include "box/index.h"
> #include "box/key_def.h"
> #include "box/schema.h"
> +#include "box/port.h"
> #include "third_party/qsort_arg.h"
>
> #include "sqlInt.h"
> @@ -263,9 +264,19 @@ stat4Destructor(void *pOld)
> * return value is BLOB, but it is really just a pointer to the Stat4Accum
> * object.
> */
> -static void
> -statInit(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_stat_init(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> Stat4Accum *p;
> int nCol; /* Number of columns in index being sampled */
> int nKeyCol; /* Number of key columns */
> @@ -275,8 +286,7 @@ statInit(sql_context * context, int argc, sql_value ** argv)
> int mxSample = SQL_STAT4_SAMPLES;
>
> /* Decode the three function arguments */
> - UNUSED_PARAMETER(argc);
> - nCol = sql_value_int(argv[0]);
> + nCol = sql_value_int(&argv[0]);
> assert(nCol > 0);
> /* Tarantool: we use an additional artificial column for the reason
> * that Tarantool's indexes don't contain PK columns after key columns.
> @@ -284,7 +294,7 @@ statInit(sql_context * context, int argc, sql_value ** argv)
> * identical rows, we have to use this artificial column.
> */
> nColUp = sizeof(tRowcnt) < 8 ? (nCol + 2) & ~1 : nCol + 1;
> - nKeyCol = sql_value_int(argv[1]);
> + nKeyCol = sql_value_int(&argv[1]);
> assert(nKeyCol <= nCol);
> assert(nKeyCol > 0);
>
> @@ -295,12 +305,10 @@ statInit(sql_context * context, int argc, sql_value ** argv)
> + sizeof(tRowcnt) * nColUp /* Stat4Accum.anLt */
> + sizeof(Stat4Sample) * (nCol + 1 + mxSample) /* Stat4Accum.aBest[], a[] */
> + sizeof(tRowcnt) * 3 * nColUp * (nCol + 1 + mxSample);
> - db = sql_context_db_handle(context);
> + db = sql_get();
> p = sqlDbMallocZero(db, n);
> - if (p == 0) {
> - context->is_aborted = true;
> - return;
> - }
> + if (p == NULL)
> + return -1;
>
> p->db = db;
> p->nRow = 0;
> @@ -316,12 +324,12 @@ statInit(sql_context * context, int argc, sql_value ** argv)
> p->iGet = -1;
> p->mxSample = mxSample;
> p->nPSample =
> - (tRowcnt) (sql_value_int64(argv[2]) /
> + (tRowcnt) (sql_value_int64(&argv[2]) /
> (mxSample / 3 + 1) + 1);
> p->current.anLt = &p->current.anEq[nColUp];
> p->iPrn =
> 0x689e962d * (u32) nCol ^ 0xd0944565 *
> - (u32) sql_value_int(argv[2]);
> + (u32) sql_value_int(&argv[2]);
>
> /* Set up the Stat4Accum.a[] and aBest[] arrays */
> p->a = (struct Stat4Sample *)&p->current.anLt[nColUp];
> @@ -347,7 +355,8 @@ statInit(sql_context * context, int argc, sql_value ** argv)
> * (given by the 3rd parameter) is never used and can be any positive
> * value.
> */
> - sql_result_blob(context, p, sizeof(*p), stat4Destructor);
> + return sqlVdbeMemSetStr(val, (const char *)p, sizeof(*p), 0,
> + stat4Destructor) != 0 ? -1 : 0;
> }
>
> /*
> @@ -535,16 +544,21 @@ samplePushPrevious(Stat4Accum * p, int iChng)
> *
> * The R parameter is only used for STAT4
> */
> -static void
> -statPush(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_stat_push(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + (void) ret;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> +
> int i;
> /* The three function arguments */
> - Stat4Accum *p = (Stat4Accum *) sql_value_blob(argv[0]);
> - int iChng = sql_value_int(argv[1]);
> + Stat4Accum *p = (Stat4Accum *) sql_value_blob(&argv[0]);
> + int iChng = sql_value_int(&argv[1]);
>
> - UNUSED_PARAMETER(argc);
> - UNUSED_PARAMETER(context);
> assert(p->nCol > 0);
> /* iChng == p->nCol means that the current and previous rows are identical */
> assert(iChng <= p->nCol);
> @@ -569,8 +583,8 @@ statPush(sql_context * context, int argc, sql_value ** argv)
> }
> }
> p->nRow++;
> - sampleSetKey(p->db, &p->current, sql_value_bytes(argv[2]),
> - sql_value_blob(argv[2]));
> + sampleSetKey(p->db, &p->current, sql_value_bytes(&argv[2]),
> + sql_value_blob(&argv[2]));
> p->current.iHash = p->iPrn = p->iPrn * 1103515245 + 12345;
> {
> tRowcnt nLt = p->current.anLt[p->nCol];
> @@ -592,6 +606,7 @@ statPush(sql_context * context, int argc, sql_value ** argv)
> }
> }
> }
> + return 0;
> }
>
> #define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
> @@ -608,12 +623,22 @@ statPush(sql_context * context, int argc, sql_value ** argv)
> * The content to returned is determined by the parameter J
> * which is one of the STAT_GET_xxxx values defined above.
> */
> -static void
> -statGet(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_stat_get(struct func *func, struct port *args, struct port *ret)
> {
> - Stat4Accum *p = (Stat4Accum *) sql_value_blob(argv[0]);
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + struct Stat4Accum *p = (struct Stat4Accum *) sql_value_blob(&argv[0]);
> /* STAT4 have a parameter on this routine. */
> - int eCall = sql_value_int(argv[1]);
> + int eCall = sql_value_int(&argv[1]);
> assert(argc == 2);
> assert(eCall == STAT_GET_STAT1 || eCall == STAT_GET_NEQ
> || eCall == STAT_GET_KEY || eCall == STAT_GET_NLT
> @@ -644,10 +669,8 @@ statGet(sql_context * context, int argc, sql_value ** argv)
> int i;
>
> char *zRet = sqlMallocZero((p->nKeyCol + 1) * 25);
> - if (zRet == 0) {
> - context->is_aborted = true;
> - return;
> - }
> + if (zRet == NULL)
> + return -1;
>
> sql_snprintf(24, zRet, "%llu", (u64) p->nRow);
> z = zRet + sqlStrlen30(zRet);
> @@ -659,8 +682,8 @@ statGet(sql_context * context, int argc, sql_value ** argv)
> assert(p->current.anEq[i]);
> }
> assert(z[0] == '\0' && z > zRet);
> -
> - sql_result_text(context, zRet, -1, sql_free);
> + if (sqlVdbeMemSetStr(val, zRet, -1, 1, SQL_DYNAMIC) != 0)
> + return -1;
> } else if (eCall == STAT_GET_KEY) {
> if (p->iGet < 0) {
> samplePushPrevious(p, 0);
> @@ -668,8 +691,9 @@ statGet(sql_context * context, int argc, sql_value ** argv)
> }
> if (p->iGet < p->nSample) {
> Stat4Sample *pS = p->a + p->iGet;
> - sql_result_blob(context, pS->aKey, pS->nKey,
> - SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, (const char *)pS->aKey,
> + pS->nKey, 0, SQL_TRANSIENT) != 0)
> + return -1;
> }
> } else {
> tRowcnt *aCnt = 0;
> @@ -687,12 +711,11 @@ statGet(sql_context * context, int argc, sql_value ** argv)
> p->iGet++;
> break;
> }
> - }
> + }
>
> - char *zRet = sqlMallocZero(p->nCol * 25);
> - if (zRet == 0) {
> - context->is_aborted = true;
> - } else {
> + char *zRet = sqlMallocZero(p->nCol * 25);
> + if (zRet == NULL)
> + return -1;
> int i;
> char *z = zRet;
> for (i = 0; i < p->nCol; i++) {
> @@ -701,17 +724,15 @@ statGet(sql_context * context, int argc, sql_value ** argv)
> }
> assert(z[0] == '\0' && z > zRet);
> z[-1] = '\0';
> - sql_result_text(context, zRet, -1, sql_free);
> + if (sqlVdbeMemSetStr(val, zRet, -1, 1, SQL_DYNAMIC) != 0)
> + return -1;
> }
> -
> -}
> -#ifndef SQL_DEBUG
> -UNUSED_PARAMETER(argc);
> -#endif
> + return 0;
> }
>
> static void
> -callStatGet(Vdbe * v, int regStat4, int iParam, int regOut)
> +vdbe_emit_analyze_stat_get(struct Vdbe * v, int regStat4, int iParam,
> + int regOut)
> {
> assert(regOut != regStat4 && regOut != regStat4 + 1);
> sqlVdbeAddOp2(v, OP_Integer, iParam, regStat4 + 1);
> @@ -964,7 +985,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
> sqlVdbeChangeP5(v, 3);
> sqlVdbeAddOp2(v, OP_Next, idx_cursor, next_row_addr);
> /* Add the entry to the stat1 table. */
> - callStatGet(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
> + vdbe_emit_analyze_stat_get(v, stat4_reg, STAT_GET_STAT1, stat1_reg);
> enum field_type types[4] = { FIELD_TYPE_STRING,
> FIELD_TYPE_STRING,
> FIELD_TYPE_STRING,
> @@ -982,12 +1003,12 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
> int sample_key_reg = col_reg + part_count;
> parse->nMem = MAX(parse->nMem, col_reg + part_count);
> int next_addr = sqlVdbeCurrentAddr(v);
> - callStatGet(v, stat4_reg, STAT_GET_KEY, sample_key_reg);
> + vdbe_emit_analyze_stat_get(v, stat4_reg, STAT_GET_KEY, sample_key_reg);
> int is_null_addr = sqlVdbeAddOp1(v, OP_IsNull,
> sample_key_reg);
> - callStatGet(v, stat4_reg, STAT_GET_NEQ, eq_reg);
> - callStatGet(v, stat4_reg, STAT_GET_NLT, lt_reg);
> - callStatGet(v, stat4_reg, STAT_GET_NDLT, dlt_reg);
> + vdbe_emit_analyze_stat_get(v, stat4_reg, STAT_GET_NEQ, eq_reg);
> + vdbe_emit_analyze_stat_get(v, stat4_reg, STAT_GET_NLT, lt_reg);
> + vdbe_emit_analyze_stat_get(v, stat4_reg, STAT_GET_NDLT, dlt_reg);
> sqlVdbeAddOp4Int(v, OP_NotFound, tab_cursor, next_addr,
> sample_key_reg, 0);
> /*
> @@ -1752,9 +1773,12 @@ void
> sql_register_analyze_builtins(void)
> {
> static FuncDef funcs[] = {
> - FUNCTION(_sql_stat_get, 2, 0, 0, statGet, FIELD_TYPE_ANY),
> - FUNCTION(_sql_stat_push, 3, 0, 0, statPush, FIELD_TYPE_ANY),
> - FUNCTION(_sql_stat_init, 3, 0, 0, statInit, FIELD_TYPE_ANY),
> + FUNCTION(_sql_stat_get, 2, 0, 0, sql_builtin_stat_get,
> + FIELD_TYPE_ANY),
> + FUNCTION(_sql_stat_push, 3, 0, 0, sql_builtin_stat_push,
> + FIELD_TYPE_ANY),
> + FUNCTION(_sql_stat_init, 3, 0, 0, sql_builtin_stat_init,
> + FIELD_TYPE_ANY),
> };
> sqlInsertBuiltinFuncs(funcs, nelem(funcs));
> }
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index d59aba9ee..18db8530d 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -39,6 +39,7 @@
> #include "version.h"
> #include "coll/coll.h"
> #include "box/func.h"
> +#include "box/port.h"
> #include "tarantoolInt.h"
> #include "box/session.h"
> #include <unicode/ustring.h>
> @@ -71,45 +72,99 @@ sqlSkipAccumulatorLoad(sql_context * context)
> context->skipFlag = 1;
> }
>
> +static const struct port_vtab port_vdbemem_vtab;
> +
> +void
> +port_vdbemem_create(struct port *base, struct sql_value *mem, uint32_t size,
> + struct sql_context *ctx)
> +{
> + struct port_vdbemem *port = (struct port_vdbemem *) base;
> + port->vtab = &port_vdbemem_vtab;
> + port->mem = mem;
> + port->size = size;
> + port->ctx = ctx;
> +}
> +
> +static struct sql_value *
> +port_vdbemem_get_vdbemem(struct port *base, uint32_t *size)
> +{
> + struct port_vdbemem *port = (struct port_vdbemem *) base;
> + assert(port->vtab == &port_vdbemem_vtab);
> + *size = port->size;
> + return port->mem;
> +}
> +
> +void *
> +port_vdbemem_get_context(struct port *base)
> +{
> + struct port_vdbemem *port = (struct port_vdbemem *) base;
> + return port->ctx;
> +}
> +
> +static const struct port_vtab port_vdbemem_vtab = {
> + .dump_msgpack = NULL,
> + .dump_msgpack_16 = NULL,
> + .dump_lua = NULL,
> + .dump_plain = NULL,
> + .get_msgpack = NULL,
> + .get_vdbemem = port_vdbemem_get_vdbemem,
> + .get_context = port_vdbemem_get_context,
> + .destroy = NULL,
> +};
> +
> /*
> * Implementation of the non-aggregate min() and max() functions
> */
> -static void
> -minmaxFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_minmax(struct func *func, struct port *args, struct port *ret)
> {
> - int i;
> - int mask; /* 0 for min() or 0xffffffff for max() */
> - int iBest;
> - struct coll *pColl;
> -
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> assert(argc > 1);
> - mask = sql_user_data(context) == 0 ? 0 : -1;
> - pColl = sqlGetFuncCollSeq(context);
> + /* 0 for min() or 0xffffffff for max() */
> + int mask = sql_user_data(ctx) == 0 ? 0 : -1;
> assert(mask == -1 || mask == 0);
> - iBest = 0;
> - if (sql_value_is_null(argv[0]))
> - return;
> - for (i = 1; i < argc; i++) {
> - if (sql_value_is_null(argv[i]))
> - return;
> - if ((sqlMemCompare(argv[iBest], argv[i], pColl) ^ mask) >=
> - 0) {
> - testcase(mask == 0);
> - iBest = i;
> - }
> + struct coll *coll = sqlGetFuncCollSeq(ctx);
> +
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + int best = 0;
> + if (sql_value_is_null(argv))
> + return 0;
> + for (uint32_t i = 1; i < argc; i++) {
> + if (sql_value_is_null(&argv[i]))
> + return 0;
> + if ((sqlMemCompare(&argv[best], &argv[i], coll) ^ mask) >= 0)
> + best = i;
> }
> - sql_result_value(context, argv[iBest]);
> + return sqlVdbeMemCopy(val, &argv[best]) != 0 ? -1 : 0;
> }
>
> /*
> * Return the type of the argument.
> */
> -static void
> -typeofFunc(sql_context * context, int NotUsed, sql_value ** argv)
> +static int
> +sql_builtin_typeof(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> const char *z = 0;
> - UNUSED_PARAMETER(NotUsed);
> - switch (sql_value_type(argv[0])) {
> + switch (sql_value_type(&argv[0])) {
> case MP_INT:
> z = "integer";
> break;
> @@ -129,40 +184,48 @@ typeofFunc(sql_context * context, int NotUsed, sql_value ** argv)
> z = "null";
> break;
> }
> - sql_result_text(context, z, -1, SQL_STATIC);
> + return sqlVdbeMemSetStr(val, z, -1, 1, SQL_STATIC) != 0 ? -1 : 0;
> }
>
> /*
> * Implementation of the length() function
> */
> -static void
> -lengthFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_length(struct func *func, struct port *args, struct port *ret)
> {
> - int len;
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
>
> assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - switch (sql_value_type(argv[0])) {
> + switch (sql_value_type(&argv[0])) {
> case MP_BIN:
> case MP_INT:
> case MP_DOUBLE:{
> - sql_result_int(context,
> - sql_value_bytes(argv[0]));
> + sqlVdbeMemSetInt64(val, sql_value_bytes(&argv[0]));
> break;
> }
> case MP_STR:{
> - const unsigned char *z = sql_value_text(argv[0]);
> + const unsigned char *z = sql_value_text(&argv[0]);
> if (z == 0)
> - return;
> - len = sql_utf8_char_count(z, sql_value_bytes(argv[0]));
> - sql_result_int(context, len);
> + return 0;
> + int len = sql_utf8_char_count(z,
> + sql_value_bytes(&argv[0]));
> + sqlVdbeMemSetInt64(val, len);
> break;
> }
> default:{
> - sql_result_null(context);
> + sqlVdbeMemSetNull(val);
> break;
> }
> }
> + return 0;
> }
>
> /*
> @@ -171,36 +234,43 @@ lengthFunc(sql_context * context, int argc, sql_value ** argv)
> * IMP: R-23979-26855 The abs(X) function returns the absolute value of
> * the numeric argument X.
> */
> -static void
> -absFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_abs(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - switch (sql_value_type(argv[0])) {
> + switch (sql_value_type(&argv[0])) {
> case MP_INT:{
> - i64 iVal = sql_value_int64(argv[0]);
> + i64 iVal = sql_value_int64(&argv[0]);
> if (iVal < 0) {
> if (iVal == SMALLEST_INT64) {
> diag_set(ClientError, ER_SQL_EXECUTE,
> "integer is overflowed");
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> iVal = -iVal;
> }
> - sql_result_int64(context, iVal);
> + sqlVdbeMemSetInt64(val, iVal);
> break;
> }
> case MP_NIL:{
> /* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */
> - sql_result_null(context);
> + sqlVdbeMemSetNull(val);
> break;
> }
> case MP_BOOL: {
> diag_set(ClientError, ER_INCONSISTENT_TYPES, "number",
> "boolean");
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> default:{
> /* Because sql_value_double() returns 0.0 if the argument is not
> @@ -208,13 +278,14 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
> * IMP: R-01992-00519 Abs(X) returns 0.0 if X is a string or blob
> * that cannot be converted to a numeric value.
> */
> - double rVal = sql_value_double(argv[0]);
> + double rVal = sql_value_double(&argv[0]);
> if (rVal < 0)
> rVal = -rVal;
> - sql_result_double(context, rVal);
> + sqlVdbeMemSetDouble(val, rVal);
> break;
> }
> }
> + return 0;
> }
>
> /**
> @@ -228,17 +299,28 @@ absFunc(sql_context * context, int argc, sql_value ** argv)
> * more than the number of bytes in haystack prior to the first
> * occurrence of needle, or 0 if needle never occurs in haystack.
> */
> -static void
> -position_func(struct sql_context *context, int argc, struct Mem **argv)
> +static int
> +sql_builtin_position(struct func *func, struct port *args, struct port *ret)
> {
> - UNUSED_PARAMETER(argc);
> - struct Mem *needle = argv[0];
> - struct Mem *haystack = argv[1];
> + (void)func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + struct Mem *needle = &argv[0];
> + struct Mem *haystack = &argv[1];
> enum mp_type needle_type = sql_value_type(needle);
> enum mp_type haystack_type = sql_value_type(haystack);
>
> if (haystack_type == MP_NIL || needle_type == MP_NIL)
> - return;
> + return 0;
> /*
> * Position function can be called only with string
> * or blob params.
> @@ -251,8 +333,7 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
> if (inconsistent_type_arg != NULL) {
> diag_set(ClientError, ER_INCONSISTENT_TYPES, "TEXT or BLOB",
> mem_type_to_str(inconsistent_type_arg));
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> /*
> * Both params of Position function must be of the same
> @@ -261,8 +342,7 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
> if (haystack_type != needle_type) {
> diag_set(ClientError, ER_INCONSISTENT_TYPES,
> mem_type_to_str(needle), mem_type_to_str(haystack));
> - context->is_aborted = true;
> - return;
> + return -1;
> }
>
> int n_needle_bytes = sql_value_bytes(needle);
> @@ -328,7 +408,7 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
> n_haystack_bytes);
> }
> int beg_offset = 0;
> - struct coll *coll = sqlGetFuncCollSeq(context);
> + struct coll *coll = sqlGetFuncCollSeq(ctx);
> int c;
> for (c = 0; c + n_needle_chars <= n_haystack_chars; c++) {
> if (coll->cmp((const char *) haystack_str + beg_offset,
> @@ -348,34 +428,47 @@ position_func(struct sql_context *context, int argc, struct Mem **argv)
> }
> }
> finish:
> - sql_result_int(context, position);
> + sqlVdbeMemSetInt64(val, position);
> + return 0;
> }
>
> /*
> * Implementation of the printf() function.
> */
> -static void
> -printfFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_printf(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> PrintfArguments x;
> StrAccum str;
> const char *zFormat;
> int n;
> - sql *db = sql_context_db_handle(context);
> + struct sql *db = sql_get();
>
> - if (argc >= 1
> - && (zFormat = (const char *)sql_value_text(argv[0])) != 0) {
> + if (argc >= 1 &&
> + (zFormat = (const char *)sql_value_text(&argv[0])) != 0) {
> x.nArg = argc - 1;
> x.nUsed = 0;
> - x.apArg = argv + 1;
> + x.apArg = &argv[1];
> sqlStrAccumInit(&str, db, 0, 0,
> db->aLimit[SQL_LIMIT_LENGTH]);
> str.printfFlags = SQL_PRINTF_SQLFUNC;
> sqlXPrintf(&str, zFormat, &x);
> n = str.nChar;
> - sql_result_text(context, sqlStrAccumFinish(&str), n,
> - SQL_DYNAMIC);
> + if (sqlVdbeMemSetStr(val, sqlStrAccumFinish(&str), n,
> + 1, SQL_DYNAMIC) != 0)
> + return -1;
> }
> + return 0;
> }
>
> /*
> @@ -390,9 +483,20 @@ printfFunc(sql_context * context, int argc, sql_value ** argv)
> *
> * If p2 is negative, return the p2 characters preceding p1.
> */
> -static void
> -substrFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_substr(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql *db = sql_get();
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> const unsigned char *z;
> const unsigned char *z2;
> int len;
> @@ -401,36 +505,33 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
> int negP2 = 0;
>
> assert(argc == 3 || argc == 2);
> - if (sql_value_is_null(argv[1])
> - || (argc == 3 && sql_value_is_null(argv[2]))
> - ) {
> - return;
> - }
> - p0type = sql_value_type(argv[0]);
> - p1 = sql_value_int(argv[1]);
> + if (sql_value_is_null(&argv[1]) ||
> + (argc == 3 && sql_value_is_null(&argv[2])))
> + return 0;
> + p0type = sql_value_type(&argv[0]);
> + p1 = sql_value_int(&argv[1]);
> if (p0type == MP_BIN) {
> - len = sql_value_bytes(argv[0]);
> - z = sql_value_blob(argv[0]);
> + len = sql_value_bytes(&argv[0]);
> + z = sql_value_blob(&argv[0]);
> if (z == 0)
> - return;
> - assert(len == sql_value_bytes(argv[0]));
> + return 0;
> + assert(len == sql_value_bytes(&argv[0]));
> } else {
> - z = sql_value_text(argv[0]);
> + z = sql_value_text(&argv[0]);
> if (z == 0)
> - return;
> + return 0;
> len = 0;
> if (p1 < 0)
> - len = sql_utf8_char_count(z, sql_value_bytes(argv[0]));
> + len = sql_utf8_char_count(z, sql_value_bytes(&argv[0]));
> }
> if (argc == 3) {
> - p2 = sql_value_int(argv[2]);
> + p2 = sql_value_int(&argv[2]);
> if (p2 < 0) {
> p2 = -p2;
> negP2 = 1;
> }
> } else {
> - p2 = sql_context_db_handle(context)->
> - aLimit[SQL_LIMIT_LENGTH];
> + p2 = db->aLimit[SQL_LIMIT_LENGTH];
> }
> if (p1 < 0) {
> p1 += len;
> @@ -459,7 +560,7 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
> * used because '\0' is not supposed to be
> * end-of-string symbol.
> */
> - int byte_size = sql_value_bytes(argv[0]);
> + int byte_size = sql_value_bytes(&argv[0]);
> int n_chars = sql_utf8_char_count(z, byte_size);
> int cnt = 0;
> int i = 0;
> @@ -475,38 +576,51 @@ substrFunc(sql_context * context, int argc, sql_value ** argv)
> cnt++;
> }
> z2 += i;
> - sql_result_text64(context, (char *)z, z2 - z,
> - SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, (char *)z, z2 - z, 1,
> + SQL_TRANSIENT) != 0)
> + return -1;
> } else {
> if (p1 + p2 > len) {
> p2 = len - p1;
> if (p2 < 0)
> p2 = 0;
> }
> - sql_result_blob64(context, (char *)&z[p1], (u64) p2,
> - SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, (char *)&z[p1], (u64) p2, 0,
> + SQL_TRANSIENT) != 0)
> + return -1;
> }
> + return 0;
> }
>
> /*
> * Implementation of the round() function
> */
> -static void
> -roundFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_round(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> int n = 0;
> double r;
> assert(argc == 1 || argc == 2);
> if (argc == 2) {
> - if (sql_value_is_null(argv[1]))
> - return;
> - n = sql_value_int(argv[1]);
> + if (sql_value_is_null(&argv[1]))
> + return 0;
> + n = sql_value_int(&argv[1]);
> if (n < 0)
> n = 0;
> }
> - if (sql_value_is_null(argv[0]))
> - return;
> - r = sql_value_double(argv[0]);
> + if (sql_value_is_null(&argv[0]))
> + return 0;
> + r = sql_value_double(&argv[0]);
> /* If Y==0 and X will fit in a 64-bit int,
> * handle the rounding directly,
> * otherwise use printf.
> @@ -519,7 +633,8 @@ roundFunc(sql_context * context, int argc, sql_value ** argv)
> const char *rounded_value = tt_sprintf("%.*f", n, r);
> sqlAtoF(rounded_value, &r, sqlStrlen30(rounded_value));
> }
> - sql_result_double(context, r);
> + sqlVdbeMemSetDouble(val, r);
> + return 0;
> }
>
> /*
> @@ -528,24 +643,15 @@ roundFunc(sql_context * context, int argc, sql_value ** argv)
> * maximum string or blob length, then raise an error and return
> * NULL.
> */
> -static void *
> -contextMalloc(sql_context * context, i64 nByte)
> +static char *
> +sql_blob_alloc(int64_t size)
> {
> - char *z;
> - sql *db = sql_context_db_handle(context);
> - assert(nByte > 0);
> - testcase(nByte == db->aLimit[SQL_LIMIT_LENGTH]);
> - testcase(nByte == db->aLimit[SQL_LIMIT_LENGTH] + 1);
> - if (nByte > db->aLimit[SQL_LIMIT_LENGTH]) {
> + struct sql *db = sql_get();
> + if (size > db->aLimit[SQL_LIMIT_LENGTH]) {
> diag_set(ClientError, ER_SQL_EXECUTE, "string or blob too big");
> - context->is_aborted = true;
> - z = 0;
> - } else {
> - z = sqlMalloc(nByte);
> - if (z == NULL)
> - context->is_aborted = true;
> + return NULL;
> }
> - return z;
> + return sql_malloc64(size);
> }
>
> /*
> @@ -553,29 +659,38 @@ contextMalloc(sql_context * context, i64 nByte)
> */
>
> #define ICU_CASE_CONVERT(case_type) \
> -static void \
> -case_type##ICUFunc(sql_context *context, int argc, sql_value **argv) \
> +static int \
> +sql_builtin_ICU##case_type(struct func *func, struct port *args, \
> + struct port *ret) \
> { \
> + (void) func; \
> + uint32_t argc; \
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc); \
> + if (argv == NULL) \
> + return -1; \
> + struct sql_context *ctx = (struct sql_context *)port_get_context(args);\
> + assert(ctx != NULL); \
> + struct Mem *val = vdbemem_alloc_on_region(1); \
> + if (val == NULL) \
> + return -1; \
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL); \
> char *z1; \
> const char *z2; \
> int n; \
> - UNUSED_PARAMETER(argc); \
> - z2 = (char *)sql_value_text(argv[0]); \
> - n = sql_value_bytes(argv[0]); \
> + z2 = (char *)sql_value_text(&argv[0]); \
> + n = sql_value_bytes(&argv[0]); \
> /* \
> * Verify that the call to _bytes() \
> * does not invalidate the _text() pointer. \
> */ \
> - assert(z2 == (char *)sql_value_text(argv[0])); \
> + assert(z2 == (char *)sql_value_text(&argv[0])); \
> if (!z2) \
> - return; \
> - z1 = contextMalloc(context, ((i64) n) + 1); \
> - if (z1 == NULL) { \
> - context->is_aborted = true; \
> - return; \
> - } \
> + return 0; \
> + z1 = sql_blob_alloc(((i64) n) + 1); \
> + if (z1 == NULL) \
> + return -1; \
> UErrorCode status = U_ZERO_ERROR; \
> - struct coll *coll = sqlGetFuncCollSeq(context); \
> + struct coll *coll = sqlGetFuncCollSeq(ctx); \
> const char *locale = NULL; \
> if (coll != NULL && coll->type == COLL_TYPE_ICU) { \
> locale = ucol_getLocaleByType(coll->collator, \
> @@ -586,17 +701,15 @@ case_type##ICUFunc(sql_context *context, int argc, sql_value **argv) \
> int len = ucasemap_utf8To##case_type(case_map, z1, n, z2, n, &status); \
> if (len > n) { \
> status = U_ZERO_ERROR; \
> - sql_free(z1); \
> - z1 = contextMalloc(context, ((i64) len) + 1); \
> - if (z1 == NULL) { \
> - context->is_aborted = true; \
> - return; \
> - } \
> + sql_free(z1); \
> + z1 = sql_blob_alloc(((i64) len) + 1); \
> + if (z1 == NULL) \
> + return -1; \
> ucasemap_utf8To##case_type(case_map, z1, len, z2, n, &status); \
> } \
> ucasemap_close(case_map); \
> - sql_result_text(context, z1, len, sql_free); \
> -} \
> + return sqlVdbeMemSetStr(val, z1, len, 1, SQL_DYNAMIC) != 0 ? -1 : 0; \
> +}
>
> ICU_CASE_CONVERT(Lower);
> ICU_CASE_CONVERT(Upper);
> @@ -607,21 +720,27 @@ ICU_CASE_CONVERT(Upper);
> * as VDBE code so that unused argument values do not have to be
> * computed.
> * However, we still need some kind of function implementation for
> - * this routines in the function table. The noopFunc macro
> - * provides this. noopFunc will never be called so it doesn't
> - * matter what the implementation is. We might as well use the
> - * "version()" function as a substitute.
> + * this routines in the function table. The sql_builtin_noop macro
> + * provides this. sql_builtin_noop will never be called so it
> + * doesn't matter what the implementation is. We might as well
> + * use the "version()" function as a substitute.
> */
> -#define noopFunc sql_func_version /* Substitute function - never called */
> +#define sql_builtin_noop sql_builtin_version
>
> /*
> * Implementation of random(). Return a random integer.
> */
> -static void
> -randomFunc(sql_context * context, int NotUsed, sql_value ** NotUsed2)
> +static int
> +sql_builtin_random(struct func *func, struct port *args, struct port *ret)
> {
> - sql_int64 r;
> - UNUSED_PARAMETER2(NotUsed, NotUsed2);
> + (void) func;
> + (void) args;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + int64_t r;
> sql_randomness(sizeof(r), &r);
> if (r < 0) {
> /* We need to prevent a random number of 0x8000000000000000
> @@ -634,28 +753,36 @@ randomFunc(sql_context * context, int NotUsed, sql_value ** NotUsed2)
> */
> r = -(r & LARGEST_INT64);
> }
> - sql_result_int64(context, r);
> + sqlVdbeMemSetInt64(val, r);
> + return 0;
> }
>
> /*
> - * Implementation of randomblob(N). Return a random blob
> + * Implementation of sql_builtin_random_blob(N). Return a random blob
> * that is N bytes long.
> */
> -static void
> -randomBlob(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_random_blob(struct func *func, struct port *args, struct port *ret)
> {
> - int n;
> - unsigned char *p;
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - n = sql_value_int(argv[0]);
> + int n = sql_value_int(&argv[0]);
> if (n < 1)
> - return;
> - p = contextMalloc(context, n);
> - if (p) {
> - sql_randomness(n, p);
> - sql_result_blob(context, (char *)p, n, sql_free);
> - }
> + return 0;
> + char *p = sql_blob_alloc(n);
> + if (p == NULL)
> + return -1;
> + sql_randomness(n, p);
> + return sqlVdbeMemSetStr(val, p, n, 0, SQL_DYNAMIC) != 0 ? -1 : 0;
> }
>
> #define Utf8Read(s, e) \
> @@ -880,49 +1007,57 @@ sql_strlike_ci(const char *zPattern, const char *zStr, unsigned int esc)
> * Both arguments (A and B) must be of type TEXT. If one arguments
> * is NULL then result is NULL as well.
> */
> -static void
> -likeFunc(sql_context *context, int argc, sql_value **argv)
> +static int
> +sql_builtin_like(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> u32 escape = SQL_END_OF_STRING;
> int nPat;
> - sql *db = sql_context_db_handle(context);
> + struct sql *db = sql_get();
> bool is_like_ci =
> (current_session()->sql_flags & LIKE_CASE_SENS_FLAG) == 0;
> - int rhs_type = sql_value_type(argv[0]);
> - int lhs_type = sql_value_type(argv[1]);
> + int rhs_type = sql_value_type(&argv[0]);
> + int lhs_type = sql_value_type(&argv[1]);
>
> if (lhs_type != MP_STR || rhs_type != MP_STR) {
> if (lhs_type == MP_NIL || rhs_type == MP_NIL)
> - return;
> + return 0;
> char *inconsistent_type = rhs_type != MP_STR ?
> - mem_type_to_str(argv[0]) :
> - mem_type_to_str(argv[1]);
> + mem_type_to_str(&argv[0]) :
> + mem_type_to_str(&argv[1]);
> diag_set(ClientError, ER_INCONSISTENT_TYPES, "TEXT",
> inconsistent_type);
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> - const char *zB = (const char *) sql_value_text(argv[0]);
> - const char *zA = (const char *) sql_value_text(argv[1]);
> - const char *zB_end = zB + sql_value_bytes(argv[0]);
> - const char *zA_end = zA + sql_value_bytes(argv[1]);
> + const char *zB = (const char *) sql_value_text(&argv[0]);
> + const char *zA = (const char *) sql_value_text(&argv[1]);
> + const char *zB_end = zB + sql_value_bytes(&argv[0]);
> + const char *zA_end = zA + sql_value_bytes(&argv[1]);
>
> /*
> * Limit the length of the LIKE pattern to avoid problems
> * of deep recursion and N*N behavior in
> * sql_utf8_pattern_compare().
> */
> - nPat = sql_value_bytes(argv[0]);
> + nPat = sql_value_bytes(&argv[0]);
> testcase(nPat == db->aLimit[SQL_LIMIT_LIKE_PATTERN_LENGTH]);
> testcase(nPat == db->aLimit[SQL_LIMIT_LIKE_PATTERN_LENGTH] + 1);
> if (nPat > db->aLimit[SQL_LIMIT_LIKE_PATTERN_LENGTH]) {
> diag_set(ClientError, ER_SQL_EXECUTE, "LIKE pattern is too "\
> "complex");
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> /* Encoding did not change */
> - assert(zB == (const char *) sql_value_text(argv[0]));
> + assert(zB == (const char *) sql_value_text(&argv[0]));
>
> if (argc == 3) {
> /*
> @@ -930,29 +1065,28 @@ likeFunc(sql_context *context, int argc, sql_value **argv)
> * single UTF-8 character. Otherwise, return an
> * error.
> */
> - const unsigned char *zEsc = sql_value_text(argv[2]);
> + const unsigned char *zEsc = sql_value_text(&argv[2]);
> if (zEsc == 0)
> - return;
> - if (sql_utf8_char_count(zEsc, sql_value_bytes(argv[2])) != 1) {
> + return 0;
> + if (sql_utf8_char_count(zEsc, sql_value_bytes(&argv[2])) != 1) {
> diag_set(ClientError, ER_SQL_EXECUTE, "ESCAPE "\
> "expression must be a single character");
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> escape = sqlUtf8Read(&zEsc);
> }
> if (!zA || !zB)
> - return;
> + return 0;
> int res;
> res = sql_utf8_pattern_compare(zB, zA, zB_end, zA_end,
> is_like_ci, escape);
> if (res == INVALID_PATTERN) {
> diag_set(ClientError, ER_SQL_EXECUTE, "LIKE pattern can only "\
> "contain UTF-8 characters");
> - context->is_aborted = true;
> - return;
> + return -1;
> }
> - sql_result_bool(context, res == MATCH);
> + mem_set_bool(val, res == MATCH);
> + return 0;
> }
>
> /*
> @@ -960,14 +1094,26 @@ likeFunc(sql_context *context, int argc, sql_value **argv)
> * argument if the arguments are different. The result is NULL if the
> * arguments are equal to each other.
> */
> -static void
> -nullifFunc(sql_context * context, int NotUsed, sql_value ** argv)
> +static int
> +sql_builtin_nullif(struct func *func, struct port *args, struct port *ret)
> {
> - struct coll *pColl = sqlGetFuncCollSeq(context);
> - UNUSED_PARAMETER(NotUsed);
> - if (sqlMemCompare(argv[0], argv[1], pColl) != 0) {
> - sql_result_value(context, argv[0]);
> - }
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + struct coll *pColl = sqlGetFuncCollSeq(ctx);
> + if (sqlMemCompare(&argv[0], &argv[1], pColl) != 0 &&
> + sqlVdbeMemCopy(val, &argv[0]) != 0)
> + return -1;
> + return 0;
> }
>
> /**
> @@ -978,12 +1124,17 @@ nullifFunc(sql_context * context, int NotUsed, sql_value ** argv)
> * @param unused1 Unused.
> * @param unused2 Unused.
> */
> -static void
> -sql_func_version(struct sql_context *context,
> - MAYBE_UNUSED int unused1,
> - MAYBE_UNUSED sql_value **unused2)
> +static int
> +sql_builtin_version(struct func *func, struct port *args, struct port *ret)
> {
> - sql_result_text(context, tarantool_version(), -1, SQL_STATIC);
> + (void) func;
> + (void) args;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + return sqlVdbeMemSetStr(val, tarantool_version(), -1, 1,
> + SQL_STATIC) != 0 ? -1 : 0;
> }
>
> /* Array for converting from half-bytes (nybbles) into ASCII hex
> @@ -1001,108 +1152,126 @@ static const char hexdigits[] = {
> * "NULL". Otherwise, the argument is enclosed in single quotes with
> * single-quote escapes.
> */
> -static void
> -quoteFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_quote(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - switch (sql_value_type(argv[0])) {
> + switch (sql_value_type(&argv[0])) {
> case MP_DOUBLE:{
> double r1, r2;
> char zBuf[50];
> - r1 = sql_value_double(argv[0]);
> + r1 = sql_value_double(&argv[0]);
> sql_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
> sqlAtoF(zBuf, &r2, 20);
> if (r1 != r2) {
> sql_snprintf(sizeof(zBuf), zBuf, "%!.20e",
> r1);
> }
> - sql_result_text(context, zBuf, -1,
> - SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, zBuf, -1, 1,
> + SQL_TRANSIENT) != 0)
> + return -1;
> break;
> }
> case MP_INT:{
> - sql_result_value(context, argv[0]);
> + if (sqlVdbeMemCopy(val, &argv[0]) != 0)
> + return -1;
> break;
> }
> case MP_BIN:{
> - char *zText = 0;
> - char const *zBlob = sql_value_blob(argv[0]);
> - int nBlob = sql_value_bytes(argv[0]);
> - assert(zBlob == sql_value_blob(argv[0])); /* No encoding change */
> - zText =
> - (char *)contextMalloc(context,
> - (2 * (i64) nBlob) + 4);
> - if (zText) {
> - int i;
> - for (i = 0; i < nBlob; i++) {
> - zText[(i * 2) + 2] =
> - hexdigits[(zBlob[i] >> 4) & 0x0F];
> - zText[(i * 2) + 3] =
> - hexdigits[(zBlob[i]) & 0x0F];
> - }
> - zText[(nBlob * 2) + 2] = '\'';
> - zText[(nBlob * 2) + 3] = '\0';
> - zText[0] = 'X';
> - zText[1] = '\'';
> - sql_result_text(context, zText, -1,
> - SQL_TRANSIENT);
> - sql_free(zText);
> + char const *zBlob = sql_value_blob(&argv[0]);
> + int nBlob = sql_value_bytes(&argv[0]);
> + assert(zBlob == sql_value_blob(&argv[0])); /* No encoding change */
> + char *z = sql_blob_alloc((2 * (i64) nBlob) + 4);
> + if (z == NULL)
> + return -1;
> + for (int i = 0; i < nBlob; i++) {
> + z[(i * 2) + 2] =
> + hexdigits[(zBlob[i] >> 4) & 0x0F];
> + z[(i * 2) + 3] =
> + hexdigits[(zBlob[i]) & 0x0F];
> }
> + z[(nBlob * 2) + 2] = '\'';
> + z[(nBlob * 2) + 3] = '\0';
> + z[0] = 'X';
> + z[1] = '\'';
> + if (sqlVdbeMemSetStr(val, z, -1, 1, SQL_DYNAMIC) != 0)
> + return -1;
> break;
> }
> case MP_STR:{
> int i, j;
> u64 n;
> - const unsigned char *zArg = sql_value_text(argv[0]);
> - char *z;
> -
> + const unsigned char *zArg = sql_value_text(&argv[0]);
> if (zArg == 0)
> - return;
> + return 0;
> for (i = 0, n = 0; zArg[i]; i++) {
> if (zArg[i] == '\'')
> n++;
> }
> - z = contextMalloc(context, ((i64) i) + ((i64) n) + 3);
> - if (z) {
> - z[0] = '\'';
> - for (i = 0, j = 1; zArg[i]; i++) {
> - z[j++] = zArg[i];
> - if (zArg[i] == '\'') {
> - z[j++] = '\'';
> - }
> + char *z = sql_blob_alloc(((i64) i) + ((i64) n) + 3);
> + if (z == NULL)
> + return -1;
> + z[0] = '\'';
> + for (i = 0, j = 1; zArg[i]; i++) {
> + z[j++] = zArg[i];
> + if (zArg[i] == '\'') {
> + z[j++] = '\'';
> }
> - z[j++] = '\'';
> - z[j] = 0;
> - sql_result_text(context, z, j,
> - sql_free);
> }
> + z[j++] = '\'';
> + z[j] = 0;
> + if (sqlVdbeMemSetStr(val, z, j, 1, SQL_DYNAMIC) != 0)
> + return -1;
> break;
> }
> case MP_BOOL: {
> - sql_result_text(context, sql_value_boolean(argv[0]) ?
> - "true" : "false", -1, SQL_TRANSIENT);
> + if (sqlVdbeMemSetStr(val, sql_value_boolean(&argv[0]) ?
> + "true" : "false", -1, 1, SQL_STATIC) != 0)
> + return -1;
> break;
> }
> default:{
> - assert(sql_value_is_null(argv[0]));
> - sql_result_text(context, "NULL", 4, SQL_STATIC);
> + assert(sql_value_is_null(&argv[0]));
> + if (sqlVdbeMemSetStr(val, "NULL", 4, 1,
> + SQL_STATIC) != 0)
> + return -1;
> break;
> }
> }
> + return 0;
> }
>
> /*
> * The unicode() function. Return the integer unicode code-point value
> * for the first character of the input string.
> */
> -static void
> -unicodeFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_unicode(struct func *func, struct port *args, struct port *ret)
> {
> - const unsigned char *z = sql_value_text(argv[0]);
> - (void)argc;
> - if (z && z[0])
> - sql_result_int(context, sqlUtf8Read(&z));
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> + const unsigned char *z = sql_value_text(&argv[0]);
> + if (z != NULL && z[0] != '\0')
> + sqlVdbeMemSetInt64(val, sqlUtf8Read(&z));
> + return 0;
> }
>
> /*
> @@ -1110,20 +1279,27 @@ unicodeFunc(sql_context * context, int argc, sql_value ** argv)
> * an integer. It constructs a string where each character of the string
> * is the unicode character for the corresponding integer argument.
> */
> -static void
> -charFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_char(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> unsigned char *z, *zOut;
> - int i;
> zOut = z = sql_malloc64(argc * 4 + 1);
> - if (z == NULL) {
> - context->is_aborted = true;
> - return;
> - }
> - for (i = 0; i < argc; i++) {
> + if (z == NULL)
> + return -1;
> + for (uint32_t i = 0; i < argc; i++) {
> sql_int64 x;
> unsigned c;
> - x = sql_value_int64(argv[i]);
> + x = sql_value_int64(&argv[i]);
> if (x < 0 || x > 0x10ffff)
> x = 0xfffd;
> c = (unsigned)(x & 0x1fffff);
> @@ -1143,52 +1319,79 @@ charFunc(sql_context * context, int argc, sql_value ** argv)
> *zOut++ = 0x80 + (u8) (c & 0x3F);
> }
> }
> - sql_result_text64(context, (char *)z, zOut - z, sql_free);
> + if (zOut - z > sql_get()->aLimit[SQL_LIMIT_LENGTH]) {
> + sql_free(z);
> + diag_set(ClientError, ER_SQL_EXECUTE, "string or blob too big");
> + return -1;
> + }
> + return sqlVdbeMemSetStr(val, (char *)z, zOut - z, 1,
> + SQL_DYNAMIC) != 0 ? -1 : 0;
> }
>
> /*
> * The hex() function. Interpret the argument as a blob. Return
> * a hexadecimal rendering as text.
> */
> -static void
> -hexFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_hex(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + assert(argc == 1);
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> int i, n;
> const unsigned char *pBlob;
> char *zHex, *z;
> - assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - pBlob = sql_value_blob(argv[0]);
> - n = sql_value_bytes(argv[0]);
> - assert(pBlob == sql_value_blob(argv[0])); /* No encoding change */
> - z = zHex = contextMalloc(context, ((i64) n) * 2 + 1);
> - if (zHex) {
> - for (i = 0; i < n; i++, pBlob++) {
> - unsigned char c = *pBlob;
> - *(z++) = hexdigits[(c >> 4) & 0xf];
> - *(z++) = hexdigits[c & 0xf];
> - }
> - *z = 0;
> - sql_result_text(context, zHex, n * 2, sql_free);
> + pBlob = sql_value_blob(&argv[0]);
> + n = sql_value_bytes(&argv[0]);
> + assert(pBlob == sql_value_blob(&argv[0])); /* No encoding change */
> + z = zHex = sql_blob_alloc(((i64) n) * 2 + 1);
> + if (z == NULL)
> + return -1;
> + for (i = 0; i < n; i++, pBlob++) {
> + unsigned char c = *pBlob;
> + *(z++) = hexdigits[(c >> 4) & 0xf];
> + *(z++) = hexdigits[c & 0xf];
> }
> + *z = 0;
> + return sqlVdbeMemSetStr(val, zHex, n * 2, 1, SQL_DYNAMIC) != 0 ? -1 : 0;
> }
>
> /*
> * The zeroblob(N) function returns a zero-filled blob of size N bytes.
> */
> -static void
> -zeroblobFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_zeroblob(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> i64 n;
> assert(argc == 1);
> UNUSED_PARAMETER(argc);
> - n = sql_value_int64(argv[0]);
> + n = sql_value_int64(&argv[0]);
> if (n < 0)
> n = 0;
> - if (sql_result_zeroblob64(context, n) != 0) {
> + if (n > sql_get()->aLimit[SQL_LIMIT_LENGTH]) {
> diag_set(ClientError, ER_SQL_EXECUTE, "string or blob too big");
> - context->is_aborted = true;
> + return -1;
> }
> + sqlVdbeMemSetZeroBlob(val, n);
> + return 0;
> }
>
> /*
> @@ -1197,9 +1400,19 @@ zeroblobFunc(sql_context * context, int argc, sql_value ** argv)
> * from A by replacing every occurrence of B with C. The match
> * must be exact. Collating sequences are not used.
> */
> -static void
> -replaceFunc(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_replace(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> +
> const unsigned char *zStr; /* The input string A */
> const unsigned char *zPattern; /* The pattern string B */
> const unsigned char *zRep; /* The replacement string C */
> @@ -1213,35 +1426,30 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
>
> assert(argc == 3);
> UNUSED_PARAMETER(argc);
> - zStr = sql_value_text(argv[0]);
> + zStr = sql_value_text(&argv[0]);
> if (zStr == 0)
> - return;
> - nStr = sql_value_bytes(argv[0]);
> - assert(zStr == sql_value_text(argv[0])); /* No encoding change */
> - zPattern = sql_value_text(argv[1]);
> - if (zPattern == 0) {
> - assert(sql_value_is_null(argv[1])
> - || sql_context_db_handle(context)->mallocFailed);
> - return;
> - }
> - nPattern = sql_value_bytes(argv[1]);
> + return 0;
> + nStr = sql_value_bytes(&argv[0]);
> + assert(zStr == sql_value_text(&argv[0]));
> + zPattern = sql_value_text(&argv[1]);
> + if (zPattern == 0)
> + return 0;
> + nPattern = sql_value_bytes(&argv[1]);
> if (nPattern == 0) {
> - assert(! sql_value_is_null(argv[1]));
> - sql_result_value(context, argv[0]);
> - return;
> + assert(! sql_value_is_null(&argv[1]));
> + return sqlVdbeMemCopy(val, &argv[0]) != 0 ? -1 : 0;
> }
> - assert(zPattern == sql_value_text(argv[1])); /* No encoding change */
> - zRep = sql_value_text(argv[2]);
> + assert(zPattern == sql_value_text(&argv[1]));
> + zRep = sql_value_text(&argv[2]);
> if (zRep == 0)
> - return;
> - nRep = sql_value_bytes(argv[2]);
> - assert(zRep == sql_value_text(argv[2]));
> + return 0;
> + nRep = sql_value_bytes(&argv[2]);
> + assert(zRep == sql_value_text(&argv[2]));
> nOut = nStr + 1;
> assert(nOut < SQL_MAX_LENGTH);
> - zOut = contextMalloc(context, (i64) nOut);
> - if (zOut == 0) {
> - return;
> - }
> + zOut = (unsigned char *)sql_blob_alloc(nOut);
> + if (zOut == NULL)
> + return -1;
> loopLimit = nStr - nPattern;
> for (i = j = 0; i <= loopLimit; i++) {
> if (zStr[i] != zPattern[0]
> @@ -1249,23 +1457,21 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
> zOut[j++] = zStr[i];
> } else {
> u8 *zOld;
> - sql *db = sql_context_db_handle(context);
> + struct sql *db = sql_get();
> nOut += nRep - nPattern;
> testcase(nOut - 1 == db->aLimit[SQL_LIMIT_LENGTH]);
> testcase(nOut - 2 == db->aLimit[SQL_LIMIT_LENGTH]);
> if (nOut - 1 > db->aLimit[SQL_LIMIT_LENGTH]) {
> diag_set(ClientError, ER_SQL_EXECUTE, "string "\
> "or blob too big");
> - context->is_aborted = true;
> sql_free(zOut);
> - return;
> + return -1;
> }
> zOld = zOut;
> zOut = sql_realloc64(zOut, (int)nOut);
> if (zOut == 0) {
> - context->is_aborted = true;
> sql_free(zOld);
> - return;
> + return -1;
> }
> memcpy(&zOut[j], zRep, nRep);
> j += nRep;
> @@ -1277,23 +1483,25 @@ replaceFunc(sql_context * context, int argc, sql_value ** argv)
> j += nStr - i;
> assert(j <= nOut);
> zOut[j] = 0;
> - sql_result_text(context, (char *)zOut, j, sql_free);
> + return sqlVdbeMemSetStr(val, (char *)zOut, j, 1,
> + SQL_DYNAMIC) != 0 ? -1 : 0;
> }
>
> /**
> * Remove characters included in @a trim_set from @a input_str
> * until encounter a character that doesn't belong to @a trim_set.
> * Remove from the side specified by @a flags.
> - * @param context SQL context.
> + * @param ret SQL value to store result.
> * @param flags Trim specification: left, right or both.
> * @param trim_set The set of characters for trimming.
> * @param char_len Lengths of each UTF-8 character in @a trim_set.
> * @param char_cnt A number of UTF-8 characters in @a trim_set.
> * @param input_str Input string for trimming.
> * @param input_str_sz Input string size in bytes.
> + * @return 0 On success, -1 otherwise.
> */
> -static void
> -trim_procedure(struct sql_context *context, enum trim_side_mask flags,
> +static int
> +trim_procedure(struct Mem *val, enum trim_side_mask flags,
> const unsigned char *trim_set, const uint8_t *char_len,
> int char_cnt, const unsigned char *input_str, int input_str_sz)
> {
> @@ -1332,8 +1540,8 @@ trim_procedure(struct sql_context *context, enum trim_side_mask flags,
> }
> }
> finish:
> - sql_result_text(context, (char *)input_str, input_str_sz,
> - SQL_TRANSIENT);
> + return sqlVdbeMemSetStr(val, (char *)input_str, input_str_sz, 1,
> + SQL_TRANSIENT) != 0 ? -1 : 0;
> }
>
> /**
> @@ -1348,8 +1556,7 @@ finish:
> * @retval -1 Memory allocation error.
> */
> static int
> -trim_prepare_char_len(struct sql_context *context,
> - const unsigned char *trim_set, int trim_set_sz,
> +trim_prepare_char_len(const unsigned char *trim_set, int trim_set_sz,
> uint8_t **char_len)
> {
> /*
> @@ -1364,7 +1571,7 @@ trim_prepare_char_len(struct sql_context *context,
> return 0;
> }
>
> - if ((*char_len = (uint8_t *)contextMalloc(context, char_cnt)) == NULL)
> + if ((*char_len = (uint8_t *)sql_blob_alloc(char_cnt)) == NULL)
> return -1;
>
> int i = 0, j = 0;
> @@ -1385,20 +1592,29 @@ trim_prepare_char_len(struct sql_context *context,
> * Call trimming procedure with TRIM_BOTH as the flags and " " as
> * the trimming set.
> */
> -static void
> -trim_func_one_arg(struct sql_context *context, int argc, sql_value **argv)
> +static int
> +sql_builtin_trim_one_arg(struct func *func, struct port *args, struct port *ret)
> {
> - assert(argc == 1);
> - (void) argc;
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
>
> + assert(argc == 1);
> const unsigned char *input_str;
> - if ((input_str = sql_value_text(argv[0])) == NULL)
> - return;
> + if ((input_str = sql_value_text(&argv[0])) == NULL)
> + return 0;
>
> - int input_str_sz = sql_value_bytes(argv[0]);
> + int input_str_sz = sql_value_bytes(&argv[0]);
> uint8_t len_one = 1;
> - trim_procedure(context, TRIM_BOTH, (const unsigned char *) " ",
> + trim_procedure(val, TRIM_BOTH, (const unsigned char *) " ",
> &len_one, 1, input_str, input_str_sz);
> + return 0;
> }
>
> /**
> @@ -1412,33 +1628,46 @@ trim_func_one_arg(struct sql_context *context, int argc, sql_value **argv)
> * If user has specified side keyword only, then call trimming
> * procedure with the specified side and " " as the trimming set.
> */
> -static void
> -trim_func_two_args(struct sql_context *context, int argc, sql_value **argv)
> +static int
> +sql_builtin_trim_two_args(struct func *func, struct port *args,
> + struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> assert(argc == 2);
> - (void) argc;
>
> const unsigned char *input_str, *trim_set;
> - if ((input_str = sql_value_text(argv[1])) == NULL)
> - return;
> + if ((input_str = sql_value_text(&argv[1])) == NULL)
> + return 0;
>
> - int input_str_sz = sql_value_bytes(argv[1]);
> - if (sql_value_type(argv[0]) == MP_INT) {
> + int input_str_sz = sql_value_bytes(&argv[1]);
> + if (sql_value_type(&argv[0]) == MP_INT) {
> uint8_t len_one = 1;
> - trim_procedure(context, sql_value_int(argv[0]),
> - (const unsigned char *) " ", &len_one, 1,
> - input_str, input_str_sz);
> - } else if ((trim_set = sql_value_text(argv[0])) != NULL) {
> - int trim_set_sz = sql_value_bytes(argv[0]);
> - uint8_t *char_len;
> - int char_cnt = trim_prepare_char_len(context, trim_set,
> - trim_set_sz, &char_len);
> - if (char_cnt == -1)
> - return;
> - trim_procedure(context, TRIM_BOTH, trim_set, char_len, char_cnt,
> - input_str, input_str_sz);
> + if (trim_procedure(val, sql_value_int(&argv[0]),
> + (const unsigned char *) " ", &len_one, 1,
> + input_str, input_str_sz) != 0)
> + return -1;
> + } else if ((trim_set = sql_value_text(&argv[0])) != NULL) {
> + int trim_set_sz = sql_value_bytes(&argv[0]);
> + uint8_t *char_len = NULL;
> + int char_cnt =
> + trim_prepare_char_len(trim_set, trim_set_sz, &char_len);
> + if (char_cnt == -1 ||
> + trim_procedure(val, TRIM_BOTH, trim_set, char_len,
> + char_cnt, input_str, input_str_sz) != 0) {
> + sql_free(char_len);
> + return -1;
> + }
> sql_free(char_len);
> }
> + return 0;
> }
>
> /**
> @@ -1448,28 +1677,37 @@ trim_func_two_args(struct sql_context *context, int argc, sql_value **argv)
> * If user has specified side keyword and <character_set>, then
> * call trimming procedure with that args.
> */
> -static void
> -trim_func_three_args(struct sql_context *context, int argc, sql_value **argv)
> +static int
> +sql_builtin_trim_three_args(struct func *func, struct port *args,
> + struct port *ret)
> {
> + (void) func;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> assert(argc == 3);
> - (void) argc;
>
> - assert(sql_value_type(argv[0]) == MP_INT);
> + assert(sql_value_type(&argv[0]) == MP_INT);
> const unsigned char *input_str, *trim_set;
> - if ((input_str = sql_value_text(argv[2])) == NULL ||
> - (trim_set = sql_value_text(argv[1])) == NULL)
> - return;
> + if ((input_str = sql_value_text(&argv[2])) == NULL ||
> + (trim_set = sql_value_text(&argv[1])) == NULL)
> + return 0;
>
> - int trim_set_sz = sql_value_bytes(argv[1]);
> - int input_str_sz = sql_value_bytes(argv[2]);
> + int trim_set_sz = sql_value_bytes(&argv[1]);
> + int input_str_sz = sql_value_bytes(&argv[2]);
> uint8_t *char_len;
> - int char_cnt = trim_prepare_char_len(context, trim_set, trim_set_sz,
> - &char_len);
> + int char_cnt = trim_prepare_char_len(trim_set, trim_set_sz, &char_len);
> if (char_cnt == -1)
> - return;
> - trim_procedure(context, sql_value_int(argv[0]), trim_set, char_len,
> - char_cnt, input_str, input_str_sz);
> + return -1;
> + int rc = trim_procedure(val, sql_value_int(&argv[0]), trim_set, char_len,
> + char_cnt, input_str, input_str_sz);
> sql_free(char_len);
> + return rc;
> }
>
> /*
> @@ -1495,72 +1733,101 @@ struct SumCtx {
> * value. TOTAL never fails, but SUM might through an exception if
> * it overflows an integer.
> */
> -static void
> -sum_step(struct sql_context *context, int argc, sql_value **argv)
> +static int
> +sql_builtin_sum_step(struct func *func, struct port *args, struct port *ret)
> {
> + (void) func;
> + (void) ret;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> assert(argc == 1);
> - UNUSED_PARAMETER(argc);
> - struct SumCtx *p = sql_aggregate_context(context, sizeof(*p));
> - int type = sql_value_type(argv[0]);
> + struct SumCtx *p = sql_aggregate_context(ctx, sizeof(*p));
> + int type = sql_value_type(&argv[0]);
> if (type == MP_NIL || p == NULL)
> - return;
> + return 0;
> if (type != MP_DOUBLE && type != MP_INT) {
> - if (mem_apply_numeric_type(argv[0]) != 0) {
> + if (mem_apply_numeric_type(&argv[0]) != 0) {
> diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> - sql_value_text(argv[0]), "number");
> - context->is_aborted = true;
> - return;
> + sql_value_text(&argv[0]), "number");
> + return -1;
> }
> - type = sql_value_type(argv[0]);
> + type = sql_value_type(&argv[0]);
> }
> p->cnt++;
> if (type == MP_INT) {
> - int64_t v = sql_value_int64(argv[0]);
> + int64_t v = sql_value_int64(&argv[0]);
> p->rSum += v;
> if ((p->approx | p->overflow) == 0 &&
> sqlAddInt64(&p->iSum, v) != 0) {
> p->overflow = 1;
> }
> } else {
> - p->rSum += sql_value_double(argv[0]);
> + p->rSum += sql_value_double(&argv[0]);
> p->approx = 1;
> }
> + return 0;
> }
>
> -static void
> -sumFinalize(sql_context * context)
> +static int
> +sql_builtin_sum_finalize(struct func *func, struct port *args, struct port *ret)
> {
> - SumCtx *p;
> - p = sql_aggregate_context(context, 0);
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct SumCtx *p = sql_aggregate_context(ctx, 0);
> if (p && p->cnt > 0) {
> if (p->overflow) {
> - diag_set(ClientError, ER_SQL_EXECUTE, "integer "\
> - "overflow");
> - context->is_aborted = true;
> + diag_set(ClientError, ER_SQL_EXECUTE,
> + "integer overflow");
> + return -1;
> } else if (p->approx) {
> - sql_result_double(context, p->rSum);
> + sqlVdbeMemSetDouble(val, p->rSum);
> } else {
> - sql_result_int64(context, p->iSum);
> + sqlVdbeMemSetInt64(val, p->iSum);
> }
> }
> + return 0;
> }
>
> -static void
> -avgFinalize(sql_context * context)
> +static int
> +sql_builtin_avg_finalize(struct func *func, struct port *args,
> + struct port *ret)
> {
> - SumCtx *p;
> - p = sql_aggregate_context(context, 0);
> - if (p && p->cnt > 0) {
> - sql_result_double(context, p->rSum / (double)p->cnt);
> - }
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct SumCtx *p = sql_aggregate_context(ctx, 0);
> + if (p && p->cnt > 0)
> + sqlVdbeMemSetDouble(val, p->rSum / (double)p->cnt);
> + return 0;
> }
>
> -static void
> -totalFinalize(sql_context * context)
> +static int
> +sql_builtin_total_finalize(struct func *func, struct port *args,
> + struct port *ret)
> {
> - SumCtx *p;
> - p = sql_aggregate_context(context, 0);
> - sql_result_double(context, p ? p->rSum : (double)0);
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct SumCtx *p = sql_aggregate_context(ctx, 0);
> + sqlVdbeMemSetDouble(val, p != NULL ? p->rSum : (double)0);
> + return 0;
> }
>
> /*
> @@ -1575,45 +1842,67 @@ struct CountCtx {
> /*
> * Routines to implement the count() aggregate function.
> */
> -static void
> -countStep(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_count_step(struct func *func, struct port *args, struct port *ret)
> {
> - CountCtx *p;
> - p = sql_aggregate_context(context, sizeof(*p));
> - if ((argc == 0 || ! sql_value_is_null(argv[0])) && p) {
> + (void) func;
> + (void) ret;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct CountCtx *p = sql_aggregate_context(ctx, sizeof(*p));
> + if ((argc == 0 || ! sql_value_is_null(&argv[0])) && p)
> p->n++;
> - }
> + return 0;
> }
>
> -static void
> -countFinalize(sql_context * context)
> +static int
> +sql_builtin_count_finalize(struct func *func, struct port *args,
> + struct port *ret)
> {
> - CountCtx *p;
> - p = sql_aggregate_context(context, 0);
> - sql_result_int64(context, p ? p->n : 0);
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct CountCtx *p = sql_aggregate_context(ctx, 0);
> + sqlVdbeMemSetInt64(val, p != NULL ? p->n : 0);
> + return 0;
> }
>
> /*
> * Routines to implement min() and max() aggregate functions.
> */
> -static void
> -minmaxStep(sql_context * context, int NotUsed, sql_value ** argv)
> +static int
> +sql_builtin_minmax_step(struct func *func, struct port *args, struct port *ret)
> {
> - Mem *pArg = (Mem *) argv[0];
> + (void) func;
> + (void) ret;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + Mem *pArg = (Mem *) &argv[0];
> Mem *pBest;
> - UNUSED_PARAMETER(NotUsed);
>
> - pBest = (Mem *) sql_aggregate_context(context, sizeof(*pBest));
> - if (!pBest)
> - return;
> + pBest = (Mem *) sql_aggregate_context(ctx, sizeof(*pBest));
> + if (pBest == NULL)
> + return -1;
>
> - if (sql_value_is_null(argv[0])) {
> + if (sql_value_is_null(&argv[0])) {
> if (pBest->flags)
> - sqlSkipAccumulatorLoad(context);
> + sqlSkipAccumulatorLoad(ctx);
> } else if (pBest->flags) {
> int max;
> int cmp;
> - struct coll *pColl = sqlGetFuncCollSeq(context);
> + struct coll *pColl = sqlGetFuncCollSeq(ctx);
> /* This step function is used for both the min() and max() aggregates,
> * the only difference between the two being that the sense of the
> * comparison is inverted. For the max() aggregate, the
> @@ -1622,88 +1911,120 @@ minmaxStep(sql_context * context, int NotUsed, sql_value ** argv)
> * Therefore the next statement sets variable 'max' to 1 for the max()
> * aggregate, or 0 for min().
> */
> - max = sql_user_data(context) != 0;
> + max = sql_user_data(ctx) != 0;
> cmp = sqlMemCompare(pBest, pArg, pColl);
> if ((max && cmp < 0) || (!max && cmp > 0)) {
> - sqlVdbeMemCopy(pBest, pArg);
> + if (sqlVdbeMemCopy(pBest, pArg) != 0)
> + return -1;
> } else {
> - sqlSkipAccumulatorLoad(context);
> + sqlSkipAccumulatorLoad(ctx);
> }
> } else {
> - pBest->db = sql_context_db_handle(context);
> - sqlVdbeMemCopy(pBest, pArg);
> + pBest->db = sql_get();
> + if (sqlVdbeMemCopy(pBest, pArg) != 0)
> + return -1;
> }
> + return 0;
> }
>
> -static void
> -minMaxFinalize(sql_context * context)
> +static int
> +sql_builtin_minmax_finalize(struct func *func, struct port *args,
> + struct port *ret)
> {
> - sql_value *pRes;
> - pRes = (sql_value *) sql_aggregate_context(context, 0);
> - if (pRes) {
> - if (pRes->flags) {
> - sql_result_value(context, pRes);
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> + struct Mem *res =
> + (struct Mem *) sql_aggregate_context(ctx, 0);
> + if (res != NULL) {
> + if (res->flags != 0 &&
> + sqlVdbeMemCopy(val, res) != 0) {
> + sqlVdbeMemRelease(res);
> + return -1;
> }
> - sqlVdbeMemRelease(pRes);
> + sqlVdbeMemRelease(res);
> }
> + return 0;
> }
>
> /*
> * group_concat(EXPR, ?SEPARATOR?)
> */
> -static void
> -groupConcatStep(sql_context * context, int argc, sql_value ** argv)
> +static int
> +sql_builtin_group_concat_step(struct func *func, struct port *args,
> + struct port *ret)
> {
> - const char *zVal;
> - StrAccum *pAccum;
> - const char *zSep;
> - int nVal, nSep;
> + (void) func;
> + (void) ret;
> + uint32_t argc;
> + struct Mem *argv = (struct Mem *)port_get_vdbemem(args, &argc);
> + if (argv == NULL)
> + return -1;
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> +
> assert(argc == 1 || argc == 2);
> - if (sql_value_is_null(argv[0]))
> - return;
> - pAccum =
> - (StrAccum *) sql_aggregate_context(context, sizeof(*pAccum));
> -
> - if (pAccum) {
> - sql *db = sql_context_db_handle(context);
> - int firstTerm = pAccum->mxAlloc == 0;
> - pAccum->mxAlloc = db->aLimit[SQL_LIMIT_LENGTH];
> - if (!firstTerm) {
> - if (argc == 2) {
> - zSep = (char *)sql_value_text(argv[1]);
> - nSep = sql_value_bytes(argv[1]);
> - } else {
> - zSep = ",";
> - nSep = 1;
> - }
> - if (zSep)
> - sqlStrAccumAppend(pAccum, zSep, nSep);
> + if (sql_value_is_null(&argv[0]))
> + return 0;
> + struct StrAccum *p =
> + (struct StrAccum *) sql_aggregate_context(ctx, sizeof(*p));
> + if (p == NULL)
> + return -1;
> +
> + struct sql *db = sql_get();
> + int firstTerm = p->mxAlloc == 0;
> + p->mxAlloc = db->aLimit[SQL_LIMIT_LENGTH];
> + if (!firstTerm) {
> + const char *zSep = NULL;
> + int nSep;
> + if (argc == 2) {
> + zSep = (char *)sql_value_text(&argv[1]);
> + nSep = sql_value_bytes(&argv[1]);
> + } else {
> + zSep = ",";
> + nSep = 1;
> }
> - zVal = (char *)sql_value_text(argv[0]);
> - nVal = sql_value_bytes(argv[0]);
> - if (zVal)
> - sqlStrAccumAppend(pAccum, zVal, nVal);
> + if (zSep != NULL)
> + sqlStrAccumAppend(p, zSep, nSep);
> }
> + const char *z = (char *)sql_value_text(&argv[0]);
> + int n = sql_value_bytes(&argv[0]);
> + if (z != NULL)
> + sqlStrAccumAppend(p, z, n);
> + return 0;
> }
>
> -static void
> -groupConcatFinalize(sql_context * context)
> +static int
> +sql_builtin_group_concat_finalize(struct func *func, struct port *args,
> + struct port *ret)
> {
> - StrAccum *pAccum;
> - pAccum = sql_aggregate_context(context, 0);
> - if (pAccum) {
> - if (pAccum->accError == STRACCUM_TOOBIG) {
> - diag_set(ClientError, ER_SQL_EXECUTE, "string or blob "\
> - "too big");
> - context->is_aborted = true;
> - } else if (pAccum->accError == STRACCUM_NOMEM) {
> - context->is_aborted = true;
> - } else {
> - sql_result_text(context,
> - sqlStrAccumFinish(pAccum),
> - pAccum->nChar, sql_free);
> - }
> + (void) func;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql_context *ctx = (struct sql_context *) port_get_context(args);
> + assert(ctx != NULL);
> +
> +
> + struct StrAccum *p = sql_aggregate_context(ctx, 0);
> + if (p == NULL)
> + return 0;
> + if (p->accError == STRACCUM_TOOBIG) {
> + diag_set(ClientError, ER_SQL_EXECUTE, "string or blob too big");
> + return -1;
> + } else if (p->accError == STRACCUM_NOMEM) {
> + return -1;
> + } else {
> + if (sqlVdbeMemSetStr(val, sqlStrAccumFinish(p), p->nChar,
> + 1, SQL_DYNAMIC) != 0)
> + return -1;
> }
> + return 0;
> }
>
> int
> @@ -1743,66 +2064,77 @@ sqlRegisterBuiltinFunctions(void)
> * For peak efficiency, put the most frequently used function last.
> */
> static FuncDef aBuiltinFunc[] = {
> - FUNCTION_COLL(trim, 1, 3, 0, trim_func_one_arg),
> - FUNCTION_COLL(trim, 2, 3, 0, trim_func_two_args),
> - FUNCTION_COLL(trim, 3, 3, 0, trim_func_three_args),
> - FUNCTION(min, -1, 0, 1, minmaxFunc, FIELD_TYPE_SCALAR),
> + FUNCTION_COLL(trim, 1, 3, 0, sql_builtin_trim_one_arg),
> + FUNCTION_COLL(trim, 2, 3, 0, sql_builtin_trim_two_args),
> + FUNCTION_COLL(trim, 3, 3, 0, sql_builtin_trim_three_args),
> + FUNCTION(min, -1, 0, 1, sql_builtin_minmax, FIELD_TYPE_SCALAR),
> FUNCTION(min, 0, 0, 1, 0, FIELD_TYPE_SCALAR),
> - AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
> - SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR),
> - FUNCTION(max, -1, 1, 1, minmaxFunc, FIELD_TYPE_SCALAR),
> + AGGREGATE2(min, 1, 0, 1, sql_builtin_minmax_step,
> + sql_builtin_minmax_finalize, SQL_FUNC_MINMAX,
> + FIELD_TYPE_SCALAR),
> + FUNCTION(max, -1, 1, 1, sql_builtin_minmax, FIELD_TYPE_SCALAR),
> FUNCTION(max, 0, 1, 1, 0, FIELD_TYPE_SCALAR),
> - AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
> - SQL_FUNC_MINMAX, FIELD_TYPE_SCALAR),
> - FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQL_FUNC_TYPEOF,
> + AGGREGATE2(max, 1, 1, 1, sql_builtin_minmax_step,
> + sql_builtin_minmax_finalize, SQL_FUNC_MINMAX,
> + FIELD_TYPE_SCALAR),
> + FUNCTION2(typeof, 1, 0, 0, sql_builtin_typeof, SQL_FUNC_TYPEOF,
> FIELD_TYPE_STRING),
> - FUNCTION2(length, 1, 0, 0, lengthFunc, SQL_FUNC_LENGTH,
> + FUNCTION2(length, 1, 0, 0, sql_builtin_length, SQL_FUNC_LENGTH,
> FIELD_TYPE_INTEGER),
> - FUNCTION(position, 2, 0, 1, position_func, FIELD_TYPE_INTEGER),
> - FUNCTION(printf, -1, 0, 0, printfFunc, FIELD_TYPE_STRING),
> - FUNCTION(unicode, 1, 0, 0, unicodeFunc, FIELD_TYPE_STRING),
> - FUNCTION(char, -1, 0, 0, charFunc, FIELD_TYPE_STRING),
> - FUNCTION(abs, 1, 0, 0, absFunc, FIELD_TYPE_NUMBER),
> - FUNCTION(round, 1, 0, 0, roundFunc, FIELD_TYPE_INTEGER),
> - FUNCTION(round, 2, 0, 0, roundFunc, FIELD_TYPE_INTEGER),
> - FUNCTION_COLL(upper, 1, 0, 1, UpperICUFunc),
> - FUNCTION_COLL(lower, 1, 0, 1, LowerICUFunc),
> - FUNCTION(hex, 1, 0, 0, hexFunc, FIELD_TYPE_STRING),
> - FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQL_FUNC_COALESCE,
> + FUNCTION(position, 2, 0, 1, sql_builtin_position,
> + FIELD_TYPE_INTEGER),
> + FUNCTION(printf, -1, 0, 0, sql_builtin_printf,
> + FIELD_TYPE_STRING),
> + FUNCTION(unicode, 1, 0, 0, sql_builtin_unicode,
> + FIELD_TYPE_STRING),
> + FUNCTION(char, -1, 0, 0, sql_builtin_char, FIELD_TYPE_STRING),
> + FUNCTION(abs, 1, 0, 0, sql_builtin_abs, FIELD_TYPE_NUMBER),
> + FUNCTION(round, 1, 0, 0, sql_builtin_round, FIELD_TYPE_INTEGER),
> + FUNCTION(round, 2, 0, 0, sql_builtin_round, FIELD_TYPE_INTEGER),
> + FUNCTION_COLL(upper, 1, 0, 1, sql_builtin_ICUUpper),
> + FUNCTION_COLL(lower, 1, 0, 1, sql_builtin_ICULower),
> + FUNCTION(hex, 1, 0, 0, sql_builtin_hex, FIELD_TYPE_STRING),
> + FUNCTION2(ifnull, 2, 0, 0, sql_builtin_noop, SQL_FUNC_COALESCE,
> FIELD_TYPE_INTEGER),
> - VFUNCTION(random, 0, 0, 0, randomFunc, FIELD_TYPE_INTEGER),
> - VFUNCTION(randomblob, 1, 0, 0, randomBlob, FIELD_TYPE_SCALAR),
> - FUNCTION(nullif, 2, 0, 1, nullifFunc, FIELD_TYPE_SCALAR),
> - FUNCTION(version, 0, 0, 0, sql_func_version, FIELD_TYPE_STRING),
> - FUNCTION(quote, 1, 0, 0, quoteFunc, FIELD_TYPE_STRING),
> - VFUNCTION(row_count, 0, 0, 0, sql_row_count, FIELD_TYPE_INTEGER),
> - FUNCTION_COLL(replace, 3, 0, 0, replaceFunc),
> - FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc, FIELD_TYPE_SCALAR),
> - FUNCTION_COLL(substr, 2, 0, 0, substrFunc),
> - FUNCTION_COLL(substr, 3, 0, 0, substrFunc),
> - AGGREGATE(sum, 1, 0, 0, sum_step, sumFinalize,
> - FIELD_TYPE_NUMBER),
> - AGGREGATE(total, 1, 0, 0, sum_step, totalFinalize,
> - FIELD_TYPE_NUMBER),
> - AGGREGATE(avg, 1, 0, 0, sum_step, avgFinalize,
> - FIELD_TYPE_NUMBER),
> - AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
> - SQL_FUNC_COUNT, FIELD_TYPE_INTEGER),
> - AGGREGATE(count, 1, 0, 0, countStep, countFinalize,
> + VFUNCTION(random, 0, 0, 0, sql_builtin_random,
> + FIELD_TYPE_INTEGER),
> + VFUNCTION(randomblob, 1, 0, 0, sql_builtin_random_blob,
> + FIELD_TYPE_SCALAR),
> + FUNCTION(nullif, 2, 0, 1, sql_builtin_nullif,
> + FIELD_TYPE_SCALAR),
> + FUNCTION(version, 0, 0, 0, sql_builtin_version,
> + FIELD_TYPE_STRING),
> + FUNCTION(quote, 1, 0, 0, sql_builtin_quote, FIELD_TYPE_STRING),
> + VFUNCTION(row_count, 0, 0, 0, sql_builtin_row_count,
> + FIELD_TYPE_INTEGER),
> + FUNCTION_COLL(replace, 3, 0, 0, sql_builtin_replace),
> + FUNCTION(zeroblob, 1, 0, 0, sql_builtin_zeroblob,
> + FIELD_TYPE_SCALAR),
> + FUNCTION_COLL(substr, 2, 0, 0, sql_builtin_substr),
> + FUNCTION_COLL(substr, 3, 0, 0, sql_builtin_substr),
> + AGGREGATE(sum, 1, 0, 0, sql_builtin_sum_step,
> + sql_builtin_sum_finalize, FIELD_TYPE_NUMBER),
> + AGGREGATE(total, 1, 0, 0, sql_builtin_sum_step,
> + sql_builtin_total_finalize, FIELD_TYPE_NUMBER),
> + AGGREGATE(avg, 1, 0, 0, sql_builtin_sum_step,
> + sql_builtin_avg_finalize, FIELD_TYPE_NUMBER),
> + AGGREGATE2(count, 0, 0, 0, sql_builtin_count_step,
> + sql_builtin_count_finalize, SQL_FUNC_COUNT,
> + FIELD_TYPE_INTEGER),
> + AGGREGATE(count, 1, 0, 0, sql_builtin_count_step,
> + sql_builtin_count_finalize, FIELD_TYPE_INTEGER),
> + AGGREGATE(group_concat, 1, 0, 0, sql_builtin_group_concat_step,
> + sql_builtin_group_concat_finalize, FIELD_TYPE_STRING),
> + AGGREGATE(group_concat, 2, 0, 0, sql_builtin_group_concat_step,
> + sql_builtin_group_concat_finalize, FIELD_TYPE_STRING),
> + FUNCTION2(like, 2, 1, 0, sql_builtin_like, SQL_FUNC_LIKE,
> + FIELD_TYPE_INTEGER),
> + FUNCTION2(like, 3, 1, 0, sql_builtin_like, SQL_FUNC_LIKE,
> FIELD_TYPE_INTEGER),
> - AGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
> - groupConcatFinalize, FIELD_TYPE_STRING),
> - AGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
> - groupConcatFinalize, FIELD_TYPE_STRING),
> -
> - LIKEFUNC(like, 2, 1, SQL_FUNC_LIKE,
> - FIELD_TYPE_INTEGER),
> - LIKEFUNC(like, 3, 1, SQL_FUNC_LIKE,
> - FIELD_TYPE_INTEGER),
> FUNCTION(coalesce, 1, 0, 0, 0, FIELD_TYPE_SCALAR),
> FUNCTION(coalesce, 0, 0, 0, 0, FIELD_TYPE_SCALAR),
> - FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQL_FUNC_COALESCE,
> - FIELD_TYPE_SCALAR),
> + FUNCTION2(coalesce, -1, 0, 0, sql_builtin_noop,
> + SQL_FUNC_COALESCE, FIELD_TYPE_SCALAR),
> };
> sql_register_analyze_builtins();
> sqlRegisterDateTimeFunctions();
> diff --git a/src/box/sql/main.c b/src/box/sql/main.c
> index 89d83d242..baaf7dcc9 100644
> --- a/src/box/sql/main.c
> +++ b/src/box/sql/main.c
> @@ -38,6 +38,7 @@
> #include "sqlInt.h"
> #include "vdbeInt.h"
> #include "version.h"
> +#include "box/port.h"
> #include "box/session.h"
>
> /*
> @@ -151,12 +152,18 @@ sql_initialize(void)
> return 0;
> }
>
> -void
> -sql_row_count(struct sql_context *context, MAYBE_UNUSED int unused1,
> - MAYBE_UNUSED sql_value **unused2)
> +int
> +sql_builtin_row_count(struct func *func, struct port *args, struct port *ret)
> {
> - sql *db = sql_context_db_handle(context);
> - sql_result_int(context, db->nChange);
> + (void) func;
> + (void) args;
> + struct Mem *val = vdbemem_alloc_on_region(1);
> + if (val == NULL)
> + return -1;
> + port_vdbemem_create(ret, (struct sql_value *)val, 1, NULL);
> + struct sql *db = sql_get();
> + sqlVdbeMemSetInt64(val, db->nChange);
> + return 0;
> }
>
> /*
> @@ -214,16 +221,15 @@ sqlRollbackAll(Vdbe * pVdbe)
> * is returned and the mallocFailed flag cleared.
> */
> int
> -sqlCreateFunc(sql * db,
> - const char *zFunctionName,
> - enum field_type type,
> - int nArg,
> - int flags,
> - void *pUserData,
> - void (*xSFunc) (sql_context *, int, sql_value **),
> - void (*xStep) (sql_context *, int, sql_value **),
> - void (*xFinal) (sql_context *),
> - FuncDestructor * pDestructor)
> +sqlCreateFunc(struct sql * db, const char *zFunctionName, enum field_type type,
> + int nArg, int flags, void *pUserData,
> + int (*xSFunc) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xStep) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xFinal) (struct func *func, struct port *args,
> + struct port *ret),
> + struct FuncDestructor * pDestructor)
> {
> FuncDef *p;
> int extraFlags;
> @@ -284,19 +290,17 @@ sqlCreateFunc(sql * db,
> return 0;
> }
>
> +
> int
> -sql_create_function_v2(sql * db,
> - const char *zFunc,
> - enum field_type type,
> - int nArg,
> - int flags,
> - void *p,
> - void (*xSFunc) (sql_context *, int,
> - sql_value **),
> - void (*xStep) (sql_context *, int,
> - sql_value **),
> - void (*xFinal) (sql_context *),
> - void (*xDestroy) (void *))
> +sql_create_function_v2(struct sql * db, const char *zFunc, enum field_type type,
> + int nArg, int flags, void *p,
> + int (*xSFunc) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xStep) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xFinal) (struct func *func, struct port *args,
> + struct port *ret),
> + void (*xDestroy) (void *ctx))
> {
> FuncDestructor *pArg = 0;
>
> diff --git a/src/box/sql/printf.c b/src/box/sql/printf.c
> index 98372f028..b9e6fbc31 100644
> --- a/src/box/sql/printf.c
> +++ b/src/box/sql/printf.c
> @@ -10,6 +10,7 @@
> * sql.
> */
> #include "sqlInt.h"
> +#include "vdbeInt.h"
>
> /*
> * Conversion types fall into various categories as defined by the
> @@ -143,7 +144,7 @@ getIntArg(PrintfArguments * p)
> {
> if (p->nArg <= p->nUsed)
> return 0;
> - return sql_value_int64(p->apArg[p->nUsed++]);
> + return sql_value_int64(&p->apArg[p->nUsed++]);
> }
>
> static double
> @@ -151,7 +152,7 @@ getDoubleArg(PrintfArguments * p)
> {
> if (p->nArg <= p->nUsed)
> return 0.0;
> - return sql_value_double(p->apArg[p->nUsed++]);
> + return sql_value_double(&p->apArg[p->nUsed++]);
> }
>
> static char *
> @@ -159,7 +160,7 @@ getTextArg(PrintfArguments * p)
> {
> if (p->nArg <= p->nUsed)
> return 0;
> - return (char *)sql_value_text(p->apArg[p->nUsed++]);
> + return (char *)sql_value_text(&p->apArg[p->nUsed++]);
> }
>
> /*
> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index 515fce3a9..ce15500e5 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -263,6 +263,7 @@
> #include <assert.h>
> #include <stddef.h>
>
> +struct port;
> typedef long long int sql_int64;
> typedef unsigned long long int sql_uint64;
> typedef sql_int64 sql_int64;
> @@ -379,52 +380,6 @@ sql_value_is_null(sql_value *value)
> return sql_value_type(value) == MP_NIL;
> }
>
> -sql *
> -sql_context_db_handle(sql_context *);
> -
> -
> -void
> -sql_result_blob(sql_context *, const void *,
> - int, void (*)(void *));
> -
> -void
> -sql_result_blob64(sql_context *, const void *,
> - sql_uint64, void (*)(void *));
> -
> -void
> -sql_result_double(sql_context *, double);
> -
> -void
> -sql_result_int(sql_context *, int);
> -
> -void
> -sql_result_bool(struct sql_context *ctx, bool value);
> -
> -void
> -sql_result_int64(sql_context *, sql_int64);
> -
> -void
> -sql_result_null(sql_context *);
> -
> -void
> -sql_result_text(sql_context *, const char *,
> - int, void (*)(void *));
> -
> -void
> -sql_result_text64(sql_context *, const char *,
> - sql_uint64, void (*)(void *));
> -
> -void
> -sql_result_value(sql_context *,
> - sql_value *);
> -
> -void
> -sql_result_zeroblob(sql_context *, int n);
> -
> -int
> -sql_result_zeroblob64(sql_context *,
> - sql_uint64 n);
> -
> char *
> sql_mprintf(const char *, ...);
> char *
> @@ -537,9 +492,8 @@ sql_randomness(int N, void *P);
> /**
> * Return the number of affected rows in the last SQL statement.
> */
> -void
> -sql_row_count(struct sql_context *context, MAYBE_UNUSED int unused1,
> - MAYBE_UNUSED sql_value **unused2);
> +int
> +sql_builtin_row_count(struct func *func, struct port *args, struct port *ret);
>
> void *
> sql_user_data(sql_context *);
> @@ -569,22 +523,15 @@ sql_initialize(void);
> #define SQL_DETERMINISTIC 0x800
>
> int
> -sql_create_function_v2(sql * db,
> - const char *zFunctionName,
> - enum field_type type,
> - int nArg,
> - int flags,
> - void *pApp,
> - void (*xFunc) (sql_context *,
> - int,
> - sql_value **),
> - void (*xStep) (sql_context *,
> - int,
> - sql_value **),
> - void (*xFinal)
> - (sql_context *),
> - void (*xDestroy) (void *)
> - );
> +sql_create_function_v2(struct sql * db, const char *zFunc, enum field_type type,
> + int nArg, int flags, void *p,
> + int (*xSFunc) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xStep) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xFinal) (struct func *func, struct port *args,
> + struct port *ret),
> + void (*xDestroy) (void *ctx));
>
> #define SQL_OPEN_READONLY 0x00000001 /* Ok for sql_open_v2() */
> #define SQL_OPEN_READWRITE 0x00000002 /* Ok for sql_open_v2() */
> @@ -1109,7 +1056,7 @@ struct sql {
> u8 dfltLockMode; /* Default locking-mode for attached dbs */
> u8 mTrace; /* zero or more sql_TRACE flags */
> u32 magic; /* Magic number for detect library misuse */
> - /** Value returned by sql_row_count(). */
> + /** Value returned by sql_builtin_row_count(). */
> int nChange;
> int aLimit[SQL_N_LIMIT]; /* Limits */
> int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
> @@ -1219,8 +1166,8 @@ struct FuncDef {
> u16 funcFlags; /* Some combination of sql_FUNC_* */
> void *pUserData; /* User data parameter */
> FuncDef *pNext; /* Next function with same name */
> - void (*xSFunc) (sql_context *, int, sql_value **); /* func or agg-step */
> - void (*xFinalize) (sql_context *); /* Agg finalizer */
> + int (*xSFunc) (struct func *func, struct port *args, struct port *ret);
> + int (*xFinalize) (struct func *func, struct port *args, struct port *ret);
> const char *zName; /* SQL name of the function. */
> union {
> FuncDef *pHash; /* Next with a different name but the same hash */
> @@ -2667,7 +2614,7 @@ int sqlIsNaN(double);
> struct PrintfArguments {
> int nArg; /* Total number of arguments */
> int nUsed; /* Number of arguments used so far */
> - sql_value **apArg; /* The argument values */
> + struct Mem *apArg; /* The argument values */
> };
>
> void sqlVXPrintf(StrAccum *, const char *, va_list);
> @@ -4273,12 +4220,16 @@ sql_key_info_to_key_def(struct sql_key_info *key_info);
> int
> sql_is_like_func(struct sql *db, struct Expr *expr, int *is_like_ci);
>
> -int sqlCreateFunc(sql *, const char *, enum field_type,
> - int, int, void *,
> - void (*)(sql_context *, int, sql_value **),
> - void (*)(sql_context *, int, sql_value **),
> - void (*)(sql_context *),
> - FuncDestructor * pDestructor);
> +int
> +sqlCreateFunc(struct sql * db, const char *zFunctionName, enum field_type type,
> + int nArg, int flags, void *pUserData,
> + int (*xSFunc) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xStep) (struct func *func, struct port *args,
> + struct port *ret),
> + int (*xFinal) (struct func *func, struct port *args,
> + struct port *ret),
> + struct FuncDestructor * pDestructor);
>
> /** Set OOM error flag. */
> static inline void
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index c8887f9b7..92c99877a 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -44,6 +44,7 @@
> #include "box/fk_constraint.h"
> #include "box/txn.h"
> #include "box/tuple.h"
> +#include "box/port.h"
> #include "sqlInt.h"
> #include "vdbeInt.h"
> #include "tarantoolInt.h"
> @@ -1719,21 +1720,18 @@ case OP_CollSeq: {
> * See also: Function0, AggStep, AggFinal
> */
> case OP_Function0: {
> - int n;
> sql_context *pCtx;
>
> assert(pOp->p4type==P4_FUNCDEF);
> - n = pOp->p5;
> assert(pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor));
> - assert(n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1));
> - assert(pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n);
> - pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sql_value*));
> + assert(pOp->p5 == 0 ||
> + (pOp->p2 > 0 && pOp->p2 + pOp->p5 <= (p->nMem+1 - p->nCursor)+1));
> + assert(pOp->p3 < pOp->p2 || pOp->p3>=pOp->p2 + pOp->p5);
> + pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx));
> if (pCtx==0) goto no_mem;
> - pCtx->pOut = 0;
> pCtx->pFunc = pOp->p4.pFunc;
> pCtx->iOp = (int)(pOp - aOp);
> pCtx->pVdbe = p;
> - pCtx->argc = n;
> pOp->p4type = P4_FUNCCTX;
> pOp->p4.pCtx = pCtx;
> pOp->opcode = OP_Function;
> @@ -1741,7 +1739,6 @@ case OP_Function0: {
> FALLTHROUGH;
> }
> case OP_Function: {
> - int i;
> sql_context *pCtx;
>
> assert(pOp->p4type==P4_FUNCCTX);
> @@ -1753,33 +1750,39 @@ case OP_Function: {
> * reinitializes the relavant parts of the sql_context object
> */
> pOut = &aMem[pOp->p3];
> - if (pCtx->pOut != pOut) {
> - pCtx->pOut = pOut;
> - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
> - }
> + struct region *region = &fiber()->gc;
> + size_t region_svp = region_used(region);
> + struct port args, ret;
> + port_vdbemem_create(&args, (struct sql_value *)&aMem[pOp->p2],
> + pOp->p5, pCtx);
>
> - memAboutToChange(p, pCtx->pOut);
> + memAboutToChange(p, pOut);
> #ifdef SQL_DEBUG
> - for(i=0; i<pCtx->argc; i++) {
> - assert(memIsValid(pCtx->argv[i]));
> - REGISTER_TRACE(p, pOp->p2+i, pCtx->argv[i]);
> + for(int i = 0; i < pOp->p5; i++) {
> + assert(memIsValid(&aMem[pOp->p2 + i]));
> + REGISTER_TRACE(p, pOp->p2+i, &aMem[pOp->p2 + i]);
> }
> #endif
> - MemSetTypeFlag(pCtx->pOut, MEM_Null);
> - pCtx->is_aborted = false;
> - (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
> -
> - /* If the function returned an error, throw an exception */
> - if (pCtx->is_aborted)
> + MemSetTypeFlag(pOut, MEM_Null);
> + rc = (*pCtx->pFunc->xSFunc)(NULL, &args, &ret);
> + if (rc != 0)
> + goto abort_due_to_error;
> + uint32_t size;
> + struct Mem *mem = (struct Mem *)port_get_vdbemem(&ret, &size);
> + if (mem == NULL) {
> + region_truncate(region, region_svp);
> goto abort_due_to_error;
> + }
> + *pOut = mem[0];
> + region_truncate(region, region_svp);
>
> /* Copy the result of the function into register P3 */
> if (pOut->flags & (MEM_Str|MEM_Blob)) {
> - if (sqlVdbeMemTooBig(pCtx->pOut)) goto too_big;
> + if (sqlVdbeMemTooBig(pOut)) goto too_big;
> }
>
> - REGISTER_TRACE(p, pOp->p3, pCtx->pOut);
> - UPDATE_MAX_BLOBSIZE(pCtx->pOut);
> + REGISTER_TRACE(p, pOp->p3, pOut);
> + UPDATE_MAX_BLOBSIZE(pOut);
> break;
> }
>
> @@ -4964,21 +4967,19 @@ case OP_DecrJumpZero: { /* jump, in1 */
> * step function.
> */
> case OP_AggStep0: {
> - int n;
> sql_context *pCtx;
>
> - assert(pOp->p4type==P4_FUNCDEF);
> - n = pOp->p5;
> + assert(pOp->p4type == P4_FUNCDEF);
> assert(pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor));
> - assert(n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1));
> - assert(pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n);
> - pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sql_value*));
> + assert(pOp->p5 == 0 ||
> + (pOp->p2 > 0 && pOp->p2 + pOp->p5 <= (p->nMem+1 - p->nCursor)+1));
> + assert(pOp->p3 < pOp->p2 || pOp->p3 >= pOp->p2 + pOp->p5);
> + pCtx = sqlDbMallocRawNN(db, sizeof(*pCtx));
> if (pCtx==0) goto no_mem;
> pCtx->pMem = 0;
> pCtx->pFunc = pOp->p4.pFunc;
> pCtx->iOp = (int)(pOp - aOp);
> pCtx->pVdbe = p;
> - pCtx->argc = n;
> pOp->p4type = P4_FUNCCTX;
> pOp->p4.pCtx = pCtx;
> pOp->opcode = OP_AggStep;
> @@ -4988,41 +4989,33 @@ case OP_AggStep0: {
> case OP_AggStep: {
> int i;
> sql_context *pCtx;
> - Mem *pMem;
> Mem t;
>
> assert(pOp->p4type==P4_FUNCCTX);
> pCtx = pOp->p4.pCtx;
> - pMem = &aMem[pOp->p3];
> + pCtx->pMem = &aMem[pOp->p3];
>
> - /* If this function is inside of a trigger, the register array in aMem[]
> - * might change from one evaluation to the next. The next block of code
> - * checks to see if the register array has changed, and if so it
> - * reinitializes the relavant parts of the sql_context object
> - */
> - if (pCtx->pMem != pMem) {
> - pCtx->pMem = pMem;
> - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
> - }
> + struct region *region = &fiber()->gc;
> + size_t region_svp = region_used(region);
> + struct port args, ret;
> + port_vdbemem_create(&args, (struct sql_value *)&aMem[pOp->p2],
> + pOp->p5, pCtx);
>
> #ifdef SQL_DEBUG
> - for(i=0; i<pCtx->argc; i++) {
> - assert(memIsValid(pCtx->argv[i]));
> - REGISTER_TRACE(p, pOp->p2+i, pCtx->argv[i]);
> + for(i = 0; i < pOp->p5; i++) {
> + assert(memIsValid(&aMem[pOp->p2 + i]));
> + REGISTER_TRACE(p, pOp->p2+i, &aMem[pOp->p2 + i]);
> }
> #endif
>
> - pMem->n++;
> sqlVdbeMemInit(&t, db, MEM_Null);
> - pCtx->pOut = &t;
> - pCtx->is_aborted = false;
> + pOut = &t;
> pCtx->skipFlag = 0;
> - (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
> - if (pCtx->is_aborted) {
> - sqlVdbeMemRelease(&t);
> + rc = (*pCtx->pFunc->xSFunc)(NULL, &args, &ret);
> + region_truncate(region, region_svp);
> + if (rc != 0)
> goto abort_due_to_error;
> - }
> - assert(t.flags==MEM_Null);
> +
> if (pCtx->skipFlag) {
> assert(pOp[-1].opcode==OP_CollSeq);
> i = pOp[-1].p1;
> diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
> index 6bfeecc85..4c8363a22 100644
> --- a/src/box/sql/vdbeInt.h
> +++ b/src/box/sql/vdbeInt.h
> @@ -181,10 +181,7 @@ struct Mem {
> u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
> sql *db; /* The associated database connection */
> void (*xDel) (void *); /* Destructor for Mem.z - only valid if MEM_Dyn */
> -#ifdef SQL_DEBUG
> Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
> - void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
> -#endif
> };
>
> /*
> @@ -294,19 +291,11 @@ mem_apply_numeric_type(struct Mem *record);
> * (Mem) which are only defined there.
> */
> struct sql_context {
> - Mem *pOut; /* The return value is stored here */
> FuncDef *pFunc; /* Pointer to function information */
> Mem *pMem; /* Memory cell used to store aggregate context */
> Vdbe *pVdbe; /* The VM that owns this context */
> int iOp; /* Instruction number of OP_Function */
> - /*
> - * True, if an error occurred during the execution of the
> - * function.
> - */
> - bool is_aborted;
> u8 skipFlag; /* Skip accumulator loading if true */
> - u8 argc; /* Number of arguments */
> - sql_value *argv[1]; /* Argument set */
> };
>
> /* A bitfield type for use inside of structures. Always follow with :N where
> @@ -584,4 +573,11 @@ char *
> sql_vdbe_mem_encode_tuple(struct Mem *fields, uint32_t field_count,
> uint32_t *tuple_size, struct region *region);
>
> +/**
> + * Allocate a sequence of initialized vdbe memory registers
> + * on region.
> + */
> +struct Mem *
> +vdbemem_alloc_on_region(uint32_t count);
> +
> #endif /* !defined(SQL_VDBEINT_H) */
> diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
> index ef0ab79d2..d014e8258 100644
> --- a/src/box/sql/vdbeapi.c
> +++ b/src/box/sql/vdbeapi.c
> @@ -69,6 +69,23 @@ invokeProfileCallback(sql * db, Vdbe * p)
> #define checkProfileCallback(DB,P) \
> if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); }
>
> +struct Mem *
> +vdbemem_alloc_on_region(uint32_t count)
> +{
> + struct region *region = &fiber()->gc;
> + struct Mem *ret = region_alloc(region, count * sizeof(*ret));
> + if (ret == NULL) {
> + diag_set(OutOfMemory, count * sizeof(*ret), "sqlValueNew", "ret");
> + return NULL;
> + }
> + memset(ret, 0, count * sizeof(*ret));
> + for (uint32_t i = 0; i < count; i++) {
> + sqlVdbeMemInit(&ret[i], sql_get(), MEM_Null);
> + assert(memIsValid(&ret[i]));
> + }
> + return ret;
> +}
> +
> /*
> * The following routine destroys a virtual machine that is created by
> * the sql_compile() routine. The integer returned is an SQL_
> @@ -254,22 +271,9 @@ sql_value_free(sql_value * pOld)
> * The invokeValueDestructor(P,X) routine invokes destructor function X()
> * on value P is not going to be used and need to be destroyed.
> */
> -static void
> -setResultStrOrError(sql_context * pCtx, /* Function context */
> - const char *z, /* String pointer */
> - int n, /* Bytes in string, or negative */
> - void (*xDel) (void *) /* Destructor function */
> - )
> -{
> - if (sqlVdbeMemSetStr(pCtx->pOut, z, n, 1, xDel) != 0)
> - pCtx->is_aborted = true;
> -}
>
> static int
> -invokeValueDestructor(const void *p, /* Value to destroy */
> - void (*xDel) (void *), /* The destructor */
> - sql_context *pCtx /* Set an error if no NULL */
> - )
> +invokeValueDestructor(const void *p, void (*xDel) (void *))
> {
> assert(xDel != SQL_DYNAMIC);
> if (xDel == 0) {
> @@ -279,114 +283,9 @@ invokeValueDestructor(const void *p, /* Value to destroy */
> } else {
> xDel((void *)p);
> }
> - if (pCtx) {
> - diag_set(ClientError, ER_SQL_EXECUTE, "string or blob is too "\
> - "big");
> - pCtx->is_aborted = true;
> - }
> return -1;
> }
>
> -void
> -sql_result_blob(sql_context * pCtx,
> - const void *z, int n, void (*xDel) (void *)
> - )
> -{
> - assert(n >= 0);
> - if (sqlVdbeMemSetStr(pCtx->pOut, z, n, 0, xDel) != 0)
> - pCtx->is_aborted = true;
> -}
> -
> -void
> -sql_result_blob64(sql_context * pCtx,
> - const void *z, sql_uint64 n, void (*xDel) (void *)
> - )
> -{
> - assert(xDel != SQL_DYNAMIC);
> - if (n > 0x7fffffff) {
> - (void)invokeValueDestructor(z, xDel, pCtx);
> - } else {
> - setResultStrOrError(pCtx, z, (int)n, xDel);
> - }
> -}
> -
> -void
> -sql_result_double(sql_context * pCtx, double rVal)
> -{
> - sqlVdbeMemSetDouble(pCtx->pOut, rVal);
> -}
> -
> -void
> -sql_result_int(sql_context * pCtx, int iVal)
> -{
> - sqlVdbeMemSetInt64(pCtx->pOut, (i64) iVal);
> -}
> -
> -void
> -sql_result_bool(struct sql_context *ctx, bool value)
> -{
> - mem_set_bool(ctx->pOut, value);
> -}
> -
> -void
> -sql_result_int64(sql_context * pCtx, i64 iVal)
> -{
> - sqlVdbeMemSetInt64(pCtx->pOut, iVal);
> -}
> -
> -void
> -sql_result_null(sql_context * pCtx)
> -{
> - sqlVdbeMemSetNull(pCtx->pOut);
> -}
> -
> -void
> -sql_result_text(sql_context * pCtx,
> - const char *z, int n, void (*xDel) (void *)
> - )
> -{
> - setResultStrOrError(pCtx, z, n, xDel);
> -}
> -
> -void
> -sql_result_text64(sql_context * pCtx,
> - const char *z,
> - sql_uint64 n,
> - void (*xDel) (void *))
> -{
> - assert(xDel != SQL_DYNAMIC);
> - if (n > 0x7fffffff) {
> - (void)invokeValueDestructor(z, xDel, pCtx);
> - } else {
> - setResultStrOrError(pCtx, z, (int)n, xDel);
> - }
> -}
> -
> -void
> -sql_result_value(sql_context * pCtx, sql_value * pValue)
> -{
> - sqlVdbeMemCopy(pCtx->pOut, pValue);
> -}
> -
> -void
> -sql_result_zeroblob(sql_context * pCtx, int n)
> -{
> - sqlVdbeMemSetZeroBlob(pCtx->pOut, n);
> -}
> -
> -int
> -sql_result_zeroblob64(sql_context * pCtx, u64 n)
> -{
> - Mem *pOut = pCtx->pOut;
> - if (n > (u64) pOut->db->aLimit[SQL_LIMIT_LENGTH]) {
> - diag_set(ClientError, ER_SQL_EXECUTE, "string or blob is too "\
> - "big");
> - return -1;
> - }
> - sqlVdbeMemSetZeroBlob(pCtx->pOut, (int)n);
> - return 0;
> -}
> -
> /*
> * Execute the statement pStmt, either until a row of data is ready, the
> * statement is completely executed or an error occurs.
> @@ -475,23 +374,6 @@ sql_user_data(sql_context * p)
> return p->pFunc->pUserData;
> }
>
> -/*
> - * Extract the user data from a sql_context structure and return a
> - * pointer to it.
> - *
> - * IMPLEMENTATION-OF: R-46798-50301 The sql_context_db_handle() interface
> - * returns a copy of the pointer to the database connection (the 1st
> - * parameter) of the sql_create_function() and
> - * sql_create_function16() routines that originally registered the
> - * application defined function.
> - */
> -sql *
> -sql_context_db_handle(sql_context * p)
> -{
> - assert(p && p->pOut);
> - return p->pOut->db;
> -}
> -
> /*
> * Return the current time for a statement. If the current time
> * is requested more than once within the same run of a single prepared
> @@ -512,7 +394,7 @@ sqlStmtCurrentTime(sql_context * p)
> p->pVdbe != 0 ? &p->pVdbe->iCurrentTime : &iTime;
> #endif
> if (*piTime == 0) {
> - rc = sqlOsCurrentTimeInt64(p->pOut->db->pVfs, piTime);
> + rc = sqlOsCurrentTimeInt64(sql_get()->pVfs, piTime);
> if (rc)
> *piTime = 0;
> }
> @@ -614,10 +496,7 @@ columnNullValue(void)
> /* .uTemp = */ (u32) 0,
> /* .db = */ (sql *) 0,
> /* .xDel = */ (void (*)(void *))0,
> -#ifdef SQL_DEBUG
> /* .pScopyFrom = */ (Mem *) 0,
> - /* .pFiller = */ (void *)0,
> -#endif
> };
> return &nullMem;
> }
> @@ -934,7 +813,7 @@ sql_bind_blob64(sql_stmt * pStmt,
> {
> assert(xDel != SQL_DYNAMIC);
> if (nData > 0x7fffffff) {
> - return invokeValueDestructor(zData, xDel, 0);
> + return invokeValueDestructor(zData, xDel);
> } else {
> return sql_bind_blob(pStmt, i, zData, (int)nData, xDel);
> }
> @@ -1017,7 +896,7 @@ sql_bind_text64(sql_stmt * pStmt,
> {
> assert(xDel != SQL_DYNAMIC);
> if (nData > 0x7fffffff) {
> - return invokeValueDestructor(zData, xDel, 0);
> + return invokeValueDestructor(zData, xDel);
> } else {
> return bindText(pStmt, i, zData, (int)nData, xDel);
> }
> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
> index f52035b0e..7bd5cdd71 100644
> --- a/src/box/sql/vdbemem.c
> +++ b/src/box/sql/vdbemem.c
> @@ -41,6 +41,7 @@
> #include "tarantoolInt.h"
> #include "box/schema.h"
> #include "box/tuple.h"
> +#include "box/port.h"
> #include "mpstream.h"
>
> #ifdef SQL_DEBUG
> @@ -315,26 +316,31 @@ sqlVdbeMemStringify(Mem * pMem, u8 bForce)
> int
> sqlVdbeMemFinalize(Mem * pMem, FuncDef * pFunc)
> {
> + int rc = 0;
> if (ALWAYS(pFunc && pFunc->xFinalize)) {
> - sql_context ctx;
> - Mem t;
> assert((pMem->flags & MEM_Null) != 0 || pFunc == pMem->u.pDef);
> + struct sql_context ctx;
> memset(&ctx, 0, sizeof(ctx));
> - memset(&t, 0, sizeof(t));
> - t.flags = MEM_Null;
> - t.db = pMem->db;
> - ctx.pOut = &t;
> ctx.pMem = pMem;
> ctx.pFunc = pFunc;
> - pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
> + struct region *region = &fiber()->gc;
> + size_t region_svp = region_used(region);
> + struct port args, ret;
> + port_vdbemem_create(&args, NULL, 0, &ctx);
> + rc = pFunc->xFinalize(NULL, &args, &ret);
> assert((pMem->flags & MEM_Dyn) == 0);
> if (pMem->szMalloc > 0)
> sqlDbFree(pMem->db, pMem->zMalloc);
> - memcpy(pMem, &t, sizeof(t));
> - if (ctx.is_aborted)
> - return -1;
> + uint32_t size;
> + struct Mem *mem = (struct Mem *)port_get_vdbemem(&ret, &size);
> + if (mem != NULL) {
> + *pMem = mem[0];
> + } else {
> + rc = -1;
> + }
> + region_truncate(region, region_svp);
> }
> - return 0;
> + return rc;
> }
>
> /*
> @@ -1239,7 +1245,6 @@ valueFromFunction(sql * db, /* The database connection */
> struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
> )
> {
> - sql_context ctx; /* Context object for function invocation */
> sql_value **apVal = 0; /* Function arguments */
> int nVal = 0; /* Size of apVal[] array */
> FuncDef *pFunc = 0; /* Function definition */
> @@ -1260,6 +1265,8 @@ valueFromFunction(sql * db, /* The database connection */
> ) {
> return 0;
> }
> + struct region *region = &fiber()->gc;
> + size_t region_svp = region_used(region);
>
> if (pList) {
> apVal =
> @@ -1285,15 +1292,25 @@ valueFromFunction(sql * db, /* The database connection */
> }
>
> assert(!pCtx->pParse->is_aborted);
> - memset(&ctx, 0, sizeof(ctx));
> - ctx.pOut = pVal;
> - ctx.pFunc = pFunc;
> - pFunc->xSFunc(&ctx, nVal, apVal);
> - assert(!ctx.is_aborted);
> +
> + struct port args, ret;
> + port_vdbemem_create(&args, (struct sql_value *)apVal, nVal, NULL);
> + if ((*pFunc->xSFunc)(NULL, &args, &ret) != 0) {
> + rc = -1;
> + goto value_from_function_out;
> + }
> sql_value_apply_type(pVal, type);
> assert(rc == 0);
> + uint32_t size;
> + struct Mem *mem = (struct Mem *)port_get_vdbemem(&ret, &size);
> + if (mem == NULL) {
> + rc = -1;
> + goto value_from_function_out;
> + }
> + *pVal = mem[0];
>
> value_from_function_out:
> + region_truncate(region, region_svp);
> if (rc != 0)
> pVal = 0;
> if (apVal) {
> diff --git a/src/lib/core/port.h b/src/lib/core/port.h
> index 09a026df5..cd8cd31f6 100644
> --- a/src/lib/core/port.h
> +++ b/src/lib/core/port.h
> @@ -40,6 +40,7 @@ extern "C" {
> struct obuf;
> struct lua_State;
> struct port;
> +struct sql_value;
>
> /**
> * A single port represents a destination of any output. One such
> @@ -98,6 +99,24 @@ struct port_vtab {
> * is responsible for cleaning up.
> **/
> const char *(*get_msgpack)(struct port *port, uint32_t *size);
> + /**
> + * Get the content of a port as a sequence of vdbe memory
> + * variables. The SQL VDBE uses this representation for
> + * process data. This API is usefull to pass VDBE
> + * variables to sql builtin functions.
> + * The lifecycle of the returned value is
> + * implementation-specific: it may either be returned
> + * directly from the port, in which case the data will
> + * stay alive as long as the port is alive, or it may be
> + * allocated on the fiber()->gc, in which case the caller
> + * is responsible for cleaning up.
> + */
> + struct sql_value *(*get_vdbemem)(struct port *port, uint32_t *size);
> + /**
> + * Get additional implementation-specific information
> + * stored in port. Used for aggregate functions in SQL.
> + */
> + void *(*get_context)(struct port *port);
> /** Destroy a port and release associated resources. */
> void (*destroy)(struct port *port);
> };
> @@ -150,6 +169,18 @@ port_get_msgpack(struct port *port, uint32_t *size)
> return port->vtab->get_msgpack(port, size);
> }
>
> +static inline struct sql_value *
> +port_get_vdbemem(struct port *port, uint32_t *size)
> +{
> + return port->vtab->get_vdbemem(port, size);
> +}
> +
> +static inline const void *
> +port_get_context(struct port *port)
> +{
> + return port->vtab->get_context(port);
> +}
> +
> #if defined(__cplusplus)
> } /* extern "C" */
> #endif /* defined __cplusplus */
> --
> 2.21.0
>
--
Konstantin Osipov, Moscow, Russia
More information about the Tarantool-patches
mailing list