Skip to content

Commit f45dc59

Browse files
committed
Improve pg_regress.c's infrastructure for issuing psql commands.
Support issuing more than one "-c command" switch to a single psql invocation. This allows combining some things that formerly required two or more backend launches into a single session. In particular, we can issue DROP DATABASE as one of the -c commands without getting "DROP DATABASE cannot run inside a transaction block". In addition to reducing the number of sessions needed, this patch also suppresses "NOTICE: database "foo" does not exist, skipping" chatter that was formerly generated during pg_regress's DROP DATABASE (or ROLE) IF NOT EXISTS calls. That moves us another step closer to the ideal of not seeing any messages during successful build/test. This also eliminates some hard-coded restrictions on the length of the commands issued. I don't think we were anywhere near hitting those, but getting rid of the limit is comforting. Patch by me, but thanks to Nathan Bossart for starting the discussion. Discussion: https://postgr.es/m/DCBAE0E4-BD56-482F-8A70-7FD0DC0860BE@amazon.com
1 parent cd124d2 commit f45dc59

File tree

1 file changed

+104
-44
lines changed

1 file changed

+104
-44
lines changed

src/test/regress/pg_regress.c

Lines changed: 104 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ static void make_directory(const char *dir);
122122

123123
static void header(const char *fmt,...) pg_attribute_printf(1, 2);
124124
static void status(const char *fmt,...) pg_attribute_printf(1, 2);
125-
static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
125+
static StringInfo psql_start_command(void);
126+
static void psql_add_command(StringInfo buf, const char *query,...) pg_attribute_printf(2, 3);
127+
static void psql_end_command(StringInfo buf, const char *database);
126128

127129
/*
128130
* allow core files if possible.
@@ -1136,51 +1138,94 @@ config_sspi_auth(const char *pgdata, const char *superuser_name)
11361138
#endif /* ENABLE_SSPI */
11371139

11381140
/*
1139-
* Issue a command via psql, connecting to the specified database
1141+
* psql_start_command, psql_add_command, psql_end_command
1142+
*
1143+
* Issue one or more commands within one psql call.
1144+
* Set up with psql_start_command, then add commands one at a time
1145+
* with psql_add_command, and finally execute with psql_end_command.
11401146
*
11411147
* Since we use system(), this doesn't return until the operation finishes
11421148
*/
1149+
static StringInfo
1150+
psql_start_command(void)
1151+
{
1152+
StringInfo buf = makeStringInfo();
1153+
1154+
appendStringInfo(buf,
1155+
"\"%s%spsql\" -X",
1156+
bindir ? bindir : "",
1157+
bindir ? "/" : "");
1158+
return buf;
1159+
}
1160+
11431161
static void
1144-
psql_command(const char *database, const char *query,...)
1162+
psql_add_command(StringInfo buf, const char *query,...)
11451163
{
1146-
char query_formatted[1024];
1147-
char query_escaped[2048];
1148-
char psql_cmd[MAXPGPATH + 2048];
1149-
va_list args;
1150-
char *s;
1151-
char *d;
1164+
StringInfoData cmdbuf;
1165+
const char *cmdptr;
1166+
1167+
/* Add each command as a -c argument in the psql call */
1168+
appendStringInfoString(buf, " -c \"");
11521169

11531170
/* Generate the query with insertion of sprintf arguments */
1154-
va_start(args, query);
1155-
vsnprintf(query_formatted, sizeof(query_formatted), query, args);
1156-
va_end(args);
1171+
initStringInfo(&cmdbuf);
1172+
for (;;)
1173+
{
1174+
va_list args;
1175+
int needed;
1176+
1177+
va_start(args, query);
1178+
needed = appendStringInfoVA(&cmdbuf, query, args);
1179+
va_end(args);
1180+
if (needed == 0)
1181+
break; /* success */
1182+
enlargeStringInfo(&cmdbuf, needed);
1183+
}
11571184

11581185
/* Now escape any shell double-quote metacharacters */
1159-
d = query_escaped;
1160-
for (s = query_formatted; *s; s++)
1186+
for (cmdptr = cmdbuf.data; *cmdptr; cmdptr++)
11611187
{
1162-
if (strchr("\\\"$`", *s))
1163-
*d++ = '\\';
1164-
*d++ = *s;
1188+
if (strchr("\\\"$`", *cmdptr))
1189+
appendStringInfoChar(buf, '\\');
1190+
appendStringInfoChar(buf, *cmdptr);
11651191
}
1166-
*d = '\0';
11671192

1168-
/* And now we can build and execute the shell command */
1169-
snprintf(psql_cmd, sizeof(psql_cmd),
1170-
"\"%s%spsql\" -X -c \"%s\" \"%s\"",
1171-
bindir ? bindir : "",
1172-
bindir ? "/" : "",
1173-
query_escaped,
1174-
database);
1193+
appendStringInfoChar(buf, '"');
1194+
1195+
pfree(cmdbuf.data);
1196+
}
1197+
1198+
static void
1199+
psql_end_command(StringInfo buf, const char *database)
1200+
{
1201+
/* Add the database name --- assume it needs no extra escaping */
1202+
appendStringInfo(buf,
1203+
" \"%s\"",
1204+
database);
11751205

1176-
if (system(psql_cmd) != 0)
1206+
/* And now we can execute the shell command */
1207+
if (system(buf->data) != 0)
11771208
{
11781209
/* psql probably already reported the error */
1179-
fprintf(stderr, _("command failed: %s\n"), psql_cmd);
1210+
fprintf(stderr, _("command failed: %s\n"), buf->data);
11801211
exit(2);
11811212
}
1213+
1214+
/* Clean up */
1215+
pfree(buf->data);
1216+
pfree(buf);
11821217
}
11831218

