Tarantool development patches archive
 help / color / mirror / Atom feed
From: Mergen Imeev via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: v.shpilevoy@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [PATCH v1 2/2] sql: introduce mem_snprintf()
Date: Mon, 15 Nov 2021 19:06:47 +0300	[thread overview]
Message-ID: <c3335c0f66ad4ec5a20898ed3857f1a692437ace.1636992280.git.imeevma@gmail.com> (raw)
In-Reply-To: <cover.1636992280.git.imeevma@gmail.com>

This patch introduces the mem_snprintf() function, which writes the
string representation of a MEM to buf.
---
 src/box/sql/func.c               |  9 +++-
 src/box/sql/mem.c                | 73 +++++++++++++++++---------------
 src/box/sql/mem.h                |  8 ++++
 src/box/sql/printf.c             | 15 ++-----
 test/sql-tap/sql-errors.test.lua |  5 ++-
 test/sql/types.result            |  2 +-
 6 files changed, 63 insertions(+), 49 deletions(-)

diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 5abaf490d..0f8f0b5cc 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -869,9 +869,14 @@ func_printf(struct sql_context *ctx, int argc, const struct Mem *argv)
 	if (argc < 1 || mem_is_null(&argv[0]))
 		return;
 	if (argc == 1 || !mem_is_str(&argv[0])) {
-		struct Mem *mem = ctx->pOut;
-		if (mem_copy(mem, &argv[0]) != 0 || mem_to_str(mem) != 0)
+		uint32_t size = mem_snprintf(NULL, 0, &argv[0]);
+		char *str = sqlDbMallocRawNN(sql_get(), size + 1);
+		if (str == NULL) {
 			ctx->is_aborted = true;
+			return;
+		}
+		mem_snprintf(str, size + 1, &argv[0]);
+		mem_set_str_allocated(ctx->pOut, str, size);
 		return;
 	}
 	struct PrintfArguments pargs;
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 9ddeea5bb..6e1729f32 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -116,30 +116,51 @@ mem_is_field_compatible(const struct Mem *mem, enum field_type type)
 	return field_mp_plain_type_is_compatible(type, mp_type, true);
 }
 
