[tarantool-patches] [PATCH v6 2/5] iproto: create port_sql
imeevma at tarantool.org
imeevma at tarantool.org
Fri Dec 28 21:11:15 MSK 2018
Hi! Thank you for review! My answers, diff between versions and
new version below.
On 12/25/18 5:57 PM, Vladislav Shpilevoy wrote:
> Hi! Thanks for the patch! See 4 comments below.
> 1. Please, write a comment about how could you use port_tuple_vtab on
> port_sql. Here I expected to see words about calling methods of a base
> structure, like BaseClass::method in C++.
Added. Not sure that it is enough.
> 2. Please, rename this method to port_sql_create and call here port_tuple_create()
> explicitly. It is just a constructor. port_tuple_add, used in sql_execute(), will
> work anyway. Since port_sql is derived from port_tuple, it can be used wherever the
> base.
Done.
> 3. I think, we should not expose port_sql to the header. It is used in execute.c
> only. Same about port_tuple_to_port_sql.
Done.
> 4. Do not forget about comments. It still describes 'response' parameter.
Fixed.
Diff between versions:
commit 414bf16c094fbb7b9e0ecb6b4b56f069f109ef86
Author: Mergen Imeev <imeevma at gmail.com>
Date: Wed Dec 26 22:00:35 2018 +0300
Temporary: review fixes
diff --git a/src/box/execute.c b/src/box/execute.c
index b07de28..c6fcb50 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -83,6 +83,36 @@ struct sql_bind {
};
/**
+ * Port implementation used for dump tuples, stored in port_tuple,
+ * to obuf or Lua.
+ *
+ * All port_tuple methods can be used with port_sql, since
+ * port_sql is a structure inherited from port_tuple. Calling
+ * port_tuple methods we are going via port_tuple_vtab.
+ */
+struct port_sql {
+ /* port_tuple to inherit from. */
+ struct port_tuple port_tuple;
+ /* Prepared SQL statement. */
+ struct sqlite3_stmt *stmt;
+};
+static_assert(sizeof(struct port_sql) <= sizeof(struct port),
+ "sizeof(struct port_sql) must be <= sizeof(struct port)");
+
+static int
+port_sql_dump_msgpack(struct port *port, struct obuf *out);
+static void
+port_sql_destroy(struct port *base);
+
+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,
+};
+
+/**
* Return a string name of a parameter marker.
* @param Bind to get name.
* @retval Zero terminated name.
@@ -356,6 +386,11 @@ sql_row_to_port(struct sqlite3_stmt *stmt, int column_count,
if (tuple == NULL)
goto error;
region_truncate(region, svp);
+ /*
+ * The port_tuple_add function can be used with port_sql,
+ * since it does not call any port_tuple methods and works
+ * only with fields.
+ */
return port_tuple_add(port, tuple);
error:
@@ -503,71 +538,24 @@ sql_get_description(struct sqlite3_stmt *stmt, struct obuf *out,
return 0;
}
-static inline int
-sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, struct port *port,
- struct region *region)
-{
- int rc, column_count = sqlite3_column_count(stmt);
- if (column_count > 0) {
- /* Either ROW or DONE or ERROR. */
- while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
- if (sql_row_to_port(stmt, column_count, region,
- port) != 0)
- return -1;
- }
- assert(rc == SQLITE_DONE || rc != SQLITE_OK);
- } else {
- /* No rows. Either DONE or ERROR. */
- rc = sqlite3_step(stmt);
- assert(rc != SQLITE_ROW && rc != SQLITE_OK);
- }
- if (rc != SQLITE_DONE) {
- diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
- 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)
+static void
+port_sql_create(struct port *port, struct sqlite3_stmt *stmt)
{
- struct sqlite3_stmt *stmt;
- sqlite3 *db = sql_get();
- if (db == NULL) {
- diag_set(ClientError, ER_LOADING);
- return -1;
- }
- if (sqlite3_prepare_v2(db, sql, len, &stmt, NULL) != SQLITE_OK) {
- diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
- return -1;
- }
- assert(stmt != NULL);
port_tuple_create(port);
- if (sql_bind(stmt, bind, bind_count) == 0 &&
- sql_execute(db, stmt, port, region) == 0) {
- port_tuple_to_port_sql(port, stmt);
- return 0;
- }
- port_destroy(port);
- sqlite3_finalize(stmt);
- return -1;
+ ((struct port_sql *)port)->stmt = stmt;
+ port->vtab = &port_sql_vtab;
}
/**
- * Dump a built response into @an out buffer. The response is
- * destroyed.
- * Response structure:
+ * Dump tuples, metadata, or information obtained from an excuted
+ * SQL query and saved to the port in obuf. The port is destroyed.
+ *
+ * Dumped msgpack 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_FIELD_NAME: column name2}, |
* | ... |
* | ], |
* | |
@@ -579,10 +567,19 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind,
* | 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 with EXECUTE response.
+ * @param port port with tuples, metadata or info.
* @param out Output buffer.
*
* @retval 0 Success.
@@ -616,6 +613,7 @@ err:
goto err;
}
pos = mp_encode_uint(pos, IPROTO_DATA);
+ /* Calling BaseStruct::methods via its vtab. */
if (port_tuple_vtab.dump_msgpack(port, out) < 0)
goto err;
} else {
@@ -670,28 +668,62 @@ err:
}
finish:
port_destroy(port);
- sqlite3_finalize(stmt);
return rc;
}
static void
port_sql_destroy(struct port *base)
{
+ /* Calling BaseStruct::methods via its vtab. */
port_tuple_vtab.destroy(base);
+ sqlite3_finalize(((struct port_sql *)base)->stmt);
}
-void
-port_tuple_to_port_sql(struct port *port, struct sqlite3_stmt *stmt)
+static inline int
+sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, struct port *port,
+ struct region *region)
{
- assert(port->vtab == &port_tuple_vtab);
- ((struct port_sql *)port)->stmt = stmt;
- port->vtab = &port_sql_vtab;
+ int rc, column_count = sqlite3_column_count(stmt);
+ if (column_count > 0) {
+ /* Either ROW or DONE or ERROR. */
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ if (sql_row_to_port(stmt, column_count, region,
+ port) != 0)
+ return -1;
+ }
+ assert(rc == SQLITE_DONE || rc != SQLITE_OK);
+ } else {
+ /* No rows. Either DONE or ERROR. */
+ rc = sqlite3_step(stmt);
+ assert(rc != SQLITE_ROW && rc != SQLITE_OK);
+ }
+ if (rc != SQLITE_DONE) {
+ diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
+ return -1;
+ }
+ return 0;
}
-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,
-};
+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 sqlite3_stmt *stmt;
+ sqlite3 *db = sql_get();
+ if (db == NULL) {
+ diag_set(ClientError, ER_LOADING);
+ return -1;
+ }
+ if (sqlite3_prepare_v2(db, sql, len, &stmt, NULL) != SQLITE_OK) {
+ diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
+ 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 5aef546..c90789f 100644
--- a/src/box/execute.h
+++ b/src/box/execute.h
@@ -48,34 +48,8 @@ enum sql_info_key {
extern const char *sql_info_key_strs[];
-struct obuf;
struct region;
struct sql_bind;
-struct sqlite3_stmt;
-
-/**
- * Port implementation used for dump tuples, stored in port_tuple,
- * to obuf or Lua.
- */
-struct port_sql {
- /* port_tuple to inherit from. */
- struct port_tuple port_tuple;
- /* Prepared SQL statement. */
- struct sqlite3_stmt *stmt;
-};
-static_assert(sizeof(struct port_sql) <= sizeof(struct port),
- "sizeof(struct port_sql) must be <= sizeof(struct port)");
-
-extern const struct port_vtab port_sql_vtab;
-
-/**
- * Transform port_tuple with already stored tuples to port_sql
- * that will dump these tuples into obut or Lua.
- *
- * @param port port_tuple to transform into port_sql.
- */
-void
-port_tuple_to_port_sql(struct port *port, struct sqlite3_stmt *stmt);
/**
* Parse MessagePack array of SQL parameters.
New version:
commit 8390b09d95aa5bcaa6ef89924d9a334dff746f19
Author: Mergen Imeev <imeevma at gmail.com>
Date: Fri Dec 21 14:52:03 2018 +0300
iproto: create port_sql
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
diff --git a/src/box/execute.c b/src/box/execute.c
index 38b6cbc..c6fcb50 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -83,6 +83,36 @@ struct sql_bind {
};
/**
+ * Port implementation used for dump tuples, stored in port_tuple,
+ * to obuf or Lua.
+ *
+ * All port_tuple methods can be used with port_sql, since
+ * port_sql is a structure inherited from port_tuple. Calling
+ * port_tuple methods we are going via port_tuple_vtab.
+ */
+struct port_sql {
+ /* port_tuple to inherit from. */
+ struct port_tuple port_tuple;
+ /* Prepared SQL statement. */
+ struct sqlite3_stmt *stmt;
+};
+static_assert(sizeof(struct port_sql) <= sizeof(struct port),
+ "sizeof(struct port_sql) must be <= sizeof(struct port)");
+
+static int
+port_sql_dump_msgpack(struct port *port, struct obuf *out);
+static void
+port_sql_destroy(struct port *base);
+
+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,
+};
+
+/**
* Return a string name of a parameter marker.
* @param Bind to get name.
* @retval Zero terminated name.
@@ -356,6 +386,11 @@ sql_row_to_port(struct sqlite3_stmt *stmt, int column_count,
if (tuple == NULL)
goto error;
region_truncate(region, svp);
+ /*
+ * The port_tuple_add function can be used with port_sql,
+ * since it does not call any port_tuple methods and works
+ * only with fields.
+ */
return port_tuple_add(port, tuple);
error:
@@ -503,63 +538,59 @@ sql_get_description(struct sqlite3_stmt *stmt, struct obuf *out,
return 0;
}
-static inline int
-sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, struct port *port,
- struct region *region)
+static void
+port_sql_create(struct port *port, struct sqlite3_stmt *stmt)
{
- int rc, column_count = sqlite3_column_count(stmt);
- if (column_count > 0) {
- /* Either ROW or DONE or ERROR. */
- while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
- if (sql_row_to_port(stmt, column_count, region,
- port) != 0)
- return -1;
- }
- assert(rc == SQLITE_DONE || rc != SQLITE_OK);
- } else {
- /* No rows. Either DONE or ERROR. */
- rc = sqlite3_step(stmt);
- assert(rc != SQLITE_ROW && rc != SQLITE_OK);
- }
- if (rc != SQLITE_DONE) {
- diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
- return -1;
- }
- return 0;
+ port_tuple_create(port);
+ ((struct port_sql *)port)->stmt = stmt;
+ port->vtab = &port_sql_vtab;
}
-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 sqlite3_stmt *stmt;
- sqlite3 *db = sql_get();
- if (db == NULL) {
- diag_set(ClientError, ER_LOADING);
- return -1;
- }
- if (sqlite3_prepare_v2(db, sql, len, &stmt, NULL) != SQLITE_OK) {
- diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
- 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);
- sqlite3_finalize(stmt);
- return -1;
-}
-
-int
-sql_response_dump(struct sql_response *response, struct obuf *out)
+/**
+ * Dump tuples, metadata, or information obtained from an excuted
+ * SQL query and saved to the port in obuf. The port is destroyed.
+ *
+ * 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 with tuples, metadata or info.
+ * @param out Output buffer.
+ *
+ * @retval 0 Success.
+ * @retval -1 Memory error.
+ */
+static int
+port_sql_dump_msgpack(struct port *port, struct obuf *out)
{
+ assert(port->vtab == &port_sql_vtab);
sqlite3 *db = sql_get();
- struct sqlite3_stmt *stmt = (struct sqlite3_stmt *) response->prep_stmt;
- struct port_tuple *port_tuple = (struct port_tuple *) &response->port;
+ struct sqlite3_stmt *stmt = ((struct port_sql *)port)->stmt;
int rc = 0, column_count = sqlite3_column_count(stmt);
if (column_count > 0) {
int keys = 2;
@@ -575,26 +606,19 @@ err:
rc = -1;
goto finish;
}
- size = mp_sizeof_uint(IPROTO_DATA) +
- mp_sizeof_array(port_tuple->size);
+ size = mp_sizeof_uint(IPROTO_DATA);
pos = (char *) obuf_alloc(out, size);
if (pos == NULL) {
diag_set(OutOfMemory, size, "obuf_alloc", "pos");
goto err;
}
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. */
+ /* Calling BaseStruct::methods via its vtab. */
+ if (port_tuple_vtab.dump_msgpack(port, out) < 0)
goto err;
- }
} 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;
@@ -643,7 +667,63 @@ err:
}
}
finish:
- port_destroy(&response->port);
- sqlite3_finalize(stmt);
+ port_destroy(port);
return rc;
}
+
+static void
+port_sql_destroy(struct port *base)
+{
+ /* Calling BaseStruct::methods via its vtab. */
+ port_tuple_vtab.destroy(base);
+ sqlite3_finalize(((struct port_sql *)base)->stmt);
+}
+
+static inline int
+sql_execute(sqlite3 *db, struct sqlite3_stmt *stmt, struct port *port,
+ struct region *region)
+{
+ int rc, column_count = sqlite3_column_count(stmt);
+ if (column_count > 0) {
+ /* Either ROW or DONE or ERROR. */
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ if (sql_row_to_port(stmt, column_count, region,
+ port) != 0)
+ return -1;
+ }
+ assert(rc == SQLITE_DONE || rc != SQLITE_OK);
+ } else {
+ /* No rows. Either DONE or ERROR. */
+ rc = sqlite3_step(stmt);
+ assert(rc != SQLITE_ROW && rc != SQLITE_OK);
+ }
+ if (rc != SQLITE_DONE) {
+ diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
+ 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 sqlite3_stmt *stmt;
+ sqlite3 *db = sql_get();
+ if (db == NULL) {
+ diag_set(ClientError, ER_LOADING);
+ return -1;
+ }
+ if (sqlite3_prepare_v2(db, sql, len, &stmt, NULL) != SQLITE_OK) {
+ diag_set(ClientError, ER_SQL_EXECUTE, sqlite3_errmsg(db));
+ 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 60b8f31..c90789f 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
@@ -78,42 +69,6 @@ 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.
@@ -128,7 +83,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 a08c8c5..9dc0462 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -1616,7 +1616,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;
@@ -1633,7 +1633,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;
/*
@@ -1645,7 +1645,7 @@ tx_process_sql(struct cmsg *m)
/* Prepare memory for the iproto header. */
if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0)
goto error;
- if (sql_response_dump(&response, out) != 0) {
+ if (port_dump_msgpack(&port, out) != 0) {
obuf_rollback_to_svp(out, &header_svp);
goto error;
}
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
More information about the Tarantool-patches
mailing list