1219+
/*
1220+
* Shorthand macro for the common case of a single command
1221+
*/
1222+
#define psql_command(database, ...) \
1223+
do { \
1224+
StringInfo cmdbuf = psql_start_command(); \
1225+
psql_add_command(cmdbuf, __VA_ARGS__); \
1226+
psql_end_command(cmdbuf, database); \
1227+
} while (0)
1228+
11841229
/*
11851230
* Spawn a process to execute the given shell command; don't wait for it
11861231
*
@@ -2012,13 +2057,19 @@ open_result_files(void)
20122057
static void
20132058
drop_database_if_exists(const char *dbname)
20142059
{
2060+
StringInfo buf = psql_start_command();
2061+
20152062
header(_("dropping database \"%s\""), dbname);
2016-
psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
2063+
/* Set warning level so we don't see chatter about nonexistent DB */
2064+
psql_add_command(buf, "SET client_min_messages = warning");
2065+
psql_add_command(buf, "DROP DATABASE IF EXISTS \"%s\"", dbname);
2066+
psql_end_command(buf, "postgres");
20172067
}
20182068

20192069
static void
20202070
create_database(const char *dbname)
20212071
{
2072+
StringInfo buf = psql_start_command();
20222073
_stringlist *sl;
20232074

20242075
/*
@@ -2027,19 +2078,20 @@ create_database(const char *dbname)
20272078
*/
20282079
header(_("creating database \"%s\""), dbname);
20292080
if (encoding)
2030-
psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
2031-
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
2081+
psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
2082+
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
20322083
else
2033-
psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
2034-
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
2035-
psql_command(dbname,
2036-
"ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
2037-
"ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
2038-
"ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
2039-
"ALTER DATABASE \"%s\" SET lc_time TO 'C';"
2040-
"ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
2041-
"ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
2042-
dbname, dbname, dbname, dbname, dbname, dbname);
2084+
psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
2085+
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
2086+
psql_add_command(buf,
2087+
"ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
2088+
"ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
2089+
"ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
2090+
"ALTER DATABASE \"%s\" SET lc_time TO 'C';"
2091+
"ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
2092+
"ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
2093+
dbname, dbname, dbname, dbname, dbname, dbname);
2094+
psql_end_command(buf, "postgres");
20432095

20442096
/*
20452097
* Install any requested extensions. We use CREATE IF NOT EXISTS so that
@@ -2055,20 +2107,28 @@ create_database(const char *dbname)
20552107
static void
20562108
drop_role_if_exists(const char *rolename)
20572109
{
2110+
StringInfo buf = psql_start_command();
2111+
20582112
header(_("dropping role \"%s\""), rolename);
2059-
psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
2113+
/* Set warning level so we don't see chatter about nonexistent role */
2114+
psql_add_command(buf, "SET client_min_messages = warning");
2115+
psql_add_command(buf, "DROP ROLE IF EXISTS \"%s\"", rolename);
2116+
psql_end_command(buf, "postgres");
20602117
}
20612118

20622119
static void
20632120
create_role(const char *rolename, const _stringlist *granted_dbs)
20642121
{
2122+
StringInfo buf = psql_start_command();
2123+
20652124
header(_("creating role \"%s\""), rolename);
2066-
psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
2125+
psql_add_command(buf, "CREATE ROLE \"%s\" WITH LOGIN", rolename);
20672126
for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
20682127
{
2069-
psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
2070-
granted_dbs->str, rolename);
2128+
psql_add_command(buf, "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
2129+
granted_dbs->str, rolename);
20712130
}
2131+
psql_end_command(buf, "postgres");
20722132
}
20732133

20742134
static void

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy