[patches] [PATCH 1/4] sql: Return last updated tuple if requested

Konstantin Osipov kostja at tarantool.org
Tue Mar 20 21:09:00 MSK 2018


* Vladislav Shpilevoy <v.shpilevoy at tarantool.org> [18/03/14 01:14]:
> 
>     If requested by IPROTO protocol , return last updated tuple.
>     Done via extra field in VDBE engine, called sql_options.
>     This new field should in future adsorb most of the sqlite3
>     internals, making it connection-local.
>     Right now this structure contains single field: pointer
>     to pointer to last updated tuple.
> 
>     If non-NULL pointer is passed, tuple_unref() must be called
>     upon end of handling.
> 
>     Part of #2618
> 
> Signed-off-by: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
> ---
>  src/box/execute.c           | 29 ++++++++++++++++++++++-------
>  src/box/execute.h           |  2 +-
>  src/box/iproto.cc           |  4 +++-
>  src/box/lua/sql.c           |  9 ++++++---
>  src/box/sql.c               | 25 ++++++++++++++++---------
>  src/box/sql.h               | 22 ++++++++++++++++++----
>  src/box/sql/analyze.c       |  4 ++--
>  src/box/sql/legacy.c        |  2 +-
>  src/box/sql/sqlite3.h       | 31 ++++++++++++++++++++++++-------
>  src/box/sql/tarantoolInt.h  |  4 ++--
>  src/box/sql/vdbe.c          | 12 ++++++++----
>  src/box/sql/vdbeInt.h       |  5 +++++
>  src/box/sql/vdbeapi.c       |  6 ++++--
>  src/box/sql/vdbeaux.c       | 44 +++++++++++++++++++++++++++-----------------
>  test/unit/sql-bitvec.result | 39 ---------------------------------------
>  15 files changed, 139 insertions(+), 99 deletions(-)
>  delete mode 100644 test/unit/sql-bitvec.result
> 
> diff --git a/src/box/execute.c b/src/box/execute.c
> index 48d166b5b..756db5fd6 100644
> --- a/src/box/execute.c
> +++ b/src/box/execute.c
> @@ -548,12 +548,12 @@ sql_get_description(struct sqlite3_stmt *stmt, struct obuf *out,
>  
>  static inline int
>  sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, int column_count,
> -	    struct port *port, struct region *region)
> +	    struct port *port, struct region *region, struct sql_options *opts)

I think sql_options *opts should not be separable from
sqlite3_stmt. Ideally we should not have any options at all, but
either create modified versions of OP_{Insert|Replace} or
add options/extra parameters to existing OP_{Insert|Replace}.

This can be done at prepare() step, not at execute().

In future, some of the user options will affect prepare, some will
affect execute. So I think you don't need to create a separate
struct sql_options, you already pass the option around in
sql_request.

The tuple itself should be returned from sql_step(), not as "out"
parameter of options.


> +		rc = sqlite3_step(stmt, opts);

You should not need to pass options into *every* step, most steps
will not ever need it.

> @@ -586,13 +587,14 @@ sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, int column_count,
>   */
>  static inline int
>  sql_execute_and_encode(sqlite3 *db, struct sqlite3_stmt *stmt, struct obuf *out,
> -		       uint64_t sync, struct region *region)
> +		       uint64_t sync, struct region *region,
> +		       struct sql_options *opts)
>  {
>  	struct port port;
>  	struct port_tuple *port_tuple = (struct port_tuple *)&port;
>  	port_tuple_create(&port);
>  	int column_count = sqlite3_column_count(stmt);
> -	if (sql_execute(db, stmt, column_count, &port, region) != 0)
> +	if (sql_execute(db, stmt, column_count, &port, region, opts) != 0)
>  		goto err_execute;
>  
>  	/*
> @@ -647,8 +649,11 @@ err_execute:
>  
>  int
>  sql_prepare_and_execute(const struct sql_request *request, struct obuf *out,
> -			struct region *region)
> +			struct region *region, bool is_last_tuple_needed)
>  {
> +	struct sql_options opts;
> +	struct tuple *last_inserted_tuple = NULL;
> +
>  	const char *sql = request->sql_text;
>  	uint32_t len;
>  	sql = mp_decode_str(&sql, &len);
> @@ -665,10 +670,20 @@ sql_prepare_and_execute(const struct sql_request *request, struct obuf *out,
>  	assert(stmt != NULL);
>  	if (sql_bind(request, stmt) != 0)
>  		goto err_stmt;
> +
> +	if (is_last_tuple_needed)
> +		sql_options_create(&opts, &last_inserted_tuple);
> +	else
> +		sql_options_create(&opts, NULL);
> +
>  	if (sql_execute_and_encode(db, stmt, out, request->sync,
> -				   region) != 0)
> +				   region, &opts) != 0)
>  		goto err_stmt;
>  	sqlite3_finalize(stmt);
> +
> +	if (opts.last_tuple != NULL  && *opts.last_tuple != NULL)
> +		tuple_unref(*opts.last_tuple);
> +
>  	return 0;
>  err_stmt:
>  	sqlite3_finalize(stmt);
> diff --git a/src/box/execute.h b/src/box/execute.h
> index 76fdbf5f5..0aef26702 100644
> --- a/src/box/execute.h
> +++ b/src/box/execute.h
> @@ -105,7 +105,7 @@ xrow_decode_sql(const struct xrow_header *row, struct sql_request *request,
>   */
>  int
>  sql_prepare_and_execute(const struct sql_request *request, struct obuf *out,
> -			struct region *region);
> +			struct region *region, bool is_last_tuple_needed);
>  
>  #if defined(__cplusplus)
>  } /* extern "C" { */
> diff --git a/src/box/iproto.cc b/src/box/iproto.cc
> index f7784ef23..9ace0d082 100644
> --- a/src/box/iproto.cc
> +++ b/src/box/iproto.cc
> @@ -1382,13 +1382,15 @@ tx_process_sql(struct cmsg *m)
>  {
>  	struct iproto_msg *msg = tx_accept_msg(m);
>  	struct obuf *out = msg->connection->tx.p_obuf;
> +	bool is_last_tuple_needed = true;
>  
>  	tx_fiber_init(msg->connection->session, msg->header.sync);
>  
>  	if (tx_check_schema(msg->header.schema_version))
>  		goto error;
>  	assert(msg->header.type == IPROTO_EXECUTE);
> -	if (sql_prepare_and_execute(&msg->sql, out, &fiber()->gc) != 0)
> +	if (sql_prepare_and_execute(&msg->sql, out, &fiber()->gc,
> +				    is_last_tuple_needed) != 0)
>  		goto error;
>  	iproto_wpos_create(&msg->wpos, out);
>  	return;
> diff --git a/src/box/lua/sql.c b/src/box/lua/sql.c
> index 16bf2acdb..e45122c9f 100644
> --- a/src/box/lua/sql.c
> +++ b/src/box/lua/sql.c
> @@ -225,10 +225,12 @@ lua_sql_execute(struct lua_State *L)
>  			l->stmt_count --;
>  			break;
>  		}
> -
> +		struct sql_options opts;
> +		sql_options_create(&opts, NULL);
>  		int column_count = sqlite3_column_count(ps->stmt);
>  		if (column_count == 0) {
> -			while ((rc = sqlite3_step(ps->stmt)) == SQLITE_ROW) { ; }
> +			while ((rc = sqlite3_step(ps->stmt,
> +						  &opts)) == SQLITE_ROW);
>  		} else {
>  			char *typestr;
>  			l->column_count = column_count;
> @@ -252,7 +254,8 @@ lua_sql_execute(struct lua_State *L)
>  			lua_rawseti(L, -2, 0);
>  
>  			int row_count = 0;
> -			while ((rc = sqlite3_step(ps->stmt)) == SQLITE_ROW) {
> +			while ((rc = sqlite3_step(ps->stmt,
> +						  &opts)) == SQLITE_ROW) {
>  				lua_push_row(L, l);
>  				row_count++;
>  				lua_rawseti(L, -2, row_count);
> diff --git a/src/box/sql.c b/src/box/sql.c
> index e32ff2383..73fd13996 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -519,7 +519,8 @@ int tarantoolSqlite3EphemeralDrop(BtCursor *pCur)
>  	return SQLITE_OK;
>  }
>  
> -static int insertOrReplace(BtCursor *pCur, int operationType)
> +static int
> +insertOrReplace(BtCursor *pCur, int operationType, struct tuple **tuple)
>  {
>  	assert(pCur->curFlags & BTCF_TaCursor);
>  	assert(operationType == TARANTOOL_INDEX_INSERT ||
> @@ -527,27 +528,33 @@ static int insertOrReplace(BtCursor *pCur, int operationType)
>  
>  	int space_id = SQLITE_PAGENO_TO_SPACEID(pCur->pgnoRoot);
>  	int rc;
> +	if (tuple != NULL && *tuple != NULL) {
> +		tuple_unref(*tuple);
> +		*tuple = NULL;
> +	}

When do you ever need to dereference the previous tuple? If you
simply emit it from sqlite3_step(), you won't have to worry about
dereference.

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.org - www.twitter.com/kostja_osipov



More information about the Tarantool-patches mailing list