From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 38DCF24F03 for ; Wed, 10 Jul 2019 14:47:18 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id vtXIT2T1r12d for ; Wed, 10 Jul 2019 14:47:18 -0400 (EDT) Received: from smtp53.i.mail.ru (smtp53.i.mail.ru [94.100.177.113]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id C6A71248F5 for ; Wed, 10 Jul 2019 14:47:16 -0400 (EDT) Date: Wed, 10 Jul 2019 21:47:13 +0300 From: Konstantin Osipov Subject: [tarantool-patches] Re: [PATCH v2 10/12] sql: refactor builtins signatures with port Message-ID: <20190710184713.GC5619@atlas> References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: Kirill Shcherbatov Cc: tarantool-patches@freelists.org, korablev@tarantool.org * Kirill Shcherbatov [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 > @@ -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 , 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 > #include > > +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->p3p2 || 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; iargc; 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->p3p2 || 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; iargc; 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