From: imeevma@tarantool.org To: v.shpilevoy@tarantool.org Cc: tarantool-patches@freelists.org Subject: [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql Date: Fri, 22 Mar 2019 13:50:40 +0300 [thread overview] Message-ID: <baa09f8703706c5919ba3f7176bb6eda1d8c97fb.1553251042.git.imeevma@gmail.com> (raw) In-Reply-To: <cover.1553251041.git.imeevma@gmail.com> This patch creates port_sql implementation for the port. This will allow us to dump sql responses to obuf or to Lua. Also this patch defines methods dump_msgpack() and destroy() of port_sql. Part of #3505 --- src/box/execute.c | 263 +++++++++++++++++++++++++++++++++++------------------- src/box/execute.h | 49 +--------- src/box/iproto.cc | 12 ++- src/box/port.h | 1 - 4 files changed, 182 insertions(+), 143 deletions(-) diff --git a/src/box/execute.c b/src/box/execute.c index 5810086..460eebc 100644 --- a/src/box/execute.c +++ b/src/box/execute.c @@ -83,6 +83,92 @@ struct sql_bind { }; /** + * Port implementation that is used to store SQL responses and + * output them to obuf or Lua. This port implementation is + * inherited from the port_tuple structure. This allows us to use + * this structure in the port_tuple methods instead of port_tuple + * itself. + * + * The methods of port_tuple are called via explicit access to + * port_tuple_vtab just like C++ does with BaseClass::method, when + * it is called in a child method. + */ +struct port_sql { + /* port_tuple to inherit from. */ + struct port_tuple port_tuple; + /* Prepared SQL statement. */ + struct sql_stmt *stmt; +}; + +static_assert(sizeof(struct port_sql) <= sizeof(struct port), + "sizeof(struct port_sql) must be <= sizeof(struct port)"); + +/** + * Dump data from port to buffer. Data in port contains tuples, + * metadata, or information obtained from an executed SQL query. + * + * Dumped msgpack structure: + * +----------------------------------------------+ + * | IPROTO_BODY: { | + * | IPROTO_METADATA: [ | + * | {IPROTO_FIELD_NAME: column name1}, | + * | {IPROTO_FIELD_NAME: column name2}, | + * | ... | + * | ], | + * | | + * | IPROTO_DATA: [ | + * | tuple, tuple, tuple, ... | + * | ] | + * | } | + * +-------------------- OR ----------------------+ + * | IPROTO_BODY: { | + * | IPROTO_SQL_INFO: { | + * | SQL_INFO_ROW_COUNT: number | + * | SQL_INFO_AUTOINCREMENT_IDS: [ | + * | id, id, id, ... | + * | ] | + * | } | + * | } | + * +-------------------- OR ----------------------+ + * | IPROTO_BODY: { | + * | IPROTO_SQL_INFO: { | + * | SQL_INFO_ROW_COUNT: number | + * | } | + * | } | + * +----------------------------------------------+ + * @param port Port that contains SQL response. + * @param[out] out Output buffer. + * + * @retval 0 Success. + * @retval -1 Memory error. + */ +static int +port_sql_dump_msgpack(struct port *port, struct obuf *out); + +static void +port_sql_destroy(struct port *base) +{ + port_tuple_vtab.destroy(base); + sql_finalize(((struct port_sql *)base)->stmt); +} + +static const struct port_vtab port_sql_vtab = { + /* .dump_msgpack = */ port_sql_dump_msgpack, + /* .dump_msgpack_16 = */ NULL, + /* .dump_lua = */ NULL, + /* .dump_plain = */ NULL, + /* .destroy = */ port_sql_destroy, +}; + +static void +port_sql_create(struct port *port, struct sql_stmt *stmt) +{ + port_tuple_create(port); + ((struct port_sql *)port)->stmt = stmt; + port->vtab = &port_sql_vtab; +} + +/** * Return a string name of a parameter marker. * @param Bind to get name. * @retval Zero terminated name. @@ -504,108 +590,36 @@ sql_get_description(struct sql_stmt *stmt, struct obuf *out, return 0; } -static inline int -sql_execute(sql *db, struct sql_stmt *stmt, struct port *port, - struct region *region) -{ - int rc, column_count = sql_column_count(stmt); - if (column_count > 0) { - /* Either ROW or DONE or ERROR. */ - while ((rc = sql_step(stmt)) == SQL_ROW) { - if (sql_row_to_port(stmt, column_count, region, - port) != 0) - return -1; - } - assert(rc == SQL_DONE || rc != SQL_OK); - } else { - /* No rows. Either DONE or ERROR. */ - rc = sql_step(stmt); - assert(rc != SQL_ROW && rc != SQL_OK); - } - if (rc != SQL_DONE) { - if (db->errCode != SQL_TARANTOOL_ERROR) { - const char *err = (char *)sql_value_text(db->pErr); - if (err == NULL) - err = sqlErrStr(db->errCode); - diag_set(ClientError, ER_VDBE_EXECUTE, err); - } - return -1; - } - return 0; -} - -int -sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind, - uint32_t bind_count, struct sql_response *response, - struct region *region) -{ - struct sql_stmt *stmt; - struct sql *db = sql_get(); - if (db == NULL) { - diag_set(ClientError, ER_LOADING); - return -1; - } - if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) { - if (db->errCode != SQL_TARANTOOL_ERROR) { - const char *err = (char *)sql_value_text(db->pErr); - if (err == NULL) - err = sqlErrStr(db->errCode); - diag_set(ClientError, ER_VDBE_EXECUTE, err); - } - return -1; - } - assert(stmt != NULL); - port_tuple_create(&response->port); - response->prep_stmt = stmt; - if (sql_bind(stmt, bind, bind_count) == 0 && - sql_execute(db, stmt, &response->port, region) == 0) - return 0; - port_destroy(&response->port); - sql_finalize(stmt); - return -1; -} - -int -sql_response_dump(struct sql_response *response, struct obuf *out) +static int +port_sql_dump_msgpack(struct port *port, struct obuf *out) { + assert(port->vtab == &port_sql_vtab); sql *db = sql_get(); - struct sql_stmt *stmt = (struct sql_stmt *) response->prep_stmt; - struct port_tuple *port_tuple = (struct port_tuple *) &response->port; - int rc = 0, column_count = sql_column_count(stmt); + struct sql_stmt *stmt = ((struct port_sql *)port)->stmt; + int column_count = sql_column_count(stmt); if (column_count > 0) { int keys = 2; int size = mp_sizeof_map(keys); char *pos = (char *) obuf_alloc(out, size); if (pos == NULL) { diag_set(OutOfMemory, size, "obuf_alloc", "pos"); - goto err; + return -1; } pos = mp_encode_map(pos, keys); - if (sql_get_description(stmt, out, column_count) != 0) { -err: - rc = -1; - goto finish; - } - size = mp_sizeof_uint(IPROTO_DATA) + - mp_sizeof_array(port_tuple->size); + if (sql_get_description(stmt, out, column_count) != 0) + return -1; + size = mp_sizeof_uint(IPROTO_DATA); pos = (char *) obuf_alloc(out, size); if (pos == NULL) { diag_set(OutOfMemory, size, "obuf_alloc", "pos"); - goto err; + return -1; } pos = mp_encode_uint(pos, IPROTO_DATA); - pos = mp_encode_array(pos, port_tuple->size); - /* - * Just like SELECT, SQL uses output format compatible - * with Tarantool 1.6 - */ - if (port_dump_msgpack_16(&response->port, out) < 0) { - /* Failed port dump destroyes the port. */ - goto err; - } + if (port_tuple_vtab.dump_msgpack(port, out) < 0) + return -1; } else { int keys = 1; - assert(port_tuple->size == 0); + assert(((struct port_tuple *)port)->size == 0); struct stailq *autoinc_id_list = vdbe_autoinc_id_list((struct Vdbe *)stmt); uint32_t map_size = stailq_empty(autoinc_id_list) ? 1 : 2; @@ -615,7 +629,7 @@ err: char *pos = (char *) obuf_alloc(out, size); if (pos == NULL) { diag_set(OutOfMemory, size, "obuf_alloc", "pos"); - goto err; + return -1; } pos = mp_encode_map(pos, keys); pos = mp_encode_uint(pos, IPROTO_SQL_INFO); @@ -638,7 +652,7 @@ err: char *buf = obuf_alloc(out, size); if (buf == NULL) { diag_set(OutOfMemory, size, "obuf_alloc", "buf"); - goto err; + return -1; } buf = mp_encode_uint(buf, SQL_INFO_ROW_COUNT); buf = mp_encode_uint(buf, changes); @@ -653,8 +667,75 @@ err: } } } -finish: - port_destroy(&response->port); - sql_finalize(stmt); - return rc; + return 0; +} + +/** + * Execute prepared SQL statement. + * + * This function uses region to allocate memory for temporary + * objects. After this function, region will be in the same state + * in which it was before this function. + * + * @param db SQL handle. + * @param stmt Prepared statement. + * @param port Port to store SQL response. + * @param region Region to allocate temporary objects. + * + * @retval 0 Success. + * @retval -1 Error. + */ +static inline int +sql_execute(sql *db, struct sql_stmt *stmt, struct port *port, + struct region *region) +{ + int rc, column_count = sql_column_count(stmt); + if (column_count > 0) { + /* Either ROW or DONE or ERROR. */ + while ((rc = sql_step(stmt)) == SQL_ROW) { + if (sql_row_to_port(stmt, column_count, region, + port) != 0) + return -1; + } + assert(rc == SQL_DONE || rc != SQL_OK); + } else { + /* No rows. Either DONE or ERROR. */ + rc = sql_step(stmt); + assert(rc != SQL_ROW && rc != SQL_OK); + } + if (rc != SQL_DONE) { + if (db->errCode != SQL_TARANTOOL_ERROR) { + const char *err = (char *)sql_value_text(db->pErr); + if (err == NULL) + err = sqlErrStr(db->errCode); + diag_set(ClientError, ER_VDBE_EXECUTE, err); + } + return -1; + } + return 0; +} + +int +sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind, + uint32_t bind_count, struct port *port, + struct region *region) +{ + struct sql_stmt *stmt; + struct sql *db = sql_get(); + if (sql_prepare_v2(db, sql, len, &stmt, NULL) != SQL_OK) { + if (db->errCode != SQL_TARANTOOL_ERROR) { + const char *err = (char *)sql_value_text(db->pErr); + if (err == NULL) + err = sqlErrStr(db->errCode); + diag_set(ClientError, ER_VDBE_EXECUTE, err); + } + return -1; + } + assert(stmt != NULL); + port_sql_create(port, stmt); + if (sql_bind(stmt, bind, bind_count) == 0 && + sql_execute(db, stmt, port, region) == 0) + return 0; + port_destroy(port); + return -1; } diff --git a/src/box/execute.h b/src/box/execute.h index 12d893a..52563cd 100644 --- a/src/box/execute.h +++ b/src/box/execute.h @@ -48,18 +48,9 @@ enum sql_info_key { extern const char *sql_info_key_strs[]; -struct obuf; struct region; struct sql_bind; -/** Response on EXECUTE request. */ -struct sql_response { - /** Port with response data if any. */ - struct port port; - /** Prepared SQL statement with metadata. */ - void *prep_stmt; -}; - /** * Parse MessagePack array of SQL parameters. * @param data MessagePack array of parameters. Each parameter @@ -76,48 +67,12 @@ int sql_bind_list_decode(const char *data, struct sql_bind **out_bind); /** - * Dump a built response into @an out buffer. The response is - * destroyed. - * Response structure: - * +----------------------------------------------+ - * | IPROTO_OK, sync, schema_version ... | iproto_header - * +----------------------------------------------+--------------- - * | Body - a map with one or two keys. | - * | | - * | IPROTO_BODY: { | - * | IPROTO_METADATA: [ | - * | {IPROTO_FIELD_NAME: column name1}, | - * | {IPROTO_FIELD_NAME: column name2}, | iproto_body - * | ... | - * | ], | - * | | - * | IPROTO_DATA: [ | - * | tuple, tuple, tuple, ... | - * | ] | - * | } | - * +-------------------- OR ----------------------+ - * | IPROTO_BODY: { | - * | IPROTO_SQL_INFO: { | - * | SQL_INFO_ROW_COUNT: number | - * | } | - * | } | - * +----------------------------------------------+ - * @param response EXECUTE response. - * @param out Output buffer. - * - * @retval 0 Success. - * @retval -1 Memory error. - */ -int -sql_response_dump(struct sql_response *response, struct obuf *out); - -/** * Prepare and execute an SQL statement. * @param sql SQL statement. * @param len Length of @a sql. * @param bind Array of parameters. * @param bind_count Length of @a bind. - * @param[out] response Response to store result. + * @param[out] port Port to store SQL response. * @param region Runtime allocator for temporary objects * (columns, tuples ...). * @@ -126,7 +81,7 @@ sql_response_dump(struct sql_response *response, struct obuf *out); */ int sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind, - uint32_t bind_count, struct sql_response *response, + uint32_t bind_count, struct port *port, struct region *region); #if defined(__cplusplus) diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 3b0ba62..1e88bf0 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -1621,7 +1621,7 @@ tx_process_sql(struct cmsg *m) { struct iproto_msg *msg = tx_accept_msg(m); struct obuf *out; - struct sql_response response; + struct port port; struct sql_bind *bind; int bind_count; const char *sql; @@ -1638,7 +1638,7 @@ tx_process_sql(struct cmsg *m) goto error; sql = msg->sql.sql_text; sql = mp_decode_str(&sql, &len); - if (sql_prepare_and_execute(sql, len, bind, bind_count, &response, + if (sql_prepare_and_execute(sql, len, bind, bind_count, &port, &fiber()->gc) != 0) goto error; /* @@ -1648,12 +1648,16 @@ tx_process_sql(struct cmsg *m) out = msg->connection->tx.p_obuf; struct obuf_svp header_svp; /* Prepare memory for the iproto header. */ - if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0) + if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0) { + port_destroy(&port); goto error; - if (sql_response_dump(&response, out) != 0) { + } + if (port_dump_msgpack(&port, out) != 0) { + port_destroy(&port); obuf_rollback_to_svp(out, &header_svp); goto error; } + port_destroy(&port); iproto_reply_sql(out, &header_svp, msg->header.sync, schema_version); iproto_wpos_create(&msg->wpos, out); return; diff --git a/src/box/port.h b/src/box/port.h index ad1b349..f188036 100644 --- a/src/box/port.h +++ b/src/box/port.h @@ -65,7 +65,6 @@ extern const struct port_vtab port_tuple_vtab; static inline struct port_tuple * port_tuple(struct port *port) { - assert(port->vtab == &port_tuple_vtab); return (struct port_tuple *)port; } -- 2.7.4
next prev parent reply other threads:[~2019-03-22 10:50 UTC|newest] Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-03-22 10:50 [tarantool-patches] [PATCH v9 0/7] sql: remove box.sql.execute imeevma 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 1/7] sql: add column name to SQL change counter imeevma 2019-03-22 15:42 ` [tarantool-patches] " Konstantin Osipov 2019-03-25 19:34 ` Mergen Imeev 2019-03-29 12:00 ` Kirill Yukhin 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 2/7] sql: fix error code for SQL errors in execute.c imeevma 2019-03-22 15:45 ` [tarantool-patches] " Konstantin Osipov 2019-03-26 21:48 ` Vladislav Shpilevoy 2019-03-27 11:43 ` Konstantin Osipov 2019-03-28 17:46 ` Mergen Imeev 2019-03-29 12:01 ` Kirill Yukhin 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 3/7] sql: remove box.sql.debug() imeevma 2019-03-22 15:46 ` [tarantool-patches] " Konstantin Osipov 2019-03-25 19:39 ` Mergen Imeev 2019-03-26 21:48 ` Vladislav Shpilevoy 2019-03-28 17:48 ` Mergen Imeev 2019-03-28 18:01 ` Vladislav Shpilevoy 2019-03-29 12:02 ` Kirill Yukhin 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() imeevma 2019-03-22 15:53 ` [tarantool-patches] " Konstantin Osipov 2019-03-29 19:26 ` Vladislav Shpilevoy 2019-03-26 21:48 ` Vladislav Shpilevoy 2019-03-28 17:54 ` Mergen Imeev 2019-03-28 18:40 ` Vladislav Shpilevoy 2019-03-28 19:56 ` Mergen Imeev 2019-03-28 21:41 ` Mergen Imeev 2019-03-29 21:06 ` Vladislav Shpilevoy 2019-03-22 10:50 ` imeevma [this message] 2019-03-22 15:55 ` [tarantool-patches] Re: [PATCH v9 5/7] iproto: create port_sql Konstantin Osipov 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 6/7] sql: create box.execute() imeevma 2019-03-22 15:57 ` [tarantool-patches] " Konstantin Osipov 2019-03-22 10:50 ` [tarantool-patches] [PATCH v9 7/7] sql: remove box.sql.execute() imeevma 2019-03-26 21:48 ` [tarantool-patches] " Vladislav Shpilevoy 2019-03-28 20:13 ` Mergen Imeev 2019-03-29 21:06 ` Vladislav Shpilevoy 2019-03-29 21:07 ` [tarantool-patches] Re: [PATCH v9 0/7] sql: remove box.sql.execute Vladislav Shpilevoy
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=baa09f8703706c5919ba3f7176bb6eda1d8c97fb.1553251042.git.imeevma@gmail.com \ --to=imeevma@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=v.shpilevoy@tarantool.org \ --subject='Re: [tarantool-patches] [PATCH v9 5/7] iproto: create port_sql' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox