@@ -64,6 +64,11 @@ exit_nicely(PGconn *conn)
64
64
exit (1 );
65
65
}
66
66
67
+ /*
68
+ * The following few functions are wrapped in macros to make the reported line
69
+ * number in an error match the line number of the invocation.
70
+ */
71
+
67
72
/*
68
73
* Print an error to stderr and terminate the program.
69
74
*/
@@ -74,7 +79,6 @@ pg_fatal_impl(int line, const char *fmt,...)
74
79
{
75
80
va_list args ;
76
81
77
-
78
82
fflush (stdout );
79
83
80
84
fprintf (stderr , "\n %s :%d : ", progname, line);
@@ -86,6 +90,170 @@ pg_fatal_impl(int line, const char *fmt,...)
86
90
exit (1 );
87
91
}
88
92
93
+ /*
94
+ * Check that the query on the given connection got canceled.
95
+ */
96
+ #define confirm_query_canceled (conn ) confirm_query_canceled_impl(__LINE__, conn)
97
+ static void
98
+ confirm_query_canceled_impl (int line , PGconn * conn )
99
+ {
100
+ PGresult * res = NULL ;
101
+
102
+ res = PQgetResult (conn );
103
+ if (res == NULL )
104
+ pg_fatal_impl (line , "PQgetResult returned null: %s" ,
105
+ PQerrorMessage (conn ));
106
+ if (PQresultStatus (res ) != PGRES_FATAL_ERROR )
107
+ pg_fatal_impl (line , "query did not fail when it was expected" );
108
+ if (strcmp (PQresultErrorField (res , PG_DIAG_SQLSTATE ), "57014" ) != 0 )
109
+ pg_fatal_impl (line , "query failed with a different error than cancellation: %s" ,
110
+ PQerrorMessage (conn ));
111
+ PQclear (res );
112
+
113
+ while (PQisBusy (conn ))
114
+ PQconsumeInput (conn );
115
+ }
116
+
117
+ #define send_cancellable_query (conn , monitorConn ) \
118
+ send_cancellable_query_impl(__LINE__, conn, monitorConn)
119
+ static void
120
+ send_cancellable_query_impl (int line , PGconn * conn , PGconn * monitorConn )
121
+ {
122
+ const char * env_wait ;
123
+ const Oid paramTypes [1 ] = {INT4OID };
124
+ int procpid = PQbackendPID (conn );
125
+
126
+ env_wait = getenv ("PG_TEST_TIMEOUT_DEFAULT" );
127
+ if (env_wait == NULL )
128
+ env_wait = "180" ;
129
+
130
+ if (PQsendQueryParams (conn , "SELECT pg_sleep($1)" , 1 , paramTypes ,
131
+ & env_wait , NULL , NULL , 0 ) != 1 )
132
+ pg_fatal_impl (line , "failed to send query: %s" , PQerrorMessage (conn ));
133
+
134
+ /*
135
+ * Wait until the query is actually running. Otherwise sending a
136
+ * cancellation request might not cancel the query due to race conditions.
137
+ */
138
+ while (true)
139
+ {
140
+ char * value ;
141
+ PGresult * res ;
142
+ const char * paramValues [1 ];
143
+ char pidval [16 ];
144
+
145
+ snprintf (pidval , 16 , "%d" , procpid );
146
+ paramValues [0 ] = pidval ;
147
+
148
+ res = PQexecParams (monitorConn ,
149
+ "SELECT count(*) FROM pg_stat_activity WHERE "
150
+ "pid = $1 AND state = 'active'" ,
151
+ 1 , NULL , paramValues , NULL , NULL , 1 );
152
+
153
+ if (PQresultStatus (res ) != PGRES_TUPLES_OK )
154
+ pg_fatal ("could not query pg_stat_activity: %s" , PQerrorMessage (monitorConn ));
155
+ if (PQntuples (res ) != 1 )
156
+ pg_fatal ("unexpected number of rows received: %d" , PQntuples (res ));
157
+ if (PQnfields (res ) != 1 )
158
+ pg_fatal ("unexpected number of columns received: %d" , PQnfields (res ));
159
+ value = PQgetvalue (res , 0 , 0 );
160
+ if (* value != '0' )
161
+ {
162
+ PQclear (res );
163
+ break ;
164
+ }
165
+ PQclear (res );
166
+
167
+ /* wait 10ms before polling again */
168
+ pg_usleep (10000 );
169
+ }
170
+ }
171
+
172
+ /*
173
+ * Create a new connection with the same conninfo as the given one.
174
+ */
175
+ static PGconn *
176
+ copy_connection (PGconn * conn )
177
+ {
178
+ PGconn * copyConn ;
179
+ PQconninfoOption * opts = PQconninfo (conn );
180
+ const char * * keywords ;
181
+ const char * * vals ;
182
+ int nopts = 1 ;
183
+ int i = 0 ;
184
+
185
+ for (PQconninfoOption * opt = opts ; opt -> keyword != NULL ; ++ opt )
186
+ nopts ++ ;
187
+
188
+ keywords = pg_malloc (sizeof (char * ) * nopts );
189
+ vals = pg_malloc (sizeof (char * ) * nopts );
190
+
191
+ for (PQconninfoOption * opt = opts ; opt -> keyword != NULL ; ++ opt )
192
+ {
193
+ if (opt -> val )
194
+ {
195
+ keywords [i ] = opt -> keyword ;
196
+ vals [i ] = opt -> val ;
197
+ i ++ ;
198
+ }
199
+ }
200
+ keywords [i ] = vals [i ] = NULL ;
201
+
202
+ copyConn = PQconnectdbParams (keywords , vals , false);
203
+
204
+ if (PQstatus (copyConn ) != CONNECTION_OK )
205
+ pg_fatal ("Connection to database failed: %s" ,
206
+ PQerrorMessage (copyConn ));
207
+
208
+ return copyConn ;
209
+ }
210
+
211
+ /*
212
+ * Test query cancellation routines
213
+ */
214
+ static void
215
+ test_cancel (PGconn * conn )
216
+ {
217
+ PGcancel * cancel ;
218
+ PGconn * monitorConn ;
219
+ char errorbuf [256 ];
220
+
221
+ fprintf (stderr , "test cancellations... " );
222
+
223
+ if (PQsetnonblocking (conn , 1 ) != 0 )
224
+ pg_fatal ("failed to set nonblocking mode: %s" , PQerrorMessage (conn ));
225
+
226
+ /*
227
+ * Make a separate connection to the database to monitor the query on the
228
+ * main connection.
229
+ */
230
+ monitorConn = copy_connection (conn );
231
+ Assert (PQstatus (monitorConn ) == CONNECTION_OK );
232
+
233
+ /* test PQcancel */
234
+ send_cancellable_query (conn , monitorConn );
235
+ cancel = PQgetCancel (conn );
236
+ if (!PQcancel (cancel , errorbuf , sizeof (errorbuf )))
237
+ pg_fatal ("failed to run PQcancel: %s" , errorbuf );
238
+ confirm_query_canceled (conn );
239
+
240
+ /* PGcancel object can be reused for the next query */
241
+ send_cancellable_query (conn , monitorConn );
242
+ if (!PQcancel (cancel , errorbuf , sizeof (errorbuf )))
243
+ pg_fatal ("failed to run PQcancel: %s" , errorbuf );
244
+ confirm_query_canceled (conn );
245
+
246
+ PQfreeCancel (cancel );
247
+
248
+ /* test PQrequestCancel */
249
+ send_cancellable_query (conn , monitorConn );
250
+ if (!PQrequestCancel (conn ))
251
+ pg_fatal ("failed to run PQrequestCancel: %s" , PQerrorMessage (conn ));
252
+ confirm_query_canceled (conn );
253
+
254
+ fprintf (stderr , "ok\n" );
255
+ }
256
+
89
257
static void
90
258
test_disallowed_in_pipeline (PGconn * conn )
91
259
{
@@ -1789,6 +1957,7 @@ usage(const char *progname)
1789
1957
static void
1790
1958
print_test_list (void )
1791
1959
{
1960
+ printf ("cancel\n" );
1792
1961
printf ("disallowed_in_pipeline\n" );
1793
1962
printf ("multi_pipelines\n" );
1794
1963
printf ("nosync\n" );
@@ -1890,7 +2059,9 @@ main(int argc, char **argv)
1890
2059
PQTRACE_SUPPRESS_TIMESTAMPS | PQTRACE_REGRESS_MODE );
1891
2060
}
1892
2061
1893
- if (strcmp (testname , "disallowed_in_pipeline" ) == 0 )
2062
+ if (strcmp (testname , "cancel" ) == 0 )
2063
+ test_cancel (conn );
2064
+ else if (strcmp (testname , "disallowed_in_pipeline" ) == 0 )
1894
2065
test_disallowed_in_pipeline (conn );
1895
2066
else if (strcmp (testname , "multi_pipelines" ) == 0 )
1896
2067
test_multi_pipelines (conn );
0 commit comments