diff --git a/src/box/execute.c b/src/box/execute.c index 06298703aabea0d08fb2a007f5744cc408dd8483..44d465602d5c221c1106d20bec840672ed641231 100644 --- a/src/box/execute.c +++ b/src/box/execute.c @@ -257,3 +257,56 @@ sql_prepare_and_execute(const char *sql, int len, const struct sql_bind *bind, port_destroy(port); return -1; } + +int +box_process_sql(const struct sql_request *request, struct port *port) +{ + struct region *region = &fiber()->gc; + struct sql_bind *bind = NULL; + int bind_count = 0; + if (request->bind != NULL) { + bind_count = sql_bind_list_decode(request->bind, &bind); + if (bind_count < 0) + return -1; + } + /* + * There are four options: + * 1. Prepare SQL query (IPROTO_PREPARE + SQL string); + * 2. Unprepare SQL query (IPROTO_PREPARE + stmt id); + * 3. Execute SQL query (IPROTO_EXECUTE + SQL string); + * 4. Execute prepared query (IPROTO_EXECUTE + stmt id). + */ + if (request->execute) { + if (request->sql_text != NULL) { + assert(request->stmt_id == NULL); + const char *sql = request->sql_text; + uint32_t len; + sql = mp_decode_str(&sql, &len); + return sql_prepare_and_execute(sql, len, + bind, bind_count, + port, region); + } else { + assert(request->stmt_id != NULL); + const char *data = request->stmt_id; + uint32_t stmt_id = mp_decode_uint(&data); + return sql_execute_prepared(stmt_id, bind, bind_count, + port, region); + } + } else { + if (request->sql_text != NULL) { + assert(request->stmt_id == NULL); + const char *sql = request->sql_text; + uint32_t len; + sql = mp_decode_str(&sql, &len); + return sql_prepare(sql, len, port); + } else { + assert(request->stmt_id != NULL); + const char *data = request->stmt_id; + uint32_t stmt_id = mp_decode_uint(&data); + if (sql_unprepare(stmt_id) != 0) + return -1; + port_sql_create(port, NULL, UNPREPARE, false); + return 0; + } + } +} diff --git a/src/box/execute.h b/src/box/execute.h index 77ca9756067800ec9cc9fa8fabc62c4f12048999..c5f64a26ef71f6fe06a60ba417976546a70516c6 100644 --- a/src/box/execute.h +++ b/src/box/execute.h @@ -51,6 +51,7 @@ extern const char *sql_info_key_strs[]; struct Vdbe; struct region; struct sql_bind; +struct sql_request; int sql_unprepare(uint32_t stmt_id); @@ -113,6 +114,22 @@ sql_stmt_busy(const struct Vdbe *stmt); int sql_prepare(const char *sql, int len, struct port *port); +/** + * Process an SQL request received over IPROTO. + * + * @param request SQL request. + * @param port Port to store request response. + * + * @retval 0 Success. + * @retval -1 Error, see diag. + * + * NOTE: The port may refer to data allocated from the fiber region. + * The caller is responsible for truncating the region after using + * the port. + */ +int +box_process_sql(const struct sql_request *request, struct port *port); + #if defined(__cplusplus) } /* extern "C" { */ #endif diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 6af38911a20b32c6f666f20a7910a3d7c2393add..f825df1a9fc2d3f9bf62ad639f9ae777c7031f1c 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -2432,11 +2432,6 @@ tx_process_sql(struct cmsg *m) struct iproto_msg *msg = tx_accept_msg(m); struct obuf *out; struct port port; - struct sql_bind *bind = NULL; - int bind_count = 0; - const char *sql; - uint32_t len; - bool is_unprepare = false; RegionGuard region_guard(&fiber()->gc); if (tx_check_msg(msg) != 0) @@ -2444,68 +2439,14 @@ tx_process_sql(struct cmsg *m) assert(msg->header.type == IPROTO_EXECUTE || msg->header.type == IPROTO_PREPARE); tx_inject_delay(); - if (msg->sql.bind != NULL) { - bind_count = sql_bind_list_decode(msg->sql.bind, &bind); - if (bind_count < 0) - goto error; - } - /* - * There are four options: - * 1. Prepare SQL query (IPROTO_PREPARE + SQL string); - * 2. Unprepare SQL query (IPROTO_PREPARE + stmt id); - * 3. Execute SQL query (IPROTO_EXECUTE + SQL string); - * 4. Execute prepared query (IPROTO_EXECUTE + stmt id). - */ - if (msg->header.type == IPROTO_EXECUTE) { - if (msg->sql.sql_text != NULL) { - assert(msg->sql.stmt_id == NULL); - sql = msg->sql.sql_text; - sql = mp_decode_str(&sql, &len); - if (sql_prepare_and_execute(sql, len, bind, bind_count, - &port, &fiber()->gc) != 0) - goto error; - } else { - assert(msg->sql.sql_text == NULL); - assert(msg->sql.stmt_id != NULL); - sql = msg->sql.stmt_id; - uint32_t stmt_id = mp_decode_uint(&sql); - if (sql_execute_prepared(stmt_id, bind, bind_count, - &port, &fiber()->gc) != 0) - goto error; - } - } else { - /* IPROTO_PREPARE */ - if (msg->sql.sql_text != NULL) { - assert(msg->sql.stmt_id == NULL); - sql = msg->sql.sql_text; - sql = mp_decode_str(&sql, &len); - if (sql_prepare(sql, len, &port) != 0) - goto error; - } else { - /* UNPREPARE */ - assert(msg->sql.sql_text == NULL); - assert(msg->sql.stmt_id != NULL); - sql = msg->sql.stmt_id; - uint32_t stmt_id = mp_decode_uint(&sql); - if (sql_unprepare(stmt_id) != 0) - goto error; - is_unprepare = true; - } - } - + if (box_process_sql(&msg->sql, &port) != 0) + goto error; /* * Take an obuf only after execute(). Else the buffer can * become out of date during yield. */ out = msg->connection->tx.p_obuf; struct obuf_svp header_svp; - if (is_unprepare) { - header_svp = obuf_create_svp(out); - iproto_reply_ok(out, msg->header.sync, schema_version); - iproto_wpos_create(&msg->wpos, out); - tx_end_msg(msg, &header_svp); - return; - } iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN); if (port_dump_msgpack(&port, out) != 0) { port_destroy(&port); diff --git a/src/box/sql/port.c b/src/box/sql/port.c index c274482165c2fa771ce6a67977319867ca2851fa..3e2113e664f45ce675a112132eb80de5a4f7f30d 100644 --- a/src/box/sql/port.c +++ b/src/box/sql/port.c @@ -345,7 +345,17 @@ port_sql_dump_msgpack(struct port *port, struct obuf *out) */ int keys = 3; return sql_get_prepare_common_keys(stmt, out, keys); + } + case UNPREPARE: { + int size = mp_sizeof_map(0); + char *pos = obuf_alloc(out, size); + if (pos == NULL) { + diag_set(OutOfMemory, size, "obuf_alloc", "pos"); + return -1; } + mp_encode_map(pos, 0); + break; + } default: { unreachable(); } diff --git a/src/box/sql/port.h b/src/box/sql/port.h index e5f09a5ff0e855252d2ea416a0caf34fca9e82ad..ff1fdfafe2d4f9f8b7ad23671729fe2c4fa69c7c 100644 --- a/src/box/sql/port.h +++ b/src/box/sql/port.h @@ -21,6 +21,7 @@ enum sql_serialization_format { DML_EXECUTE = 1, DQL_PREPARE = 2, DML_PREPARE = 3, + UNPREPARE = 4, }; /** Methods of struct port_sql. */ diff --git a/src/box/xrow.c b/src/box/xrow.c index de0403cc8067dd51d89789b629ae1e4e44b43bd3..4194568c0cf1c964d11e5fe1cb10b4f650de4a92 100644 --- a/src/box/xrow.c +++ b/src/box/xrow.c @@ -753,6 +753,7 @@ iproto_reply_select_with_position(struct obuf *buf, struct obuf_svp *svp, int xrow_decode_sql(const struct xrow_header *row, struct sql_request *request) { + assert(row->type == IPROTO_EXECUTE || row->type == IPROTO_PREPARE); if (row->bodycnt == 0) { diag_set(ClientError, ER_INVALID_MSGPACK, "missing request body"); return 1; @@ -765,6 +766,7 @@ xrow_decode_sql(const struct xrow_header *row, struct sql_request *request) } uint32_t map_size = mp_decode_map(&data); + request->execute = row->type == IPROTO_EXECUTE; request->sql_text = NULL; request->bind = NULL; request->stmt_id = NULL; diff --git a/src/box/xrow.h b/src/box/xrow.h index 9764dd57949da1326c20d2d5cc3b85dd3700460d..edd2d96e1ca5d8cbaf3c087d7e330db92080a219 100644 --- a/src/box/xrow.h +++ b/src/box/xrow.h @@ -831,6 +831,8 @@ iproto_reply_error(struct obuf *out, const struct error *e, uint64_t sync, /** EXECUTE/PREPARE request. */ struct sql_request { + /** True for EXECUTE, false for PREPARE. */ + bool execute; /** SQL statement text. */ const char *sql_text; /** MessagePack array of parameters. */ diff --git a/test/fuzz/xrow_decode_sql_fuzzer.c b/test/fuzz/xrow_decode_sql_fuzzer.c index 4cdc1e370433d78e7290191617481edf7cf4b998..621b0aa12bf7bf25ac172f79aeb6f1ce7109a842 100644 --- a/test/fuzz/xrow_decode_sql_fuzzer.c +++ b/test/fuzz/xrow_decode_sql_fuzzer.c @@ -35,6 +35,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) body.iov_len = size; struct xrow_header row = {0}; + row.type = IPROTO_EXECUTE; row.body[0] = body; row.bodycnt = 1;