PostgreSQL Source Code git master
pg_regress_ecpg.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_regress_ecpg --- regression test driver for ecpg
4 *
5 * This is a C implementation of the previous shell script for running
6 * the regression tests, and should be mostly compatible with it.
7 * Initial author of C translation: Magnus Hagander
8 *
9 * This code is released under the terms of the PostgreSQL License.
10 *
11 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
13 *
14 * src/interfaces/ecpg/test/pg_regress_ecpg.c
15 *
16 *-------------------------------------------------------------------------
17 */
18
19#include "postgres_fe.h"
20
21#include "common/string.h"
22#include "lib/stringinfo.h"
23#include "pg_regress.h"
24
25
26/*
27 * Create a filtered copy of sourcefile, removing any path
28 * appearing in #line directives; for example, replace
29 * #line x "./../bla/foo.h" with #line x "foo.h".
30 * This is needed because the path part can vary depending
31 * on compiler, platform, build options, etc.
32 */
33static void
34ecpg_filter_source(const char *sourcefile, const char *outfile)
35{
36 FILE *s,
37 *t;
38 StringInfoData linebuf;
39
40 s = fopen(sourcefile, "r");
41 if (!s)
42 {
43 fprintf(stderr, "Could not open file %s for reading\n", sourcefile);
44 exit(2);
45 }
46 t = fopen(outfile, "w");
47 if (!t)
48 {
49 fprintf(stderr, "Could not open file %s for writing\n", outfile);
50 exit(2);
51 }
52
53 initStringInfo(&linebuf);
54
55 while (pg_get_line_buf(s, &linebuf))
56 {
57 /* check for "#line " in the beginning */
58 if (strstr(linebuf.data, "#line ") == linebuf.data)
59 {
60 char *p = strchr(linebuf.data, '"');
61 int plen = 1;
62
63 while (*p && (*(p + plen) == '.' || strchr(p + plen, '/') != NULL))
64 {
65 plen++;
66 }
67 /* plen is one more than the number of . and / characters */
68 if (plen > 1)
69 {
70 memmove(p + 1, p + plen, strlen(p + plen) + 1);
71 /* we don't bother to fix up linebuf.len */
72 }
73 }
74 fputs(linebuf.data, t);
75 }
76
77 pfree(linebuf.data);
78 fclose(s);
79 fclose(t);
80}
81
82/*
83 * Remove the details of connection failure error messages
84 * in a test result file, since the target host/pathname and/or port
85 * can vary. Rewrite the result file in-place.
86 *
87 * At some point it might be interesting to unify this with
88 * ecpg_filter_source, but building a general pattern matcher
89 * is no fun, nor does it seem desirable to introduce a
90 * dependency on an external one.
91 */
92static void
93ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
94{
95 FILE *s,
96 *t;
97 StringInfoData linebuf;
98
99 s = fopen(resultfile, "r");
100 if (!s)
101 {
102 fprintf(stderr, "Could not open file %s for reading\n", resultfile);
103 exit(2);
104 }
105 t = fopen(tmpfile, "w");
106 if (!t)
107 {
108 fprintf(stderr, "Could not open file %s for writing\n", tmpfile);
109 exit(2);
110 }
111
112 initStringInfo(&linebuf);
113
114 while (pg_get_line_buf(s, &linebuf))
115 {
116 char *p1 = strstr(linebuf.data, "connection to server ");
117
118 if (p1)
119 {
120 char *p2 = strstr(p1, "failed: ");
121
122 if (p2)
123 {
124 memmove(p1 + 21, p2, strlen(p2) + 1);
125 /* we don't bother to fix up linebuf.len */
126 }
127 }
128 fputs(linebuf.data, t);
129 }
130
131 pfree(linebuf.data);
132 fclose(s);
133 fclose(t);
134 if (rename(tmpfile, resultfile) != 0)
135 {
136 fprintf(stderr, "Could not overwrite file %s with %s\n",
137 resultfile, tmpfile);
138 exit(2);
139 }
140}
141
142/*
143 * start an ecpg test process for specified file (including redirection),
144 * and return process ID
145 */
146
147static PID_TYPE
148ecpg_start_test(const char *testname,
149 _stringlist **resultfiles,
150 _stringlist **expectfiles,
151 _stringlist **tags)
152{
153 PID_TYPE pid;
154 char inprg[MAXPGPATH];
155 char insource[MAXPGPATH];
156 StringInfoData testname_dash;
157 char outfile_stdout[MAXPGPATH],
158 expectfile_stdout[MAXPGPATH];
159 char outfile_stderr[MAXPGPATH],
160 expectfile_stderr[MAXPGPATH];
161 char outfile_source[MAXPGPATH],
162 expectfile_source[MAXPGPATH];
163 char cmd[MAXPGPATH * 3];
164 char *appnameenv;
165
166 snprintf(inprg, sizeof(inprg), "%s/%s", inputdir, testname);
167 snprintf(insource, sizeof(insource), "%s/%s.c", inputdir, testname);
168
169 /* make a version of the test name that has dashes in place of slashes */
170 initStringInfo(&testname_dash);
171 appendStringInfoString(&testname_dash, testname);
172 for (char *c = testname_dash.data; *c != '\0'; c++)
173 {
174 if (*c == '/')
175 *c = '-';
176 }
177
178 snprintf(expectfile_stdout, sizeof(expectfile_stdout),
179 "%s/expected/%s.stdout",
180 expecteddir, testname_dash.data);
181 snprintf(expectfile_stderr, sizeof(expectfile_stderr),
182 "%s/expected/%s.stderr",
183 expecteddir, testname_dash.data);
184 snprintf(expectfile_source, sizeof(expectfile_source),
185 "%s/expected/%s.c",
186 expecteddir, testname_dash.data);
187
188 snprintf(outfile_stdout, sizeof(outfile_stdout),
189 "%s/results/%s.stdout",
190 outputdir, testname_dash.data);
191 snprintf(outfile_stderr, sizeof(outfile_stderr),
192 "%s/results/%s.stderr",
193 outputdir, testname_dash.data);
194 snprintf(outfile_source, sizeof(outfile_source),
195 "%s/results/%s.c",
196 outputdir, testname_dash.data);
197
198 add_stringlist_item(resultfiles, outfile_stdout);
199 add_stringlist_item(expectfiles, expectfile_stdout);
200 add_stringlist_item(tags, "stdout");
201
202 add_stringlist_item(resultfiles, outfile_stderr);
203 add_stringlist_item(expectfiles, expectfile_stderr);
204 add_stringlist_item(tags, "stderr");
205
206 add_stringlist_item(resultfiles, outfile_source);
207 add_stringlist_item(expectfiles, expectfile_source);
208 add_stringlist_item(tags, "source");
209
210 ecpg_filter_source(insource, outfile_source);
211
212 snprintf(cmd, sizeof(cmd),
213 "\"%s\" >\"%s\" 2>\"%s\"",
214 inprg,
215 outfile_stdout,
216 outfile_stderr);
217
218 appnameenv = psprintf("ecpg/%s", testname_dash.data);
219 setenv("PGAPPNAME", appnameenv, 1);
220 free(appnameenv);
221
222 pid = spawn_process(cmd);
223
224 if (pid == INVALID_PID)
225 {
226 fprintf(stderr, _("could not start process for test %s\n"),
227 testname);
228 exit(2);
229 }
230
231 unsetenv("PGAPPNAME");
232
233 free(testname_dash.data);
234
235 return pid;
236}
237
238static void
240{
241 int nlen = strlen(filename);
242
243 /* Only stderr files require filtering, at the moment */
244 if (nlen > 7 && strcmp(filename + nlen - 7, ".stderr") == 0)
245 {
246 char *tmpfile = psprintf("%s.tmp", filename);
247
249 pfree(tmpfile);
250 }
251}
252
253static void
254ecpg_init(int argc, char *argv[])
255{
256 /* nothing to do here at the moment */
257}
258
259int
260main(int argc, char *argv[])
261{
262 return regression_main(argc, argv,
263 ecpg_init,
266}
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
#define _(x)
Definition: elog.c:90
#define free(a)
Definition: header.h:65
static void add_stringlist_item(_stringlist **listhead, const char *str)
Definition: initdb.c:444
exit(1)
void pfree(void *pointer)
Definition: mcxt.c:1521
#define MAXPGPATH
static char * filename
Definition: pg_dumpall.c:119
bool pg_get_line_buf(FILE *stream, StringInfo buf)
Definition: pg_get_line.c:95
static char * outfile
char * outputdir
Definition: pg_regress.c:100
char * inputdir
Definition: pg_regress.c:99
int regression_main(int argc, char *argv[], init_function ifunc, test_start_function startfunc, postprocess_result_function postfunc)
Definition: pg_regress.c:2065
char * expecteddir
Definition: pg_regress.c:101
PID_TYPE spawn_process(const char *cmdline)
Definition: pg_regress.c:1200
#define PID_TYPE
Definition: pg_regress.h:14
#define INVALID_PID
Definition: pg_regress.h:15
int main(int argc, char *argv[])
static void ecpg_init(int argc, char *argv[])
static void ecpg_postprocess_result(const char *filename)
static void ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
static void ecpg_filter_source(const char *sourcefile, const char *outfile)
static PID_TYPE ecpg_start_test(const char *testname, _stringlist **resultfiles, _stringlist **expectfiles, _stringlist **tags)
#define snprintf
Definition: port.h:238
char * c
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
#define unsetenv(x)
Definition: win32_port.h:554
#define setenv(x, y, z)
Definition: win32_port.h:553