-const char *
-mem_str(const struct Mem *mem)
+int
+mem_snprintf(char *buf, uint32_t size, const struct Mem *mem)
 {
-	char buf[STR_VALUE_MAX_LEN];
-	const char *type = mem_type_to_str(mem);
 	switch (mem->type) {
 	case MEM_TYPE_NULL:
-		return "NULL";
+		return snprintf(buf, size, "NULL");
 	case MEM_TYPE_STR:
+	case MEM_TYPE_BIN:
+		return snprintf(buf, size, "%.*s", mem->n, mem->z);
+	case MEM_TYPE_INT:
+		return snprintf(buf, size, "%lld", mem->u.i);
+	case MEM_TYPE_UINT:
+		return snprintf(buf, size, "%llu",
+				(unsigned long long)mem->u.u);
+	case MEM_TYPE_DOUBLE: {
+		char str[BUF_SIZE];
+		sql_snprintf(BUF_SIZE, str, "%!.15g", mem->u.r);
+		return snprintf(buf, size, "%s", str);
+	}
+	case MEM_TYPE_DEC:
+		return snprintf(buf, size, "%s", decimal_str(&mem->u.d));
+	case MEM_TYPE_MAP:
+	case MEM_TYPE_ARRAY:
+		return mp_snprint(buf, size, mem->z);
+	case MEM_TYPE_UUID:
+		return snprintf(buf, size, "%s", tt_uuid_str(&mem->u.uuid));
+	case MEM_TYPE_BOOL:
+		return snprintf(buf, size, "%s", mem->u.b ? "TRUE" : "FALSE");
+	default:
+		return snprintf(buf, size, "unknown");
+	}
+}
+
+const char *
+mem_str(const struct Mem *mem)
+{
+	const char *type = mem_type_to_str(mem);
+	if (mem->type == MEM_TYPE_STR) {
 		if (mem->n <= STR_VALUE_MAX_LEN)
 			return tt_sprintf("%s('%.*s')", type, mem->n, mem->z);
 		return tt_sprintf("%s('%.*s...)", type, STR_VALUE_MAX_LEN,
 				  mem->z);
-	case MEM_TYPE_INT:
-		return tt_sprintf("%s(%lld)", type, mem->u.i);
-	case MEM_TYPE_UINT:
-		return tt_sprintf("%s(%llu)", type, mem->u.u);
-	case MEM_TYPE_DOUBLE:
-		sql_snprintf(STR_VALUE_MAX_LEN, buf, "%!.15g", mem->u.r);
-		return tt_sprintf("%s(%s)", type, buf);
-	case MEM_TYPE_DEC:
-		decimal_to_string(&mem->u.d, buf);
-		return tt_sprintf("%s(%s)", type, buf);
-	case MEM_TYPE_BIN: {
+	}
+	char buf[STR_VALUE_MAX_LEN];
+	if (mem->type == MEM_TYPE_BIN) {
 		int len = MIN(mem->n, STR_VALUE_MAX_LEN / 2);
 		for (int i = 0; i < len; ++i) {
 			int n = (mem->z[i] & 0xF0) >> 4;
@@ -151,24 +172,10 @@ mem_str(const struct Mem *mem)
 			return tt_sprintf("%s(x'%.*s...)", type, len * 2, buf);
 		return tt_sprintf("%s(x'%.*s')", type, len * 2, buf);
 	}
-	case MEM_TYPE_MAP:
-	case MEM_TYPE_ARRAY: {
-		const char *str = mp_str(mem->z);
-		uint32_t len = strlen(str);
-		uint32_t minlen = MIN(STR_VALUE_MAX_LEN, len);
-		memcpy(buf, str, minlen);
-		if (len <= STR_VALUE_MAX_LEN)
-			return tt_sprintf("%s(%.*s)", type, minlen, buf);
-		return tt_sprintf("%s(%.*s...)", type, minlen, buf);
-	}
-	case MEM_TYPE_UUID:
-		tt_uuid_to_string(&mem->u.uuid, buf);
+	int size = mem_snprintf(buf, STR_VALUE_MAX_LEN, mem);
+	if (size <= STR_VALUE_MAX_LEN)
 		return tt_sprintf("%s(%s)", type, buf);
-	case MEM_TYPE_BOOL:
-		return tt_sprintf("%s(%s)", type, mem->u.b ? "TRUE" : "FALSE");
-	default:
-		return "unknown";
-	}
+	return tt_sprintf("%s(%.*s...)", type, STR_VALUE_MAX_LEN, buf);
 }
 
 static const char *
diff --git a/src/box/sql/mem.h b/src/box/sql/mem.h
index e3313ff98..62830c3f5 100644
--- a/src/box/sql/mem.h
+++ b/src/box/sql/mem.h
@@ -246,6 +246,14 @@ mem_is_any_null(const struct Mem *mem1, const struct Mem *mem2)
 bool
 mem_is_field_compatible(const struct Mem *mem, enum field_type type);
 
+/**
+ * Write a NULL-terminated string representation of a MEM to buf. Returns the
+ * number of bytes required to write the value, excluding '\0'. If the return
+ * value is equal to or greater than size, then the value has been truncated.
+ */
+int
+mem_snprintf(char *buf, uint32_t size, const struct Mem *mem);
+
 /**
  * Return a string that contains description of type and value of MEM. String is
  * either allocated using static_alloc() of just a static variable. This
diff --git a/src/box/sql/printf.c b/src/box/sql/printf.c
index 8da7c9878..08e18c15d 100644
--- a/src/box/sql/printf.c
+++ b/src/box/sql/printf.c
@@ -160,19 +160,12 @@ getTextArg(PrintfArguments * p)
 {
 	if (p->nArg <= p->nUsed)
 		return 0;
-	struct Mem mem;
-	mem_create(&mem);
-	mem_copy_as_ephemeral(&mem, &p->apArg[p->nUsed++]);
-	if (mem_to_str(&mem) != 0) {
-		mem_destroy(&mem);
-		return NULL;
-	}
-	char *str = sqlDbMallocRawNN(sql_get(), mem.n + 1);
+	const struct Mem *mem = &p->apArg[p->nUsed++];
+	uint32_t size = mem_snprintf(NULL, 0, mem);
+	char *str = sqlDbMallocRawNN(sql_get(), size + 1);
 	if (str == NULL)
 		return NULL;
-	memcpy(str, mem.z, mem.n);
-	str[mem.n] = '\0';
-	mem_destroy(&mem);
+	mem_snprintf(str, size + 1, mem);
 	return str;
 }
 
diff --git a/test/sql-tap/sql-errors.test.lua b/test/sql-tap/sql-errors.test.lua
index a9aa5acf7..6cecd7c6d 100755
--- a/test/sql-tap/sql-errors.test.lua
+++ b/test/sql-tap/sql-errors.test.lua
@@ -812,7 +812,8 @@ test:do_catchsql_test(
 	"SELECT CAST(a AS UNSIGNED) from test;", {
 		1, 'Type mismatch: can not convert array(["aaaaaaaaaaaaaaaaaa'..
 		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'..
-		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...) to unsigned'
+		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...) to '..
+		'unsigned'
 	})
 
 test:do_catchsql_test(
@@ -820,7 +821,7 @@ test:do_catchsql_test(
 	"SELECT CAST(m AS UNSIGNED) from test;", {
 		1, 'Type mismatch: can not convert map({"a": 1, "b": "aaaaaaa'..
 		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'..
-		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...) to unsigned'
+		'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...) to unsigned'
 	})
 
 test:execsql('DROP TABLE test;')
diff --git a/test/sql/types.result b/test/sql/types.result
index 947795b03..5a822fddf 100644
--- a/test/sql/types.result
+++ b/test/sql/types.result
@@ -1623,7 +1623,7 @@ box.execute('INSERT INTO t1(a) SELECT a FROM t2;')
 - null
 - 'Type mismatch: can not convert array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
   14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
-  34, ...) to scalar'
+  34,...) to scalar'
 ...
 s:drop()
 ---
-- 
2.25.1


  parent reply	other threads:[~2021-11-15 16:07 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-15 16:06 [Tarantool-patches] [PATCH v1 0/2] Introduce mem_snprintf() Mergen Imeev via Tarantool-patches
2021-11-15 16:06 ` [Tarantool-patches] [PATCH v1 1/2] sql: omit quotes for UUID values in errors Mergen Imeev via Tarantool-patches
2021-11-15 16:06 ` Mergen Imeev via Tarantool-patches [this message]
2021-11-17 23:03   ` [Tarantool-patches] [PATCH v1 2/2] sql: introduce mem_snprintf() Vladislav Shpilevoy via Tarantool-patches
2021-11-19 12:02     ` Mergen Imeev via Tarantool-patches
2021-11-21 15:27 ` [Tarantool-patches] [PATCH v1 0/2] Introduce mem_snprintf() Vladislav Shpilevoy via Tarantool-patches
2021-11-22  8:22 Mergen Imeev via Tarantool-patches
2021-11-22  8:22 ` [Tarantool-patches] [PATCH v1 2/2] sql: introduce mem_snprintf() Mergen Imeev via Tarantool-patches

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=c3335c0f66ad4ec5a20898ed3857f1a692437ace.1636992280.git.imeevma@gmail.com \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=imeevma@tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v1 2/2] sql: introduce mem_snprintf()' \
    /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