From 1f7e7aa2bf47445dffc713df336288676b927445 Mon Sep 17 00:00:00 2001
From: Chris Sosnin <k.sosnin@tarantool.org>
Date: Wed, 11 Mar 2020 11:43:22 +0300
Subject: [PATCH] iproto: add an empty body to the unprepare response

Absence of the body in the unprepare response forces users to perform
additional checks to avoid errors. Adding an empty body fixes this problem.

Closes #4769

Reviewed-by: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Reviewed-by: Nikita Pettik <korablev@tarantool.org>
---
 src/box/iproto.cc                             | 17 +++--
 .../gh-4769-unprepare-response-body.result    | 64 +++++++++++++++++++
 .../gh-4769-unprepare-response-body.test.lua  | 25 ++++++++
 3 files changed, 99 insertions(+), 7 deletions(-)
 create mode 100644 test/box/gh-4769-unprepare-response-body.result
 create mode 100644 test/box/gh-4769-unprepare-response-body.test.lua

diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index f313af6aec..f31738cb03 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -1780,21 +1780,24 @@ tx_process_sql(struct cmsg *m)
 	 * become out of date during yield.
 	 */
 	out = msg->connection->tx.p_obuf;
+	if (is_unprepare) {
+		if (iproto_reply_ok(out, msg->header.sync, schema_version) != 0)
+			goto error;
+		iproto_wpos_create(&msg->wpos, out);
+		return;
+	}
 	struct obuf_svp header_svp;
 	/* Prepare memory for the iproto header. */
 	if (iproto_prepare_header(out, &header_svp, IPROTO_HEADER_LEN) != 0) {
 		port_destroy(&port);
 		goto error;
 	}
-	/* Nothing to dump in case of UNPREPARE request. */
-	if (!is_unprepare) {
-		if (port_dump_msgpack(&port, out) != 0) {
-			port_destroy(&port);
-			obuf_rollback_to_svp(out, &header_svp);
-			goto error;
-		}
+	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/test/box/gh-4769-unprepare-response-body.result b/test/box/gh-4769-unprepare-response-body.result
new file mode 100644
index 0000000000..96e234b9b1
--- /dev/null
+++ b/test/box/gh-4769-unprepare-response-body.result
@@ -0,0 +1,64 @@
+-- test-run result file version 2
+net_box = require('net.box')
+ | ---
+ | ...
+msgpack = require('msgpack')
+ | ---
+ | ...
+urilib = require('uri')
+ | ---
+ | ...
+
+IPROTO_REQUEST_TYPE = 0x00
+ | ---
+ | ...
+IPROTO_PREPARE      = 13
+ | ---
+ | ...
+IPROTO_SQL_TEXT     = 0x40
+ | ---
+ | ...
+IPROTO_STMT_ID      = 0x43
+ | ---
+ | ...
+
+box.schema.user.grant('guest', 'read, write, execute', 'universe')
+ | ---
+ | ...
+uri = urilib.parse(box.cfg.listen)
+ | ---
+ | ...
+socket = net_box.establish_connection(uri.host, uri.service)
+ | ---
+ | ...
+
+header = { [IPROTO_REQUEST_TYPE] = IPROTO_PREPARE }
+ | ---
+ | ...
+body = { [IPROTO_SQL_TEXT] = 'SELECT 1' }
+ | ---
+ | ...
+response = iproto_request(socket, header, body)
+ | ---
+ | ...
+
+body = { [IPROTO_STMT_ID] = response['body'][IPROTO_STMT_ID] }
+ | ---
+ | ...
+-- Decoding of the response will fail if there's no body.
+response = iproto_request(socket, header, body)
+ | ---
+ | ...
+response.body
+ | ---
+ | - {}
+ | ...
+
+box.schema.user.revoke('guest', 'read, write, execute', 'universe')
+ | ---
+ | ...
+socket:close()
+ | ---
+ | - true
+ | ...
+
diff --git a/test/box/gh-4769-unprepare-response-body.test.lua b/test/box/gh-4769-unprepare-response-body.test.lua
new file mode 100644
index 0000000000..9b53022019
--- /dev/null
+++ b/test/box/gh-4769-unprepare-response-body.test.lua
@@ -0,0 +1,25 @@
+net_box = require('net.box')
+msgpack = require('msgpack')
+urilib = require('uri')
+
+IPROTO_REQUEST_TYPE = 0x00
+IPROTO_PREPARE      = 13
+IPROTO_SQL_TEXT     = 0x40
+IPROTO_STMT_ID      = 0x43
+
+box.schema.user.grant('guest', 'read, write, execute', 'universe')
+uri = urilib.parse(box.cfg.listen)
+socket = net_box.establish_connection(uri.host, uri.service)
+
+header = { [IPROTO_REQUEST_TYPE] = IPROTO_PREPARE }
+body = { [IPROTO_SQL_TEXT] = 'SELECT 1' }
+response = iproto_request(socket, header, body)
+
+body = { [IPROTO_STMT_ID] = response['body'][IPROTO_STMT_ID] }
+-- Decoding of the response will fail if there's no body.
+response = iproto_request(socket, header, body)
+response.body
+
+box.schema.user.revoke('guest', 'read, write, execute', 'universe')
+socket:close()
+
-- 
GitLab