From bae717f8b2710a6e29f0acf5b52de5f5501b8ccc Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Tue, 12 Aug 2025 01:08:24 +0900 Subject: [PATCH 1/2] pgbench: Allow variables to be used as an SQL literal or identifier Now, the form :'var' and :"var" can be used in addition to :var, but only in the SQL command. They do not work in arguments of meta-commands for now. --- src/bin/pgbench/pgbench.c | 116 +++++++++++++++++++++++++++++++++----- 1 file changed, 103 insertions(+), 13 deletions(-) diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 125f3c7bbbe..14d1261ff8d 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -1913,10 +1913,32 @@ putVariableInt(Variables *variables, const char *context, char *name, * Otherwise, return NULL. */ static char * -parseVariable(const char *sql, int *eaten) +parseVariable(const char *sql, int *eaten, PsqlScanQuoteType *quote) { int i = 1; /* starting at 1 skips the colon */ char *name; + int start; + int len; + + + if (sql[i] == '\'') + { + i++; + *quote = PQUOTE_SQL_LITERAL; + start = 2; + } + else if (sql[i] == '"') + { + i++; + *quote = PQUOTE_SQL_IDENT; + start = 2; + } + else + { + *quote = PQUOTE_PLAIN; + start = 1; + } + /* keep this logic in sync with valid_variable_name() */ if (IS_HIGHBIT_SET(sql[i]) || @@ -1931,18 +1953,82 @@ parseVariable(const char *sql, int *eaten) "_0123456789", sql[i]) != NULL) i++; - name = pg_malloc(i); - memcpy(name, &sql[1], i - 1); - name[i - 1] = '\0'; + if (*quote == PQUOTE_SQL_LITERAL) + { + if (sql[i] != '\'') + return NULL; + i++; + len = i - 3; + } + else if (*quote == PQUOTE_SQL_IDENT) + { + if (sql[i] != '"') + return NULL; + i++; + len = i - 3; + } + else + { + Assert(*quote == PQUOTE_PLAIN); + len = i - 1; + } + + name = pg_malloc(len + 1); + memcpy(name, &sql[start], len); + name[len] = '\0'; *eaten = i; return name; } static char * -replaceVariable(char **sql, char *param, int len, char *value) +replaceVariable(char **sql, char *param, int len, char *value, PsqlScanQuoteType quote, PGconn *conn) { - int valueln = strlen(value); + int valueln; + char quotechar; + char *quoted_value; + + switch (quote) + { + case PQUOTE_PLAIN: + quoted_value = pg_strdup(value); + break; + case PQUOTE_SQL_LITERAL: + case PQUOTE_SQL_IDENT: + { + /* + * For these cases, we use libpq's quoting functions, which + * assume the string is in the connection's client encoding. + */ + char *escaped_value; + + Assert(conn); + + if (quote == PQUOTE_SQL_LITERAL) + escaped_value = + PQescapeLiteral(conn, value, strlen(value)); + else + escaped_value = + PQescapeIdentifier(conn, value, strlen(value)); + + if (escaped_value == NULL) + { + pg_log_error("escape failed: %s", PQerrorMessage(conn)); + exit(1); + } + + /* + * Rather than complicate the lexer's API with a notion of + * which free() routine to use, just pay the price of an extra + * strdup(). + */ + quoted_value = pg_strdup(escaped_value); + PQfreemem(escaped_value); + break; + } + } + + valueln = strlen(quoted_value); if (valueln > len) { @@ -1954,13 +2040,15 @@ replaceVariable(char **sql, char *param, int len, char *value) if (valueln != len) memmove(param + valueln, param + len, strlen(param + len) + 1); - memcpy(param, value, valueln); + memcpy(param, quoted_value, valueln); + + pfree(quoted_value); return param + valueln; } static char * -assignVariables(Variables *variables, char *sql) +assignVariables(Variables *variables, char *sql, PGconn *conn) { char *p, *name, @@ -1970,8 +2058,9 @@ assignVariables(Variables *variables, char *sql) while ((p = strchr(p, ':')) != NULL) { int eaten; + PsqlScanQuoteType quote; - name = parseVariable(p, &eaten); + name = parseVariable(p, &eaten, "e); if (name == NULL) { while (*p == ':') @@ -1989,7 +2078,7 @@ assignVariables(Variables *variables, char *sql) continue; } - p = replaceVariable(&sql, p, eaten, val); + p = replaceVariable(&sql, p, eaten, val, quote, conn); } return sql; @@ -3183,7 +3272,7 @@ sendCommand(CState *st, Command *command) char *sql; sql = pg_strdup(command->argv[0]); - sql = assignVariables(&st->variables, sql); + sql = assignVariables(&st->variables, sql, st->con); pg_log_debug("client %d sending %s", st->id, sql); r = PQsendQuery(st->con, sql); @@ -5480,8 +5569,9 @@ parseQuery(Command *cmd) char var[13]; char *name; int eaten; + PsqlScanQuoteType quote; - name = parseVariable(p, &eaten); + name = parseVariable(p, &eaten, "e); if (name == NULL) { while (*p == ':') @@ -5504,7 +5594,7 @@ parseQuery(Command *cmd) } sprintf(var, "$%d", cmd->argc); - p = replaceVariable(&sql, p, eaten, var); + p = replaceVariable(&sql, p, eaten, var, PQUOTE_PLAIN, NULL); cmd->argv[cmd->argc] = name; cmd->argc++; -- 2.43.0