Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * fe-exec.c
4 : : * functions related to sending a query down to the backend
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/interfaces/libpq/fe-exec.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres_fe.h"
16 : :
17 : : #include <ctype.h>
18 : : #include <fcntl.h>
19 : : #include <limits.h>
20 : :
21 : : #ifdef WIN32
22 : : #include "win32.h"
23 : : #else
24 : : #include <unistd.h>
25 : : #endif
26 : :
27 : : #include "libpq-fe.h"
28 : : #include "libpq-int.h"
29 : : #include "mb/pg_wchar.h"
30 : :
31 : : /* keep this in same order as ExecStatusType in libpq-fe.h */
32 : : char *const pgresStatus[] = {
33 : : "PGRES_EMPTY_QUERY",
34 : : "PGRES_COMMAND_OK",
35 : : "PGRES_TUPLES_OK",
36 : : "PGRES_COPY_OUT",
37 : : "PGRES_COPY_IN",
38 : : "PGRES_BAD_RESPONSE",
39 : : "PGRES_NONFATAL_ERROR",
40 : : "PGRES_FATAL_ERROR",
41 : : "PGRES_COPY_BOTH",
42 : : "PGRES_SINGLE_TUPLE",
43 : : "PGRES_PIPELINE_SYNC",
44 : : "PGRES_PIPELINE_ABORTED",
45 : : "PGRES_TUPLES_CHUNK"
46 : : };
47 : :
48 : : /* We return this if we're unable to make a PGresult at all */
49 : : static const PGresult OOM_result = {
50 : : .resultStatus = PGRES_FATAL_ERROR,
51 : : .client_encoding = PG_SQL_ASCII,
52 : : .errMsg = "out of memory\n",
53 : : };
54 : :
55 : : /*
56 : : * static state needed by PQescapeString and PQescapeBytea; initialize to
57 : : * values that result in backward-compatible behavior
58 : : */
59 : : static int static_client_encoding = PG_SQL_ASCII;
60 : : static bool static_std_strings = false;
61 : :
62 : :
63 : : static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize);
64 : : static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
65 : : const char **errmsgp);
66 : : static int PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery);
67 : : static bool PQsendQueryStart(PGconn *conn, bool newQuery);
68 : : static int PQsendQueryGuts(PGconn *conn,
69 : : const char *command,
70 : : const char *stmtName,
71 : : int nParams,
72 : : const Oid *paramTypes,
73 : : const char *const *paramValues,
74 : : const int *paramLengths,
75 : : const int *paramFormats,
76 : : int resultFormat);
77 : : static void parseInput(PGconn *conn);
78 : : static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype);
79 : : static bool PQexecStart(PGconn *conn);
80 : : static PGresult *PQexecFinish(PGconn *conn);
81 : : static int PQsendTypedCommand(PGconn *conn, char command, char type,
82 : : const char *target);
83 : : static int check_field_number(const PGresult *res, int field_num);
84 : : static void pqPipelineProcessQueue(PGconn *conn);
85 : : static int pqPipelineSyncInternal(PGconn *conn, bool immediate_flush);
86 : : static int pqPipelineFlush(PGconn *conn);
87 : :
88 : :
89 : : /* ----------------
90 : : * Space management for PGresult.
91 : : *
92 : : * Formerly, libpq did a separate malloc() for each field of each tuple
93 : : * returned by a query. This was remarkably expensive --- malloc/free
94 : : * consumed a sizable part of the application's runtime. And there is
95 : : * no real need to keep track of the fields separately, since they will
96 : : * all be freed together when the PGresult is released. So now, we grab
97 : : * large blocks of storage from malloc and allocate space for query data
98 : : * within these blocks, using a trivially simple allocator. This reduces
99 : : * the number of malloc/free calls dramatically, and it also avoids
100 : : * fragmentation of the malloc storage arena.
101 : : * The PGresult structure itself is still malloc'd separately. We could
102 : : * combine it with the first allocation block, but that would waste space
103 : : * for the common case that no extra storage is actually needed (that is,
104 : : * the SQL command did not return tuples).
105 : : *
106 : : * We also malloc the top-level array of tuple pointers separately, because
107 : : * we need to be able to enlarge it via realloc, and our trivial space
108 : : * allocator doesn't handle that effectively. (Too bad the FE/BE protocol
109 : : * doesn't tell us up front how many tuples will be returned.)
110 : : * All other subsidiary storage for a PGresult is kept in PGresult_data blocks
111 : : * of size PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block
112 : : * is just a link to the next one, if any. Free-space management info is
113 : : * kept in the owning PGresult.
114 : : * A query returning a small amount of data will thus require three malloc
115 : : * calls: one for the PGresult, one for the tuples pointer array, and one
116 : : * PGresult_data block.
117 : : *
118 : : * Only the most recently allocated PGresult_data block is a candidate to
119 : : * have more stuff added to it --- any extra space left over in older blocks
120 : : * is wasted. We could be smarter and search the whole chain, but the point
121 : : * here is to be simple and fast. Typical applications do not keep a PGresult
122 : : * around very long anyway, so some wasted space within one is not a problem.
123 : : *
124 : : * Tuning constants for the space allocator are:
125 : : * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes
126 : : * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data
127 : : * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate
128 : : * blocks, instead of being crammed into a regular allocation block.
129 : : * Requirements for correct function are:
130 : : * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements
131 : : * of all machine data types. (Currently this is set from configure
132 : : * tests, so it should be OK automatically.)
133 : : * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <=
134 : : * PGRESULT_DATA_BLOCKSIZE
135 : : * pqResultAlloc assumes an object smaller than the threshold will fit
136 : : * in a new block.
137 : : * The amount of space wasted at the end of a block could be as much as
138 : : * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large.
139 : : * ----------------
140 : : */
141 : :
142 : : #define PGRESULT_DATA_BLOCKSIZE 2048
143 : : #define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */
144 : : #define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
145 : : #define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2)
146 : :
147 : :
148 : : /*
149 : : * PQmakeEmptyPGresult
150 : : * returns a newly allocated, initialized PGresult with given status.
151 : : * If conn is not NULL and status indicates an error, the conn's
152 : : * errorMessage is copied. Also, any PGEvents are copied from the conn.
153 : : *
154 : : * Note: the logic to copy the conn's errorMessage is now vestigial;
155 : : * no internal caller uses it. However, that behavior is documented for
156 : : * outside callers, so we'd better keep it.
157 : : */
158 : : PGresult *
9865 bruce@momjian.us 159 :CBC 1156299 : PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
160 : : {
161 : : PGresult *result;
162 : :
10226 163 : 1156299 : result = (PGresult *) malloc(sizeof(PGresult));
7391 neilc@samurai.com 164 [ - + ]: 1156299 : if (!result)
7391 neilc@samurai.com 165 :UBC 0 : return NULL;
166 : :
10226 bruce@momjian.us 167 :CBC 1156299 : result->ntups = 0;
168 : 1156299 : result->numAttributes = 0;
169 : 1156299 : result->attDescs = NULL;
170 : 1156299 : result->tuples = NULL;
171 : 1156299 : result->tupArrSize = 0;
6959 tgl@sss.pgh.pa.us 172 : 1156299 : result->numParameters = 0;
173 : 1156299 : result->paramDescs = NULL;
10226 bruce@momjian.us 174 : 1156299 : result->resultStatus = status;
175 : 1156299 : result->cmdStatus[0] = '\0';
176 : 1156299 : result->binary = 0;
6198 tgl@sss.pgh.pa.us 177 : 1156299 : result->events = NULL;
178 : 1156299 : result->nEvents = 0;
9837 179 : 1156299 : result->errMsg = NULL;
8113 180 : 1156299 : result->errFields = NULL;
3443 181 : 1156299 : result->errQuery = NULL;
9789 182 : 1156299 : result->null_field[0] = '\0';
183 : 1156299 : result->curBlock = NULL;
184 : 1156299 : result->curOffset = 0;
185 : 1156299 : result->spaceLeft = 0;
2552 186 : 1156299 : result->memorySize = sizeof(PGresult);
187 : :
9310 188 [ + + ]: 1156299 : if (conn)
189 : : {
190 : : /* copy connection data we might need for operations on PGresult */
8113 191 : 353638 : result->noticeHooks = conn->noticeHooks;
9307 192 : 353638 : result->client_encoding = conn->client_encoding;
193 : :
194 : : /* consider copying conn's errorMessage */
9837 195 [ + + ]: 353638 : switch (status)
196 : : {
197 : 353122 : case PGRES_EMPTY_QUERY:
198 : : case PGRES_COMMAND_OK:
199 : : case PGRES_TUPLES_OK:
200 : : case PGRES_COPY_OUT:
201 : : case PGRES_COPY_IN:
202 : : case PGRES_COPY_BOTH:
203 : : case PGRES_SINGLE_TUPLE:
204 : : case PGRES_TUPLES_CHUNK:
205 : : /* non-error cases */
206 : 353122 : break;
207 : 516 : default:
208 : : /* we intentionally do not use or modify errorReported here */
1296 209 : 516 : pqSetResultError(result, &conn->errorMessage, 0);
9837 210 : 516 : break;
211 : : }
212 : :
213 : : /* copy events last; result must be valid if we need to PQclear */
6198 214 [ - + ]: 353638 : if (conn->nEvents > 0)
215 : : {
2552 tgl@sss.pgh.pa.us 216 :UBC 0 : result->events = dupEvents(conn->events, conn->nEvents,
217 : : &result->memorySize);
6198 218 [ # # ]: 0 : if (!result->events)
219 : : {
220 : 0 : PQclear(result);
221 : 0 : return NULL;
222 : : }
223 : 0 : result->nEvents = conn->nEvents;
224 : : }
225 : : }
226 : : else
227 : : {
228 : : /* defaults... */
8113 tgl@sss.pgh.pa.us 229 :CBC 802661 : result->noticeHooks.noticeRec = NULL;
230 : 802661 : result->noticeHooks.noticeRecArg = NULL;
231 : 802661 : result->noticeHooks.noticeProc = NULL;
232 : 802661 : result->noticeHooks.noticeProcArg = NULL;
233 : 802661 : result->client_encoding = PG_SQL_ASCII;
234 : : }
235 : :
10226 bruce@momjian.us 236 : 1156299 : return result;
237 : : }
238 : :
239 : : /*
240 : : * PQsetResultAttrs
241 : : *
242 : : * Set the attributes for a given result. This function fails if there are
243 : : * already attributes contained in the provided result. The call is
244 : : * ignored if numAttributes is zero or attDescs is NULL. If the
245 : : * function fails, it returns zero. If the function succeeds, it
246 : : * returns a non-zero value.
247 : : */
248 : : int
6198 tgl@sss.pgh.pa.us 249 : 2644 : PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
250 : : {
251 : : int i;
252 : :
253 : : /* Fail if argument is NULL or OOM_result */
1296 254 [ + - - + ]: 2644 : if (!res || (const PGresult *) res == &OOM_result)
1296 tgl@sss.pgh.pa.us 255 :UBC 0 : return false;
256 : :
257 : : /* If attrs already exist, they cannot be overwritten. */
1296 tgl@sss.pgh.pa.us 258 [ - + ]:CBC 2644 : if (res->numAttributes > 0)
2943 peter_e@gmx.net 259 :UBC 0 : return false;
260 : :
261 : : /* ignore no-op request */
6198 tgl@sss.pgh.pa.us 262 [ + - - + ]:CBC 2644 : if (numAttributes <= 0 || !attDescs)
2943 peter_e@gmx.net 263 :UBC 0 : return true;
264 : :
6198 tgl@sss.pgh.pa.us 265 :CBC 2644 : res->attDescs = (PGresAttDesc *)
266 : 2644 : PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
267 : :
268 [ - + ]: 2644 : if (!res->attDescs)
2943 peter_e@gmx.net 269 :UBC 0 : return false;
270 : :
6198 tgl@sss.pgh.pa.us 271 :CBC 2644 : res->numAttributes = numAttributes;
272 : 2644 : memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
273 : :
274 : : /* deep-copy the attribute names, and determine format */
275 : 2644 : res->binary = 1;
276 [ + + ]: 10436 : for (i = 0; i < res->numAttributes; i++)
277 : : {
278 [ + - ]: 7792 : if (res->attDescs[i].name)
279 : 7792 : res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
280 : : else
6198 tgl@sss.pgh.pa.us 281 :UBC 0 : res->attDescs[i].name = res->null_field;
282 : :
6198 tgl@sss.pgh.pa.us 283 [ - + ]:CBC 7792 : if (!res->attDescs[i].name)
2943 peter_e@gmx.net 284 :UBC 0 : return false;
285 : :
6198 tgl@sss.pgh.pa.us 286 [ + + ]:CBC 7792 : if (res->attDescs[i].format == 0)
287 : 505 : res->binary = 0;
288 : : }
289 : :
2943 peter_e@gmx.net 290 : 2644 : return true;
291 : : }
292 : :
293 : : /*
294 : : * PQcopyResult
295 : : *
296 : : * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
297 : : * The 'flags' argument controls which portions of the result will or will
298 : : * NOT be copied. The created result is always put into the
299 : : * PGRES_TUPLES_OK status. The source result error message is not copied,
300 : : * although cmdStatus is.
301 : : *
302 : : * To set custom attributes, use PQsetResultAttrs. That function requires
303 : : * that there are no attrs contained in the result, so to use that
304 : : * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
305 : : * options with this function.
306 : : *
307 : : * Options:
308 : : * PG_COPYRES_ATTRS - Copy the source result's attributes
309 : : *
310 : : * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies
311 : : * copying the attrs, seeing how the attrs are needed by the tuples.
312 : : *
313 : : * PG_COPYRES_EVENTS - Copy the source result's events.
314 : : *
315 : : * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
316 : : */
317 : : PGresult *
6198 tgl@sss.pgh.pa.us 318 : 2644 : PQcopyResult(const PGresult *src, int flags)
319 : : {
320 : : PGresult *dest;
321 : : int i;
322 : :
323 [ - + ]: 2644 : if (!src)
6198 tgl@sss.pgh.pa.us 324 :UBC 0 : return NULL;
325 : :
6198 tgl@sss.pgh.pa.us 326 :CBC 2644 : dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);
327 [ - + ]: 2644 : if (!dest)
6198 tgl@sss.pgh.pa.us 328 :UBC 0 : return NULL;
329 : :
330 : : /* Always copy these over. Is cmdStatus really useful here? */
6198 tgl@sss.pgh.pa.us 331 :CBC 2644 : dest->client_encoding = src->client_encoding;
332 : 2644 : strcpy(dest->cmdStatus, src->cmdStatus);
333 : :
334 : : /* Wants attrs? */
335 [ + - ]: 2644 : if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES))
336 : : {
337 [ - + ]: 2644 : if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
338 : : {
6198 tgl@sss.pgh.pa.us 339 :UBC 0 : PQclear(dest);
340 : 0 : return NULL;
341 : : }
342 : : }
343 : :
344 : : /* Wants to copy tuples? */
6198 tgl@sss.pgh.pa.us 345 [ - + ]:CBC 2644 : if (flags & PG_COPYRES_TUPLES)
346 : : {
347 : : int tup,
348 : : field;
349 : :
6198 tgl@sss.pgh.pa.us 350 [ # # ]:UBC 0 : for (tup = 0; tup < src->ntups; tup++)
351 : : {
352 [ # # ]: 0 : for (field = 0; field < src->numAttributes; field++)
353 : : {
354 [ # # ]: 0 : if (!PQsetvalue(dest, tup, field,
355 : 0 : src->tuples[tup][field].value,
356 : 0 : src->tuples[tup][field].len))
357 : : {
358 : 0 : PQclear(dest);
359 : 0 : return NULL;
360 : : }
361 : : }
362 : : }
363 : : }
364 : :
365 : : /* Wants to copy notice hooks? */
6198 tgl@sss.pgh.pa.us 366 [ + - ]:CBC 2644 : if (flags & PG_COPYRES_NOTICEHOOKS)
367 : 2644 : dest->noticeHooks = src->noticeHooks;
368 : :
369 : : /* Wants to copy PGEvents? */
370 [ + - - + ]: 2644 : if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
371 : : {
2552 tgl@sss.pgh.pa.us 372 :UBC 0 : dest->events = dupEvents(src->events, src->nEvents,
373 : : &dest->memorySize);
6198 374 [ # # ]: 0 : if (!dest->events)
375 : : {
376 : 0 : PQclear(dest);
377 : 0 : return NULL;
378 : : }
379 : 0 : dest->nEvents = src->nEvents;
380 : : }
381 : :
382 : : /* Okay, trigger PGEVT_RESULTCOPY event */
6198 tgl@sss.pgh.pa.us 383 [ - + ]:CBC 2644 : for (i = 0; i < dest->nEvents; i++)
384 : : {
385 : : /* We don't fire events that had some previous failure */
6196 tgl@sss.pgh.pa.us 386 [ # # ]:UBC 0 : if (src->events[i].resultInitialized)
387 : : {
388 : : PGEventResultCopy evt;
389 : :
390 : 0 : evt.src = src;
391 : 0 : evt.dest = dest;
1296 392 [ # # ]: 0 : if (dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
393 : 0 : dest->events[i].passThrough))
394 : 0 : dest->events[i].resultInitialized = true;
395 : : }
396 : : }
397 : :
6198 tgl@sss.pgh.pa.us 398 :CBC 2644 : return dest;
399 : : }
400 : :
401 : : /*
402 : : * Copy an array of PGEvents (with no extra space for more).
403 : : * Does not duplicate the event instance data, sets this to NULL.
404 : : * Also, the resultInitialized flags are all cleared.
405 : : * The total space allocated is added to *memSize.
406 : : */
407 : : static PGEvent *
2552 tgl@sss.pgh.pa.us 408 :UBC 0 : dupEvents(PGEvent *events, int count, size_t *memSize)
409 : : {
410 : : PGEvent *newEvents;
411 : : size_t msize;
412 : : int i;
413 : :
6198 414 [ # # # # ]: 0 : if (!events || count <= 0)
415 : 0 : return NULL;
416 : :
2552 417 : 0 : msize = count * sizeof(PGEvent);
418 : 0 : newEvents = (PGEvent *) malloc(msize);
6198 419 [ # # ]: 0 : if (!newEvents)
420 : 0 : return NULL;
421 : :
422 [ # # ]: 0 : for (i = 0; i < count; i++)
423 : : {
6196 424 : 0 : newEvents[i].proc = events[i].proc;
425 : 0 : newEvents[i].passThrough = events[i].passThrough;
6198 426 : 0 : newEvents[i].data = NULL;
2943 peter_e@gmx.net 427 : 0 : newEvents[i].resultInitialized = false;
6196 tgl@sss.pgh.pa.us 428 : 0 : newEvents[i].name = strdup(events[i].name);
6198 429 [ # # ]: 0 : if (!newEvents[i].name)
430 : : {
431 [ # # ]: 0 : while (--i >= 0)
432 : 0 : free(newEvents[i].name);
433 : 0 : free(newEvents);
434 : 0 : return NULL;
435 : : }
2552 436 : 0 : msize += strlen(events[i].name) + 1;
437 : : }
438 : :
439 : 0 : *memSize += msize;
6198 440 : 0 : return newEvents;
441 : : }
442 : :
443 : :
444 : : /*
445 : : * Sets the value for a tuple field. The tup_num must be less than or
446 : : * equal to PQntuples(res). If it is equal, a new tuple is created and
447 : : * added to the result.
448 : : * Returns a non-zero value for success and zero for failure.
449 : : * (On failure, we report the specific problem via pqInternalNotice.)
450 : : */
451 : : int
452 : 0 : PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
453 : : {
454 : : PGresAttValue *attval;
2930 455 : 0 : const char *errmsg = NULL;
456 : :
457 : : /* Fail if argument is NULL or OOM_result */
1296 458 [ # # # # ]: 0 : if (!res || (const PGresult *) res == &OOM_result)
459 : 0 : return false;
460 : :
461 : : /* Invalid field_num? */
6198 462 [ # # ]: 0 : if (!check_field_number(res, field_num))
2943 peter_e@gmx.net 463 : 0 : return false;
464 : :
465 : : /* Invalid tup_num, must be <= ntups */
6198 tgl@sss.pgh.pa.us 466 [ # # # # ]: 0 : if (tup_num < 0 || tup_num > res->ntups)
467 : : {
2930 468 : 0 : pqInternalNotice(&res->noticeHooks,
469 : : "row number %d is out of range 0..%d",
470 : : tup_num, res->ntups);
2943 peter_e@gmx.net 471 : 0 : return false;
472 : : }
473 : :
474 : : /* need to allocate a new tuple? */
5161 tgl@sss.pgh.pa.us 475 [ # # ]: 0 : if (tup_num == res->ntups)
476 : : {
477 : : PGresAttValue *tup;
478 : : int i;
479 : :
480 : : tup = (PGresAttValue *)
6198 481 : 0 : pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
482 : : true);
483 : :
484 [ # # ]: 0 : if (!tup)
2930 485 : 0 : goto fail;
486 : :
487 : : /* initialize each column to NULL */
6198 488 [ # # ]: 0 : for (i = 0; i < res->numAttributes; i++)
489 : : {
490 : 0 : tup[i].len = NULL_LEN;
491 : 0 : tup[i].value = res->null_field;
492 : : }
493 : :
494 : : /* add it to the array */
2930 495 [ # # ]: 0 : if (!pqAddTuple(res, tup, &errmsg))
496 : 0 : goto fail;
497 : : }
498 : :
6198 499 : 0 : attval = &res->tuples[tup_num][field_num];
500 : :
501 : : /* treat either NULL_LEN or NULL value pointer as a NULL field */
502 [ # # # # ]: 0 : if (len == NULL_LEN || value == NULL)
503 : : {
504 : 0 : attval->len = NULL_LEN;
505 : 0 : attval->value = res->null_field;
506 : : }
507 [ # # ]: 0 : else if (len <= 0)
508 : : {
509 : 0 : attval->len = 0;
510 : 0 : attval->value = res->null_field;
511 : : }
512 : : else
513 : : {
2943 peter_e@gmx.net 514 : 0 : attval->value = (char *) pqResultAlloc(res, len + 1, true);
6198 tgl@sss.pgh.pa.us 515 [ # # ]: 0 : if (!attval->value)
2930 516 : 0 : goto fail;
6198 517 : 0 : attval->len = len;
518 : 0 : memcpy(attval->value, value, len);
519 : 0 : attval->value[len] = '\0';
520 : : }
521 : :
2943 peter_e@gmx.net 522 : 0 : return true;
523 : :
524 : : /*
525 : : * Report failure via pqInternalNotice. If preceding code didn't provide
526 : : * an error message, assume "out of memory" was meant.
527 : : */
2930 tgl@sss.pgh.pa.us 528 : 0 : fail:
529 [ # # ]: 0 : if (!errmsg)
530 : 0 : errmsg = libpq_gettext("out of memory");
531 : 0 : pqInternalNotice(&res->noticeHooks, "%s", errmsg);
532 : :
2943 peter_e@gmx.net 533 : 0 : return false;
534 : : }
535 : :
536 : : /*
537 : : * pqResultAlloc - exported routine to allocate local storage in a PGresult.
538 : : *
539 : : * We force all such allocations to be maxaligned, since we don't know
540 : : * whether the value might be binary.
541 : : */
542 : : void *
6198 tgl@sss.pgh.pa.us 543 :CBC 2644 : PQresultAlloc(PGresult *res, size_t nBytes)
544 : : {
545 : : /* Fail if argument is NULL or OOM_result */
1296 546 [ + - - + ]: 2644 : if (!res || (const PGresult *) res == &OOM_result)
1296 tgl@sss.pgh.pa.us 547 :UBC 0 : return NULL;
548 : :
2943 peter_e@gmx.net 549 :CBC 2644 : return pqResultAlloc(res, nBytes, true);
550 : : }
551 : :
552 : : /*
553 : : * pqResultAlloc -
554 : : * Allocate subsidiary storage for a PGresult.
555 : : *
556 : : * nBytes is the amount of space needed for the object.
557 : : * If isBinary is true, we assume that we need to align the object on
558 : : * a machine allocation boundary.
559 : : * If isBinary is false, we assume the object is a char string and can
560 : : * be allocated on any byte boundary.
561 : : */
562 : : void *
9431 bruce@momjian.us 563 : 23504081 : pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
564 : : {
565 : : char *space;
566 : : PGresult_data *block;
567 : :
9601 568 [ - + ]: 23504081 : if (!res)
9789 tgl@sss.pgh.pa.us 569 :UBC 0 : return NULL;
570 : :
9789 tgl@sss.pgh.pa.us 571 [ + + ]:CBC 23504081 : if (nBytes <= 0)
572 : 188 : return res->null_field;
573 : :
574 : : /*
575 : : * If alignment is needed, round up the current position to an alignment
576 : : * boundary.
577 : : */
578 [ + + ]: 23503893 : if (isBinary)
579 : : {
9601 bruce@momjian.us 580 : 4366784 : int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;
581 : :
9789 tgl@sss.pgh.pa.us 582 [ + + ]: 4366784 : if (offset)
583 : : {
584 : 3279143 : res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;
585 : 3279143 : res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset;
586 : : }
587 : : }
588 : :
589 : : /* If there's enough space in the current block, no problem. */
8717 bruce@momjian.us 590 [ + + ]: 23503893 : if (nBytes <= (size_t) res->spaceLeft)
591 : : {
9789 tgl@sss.pgh.pa.us 592 : 23084885 : space = res->curBlock->space + res->curOffset;
593 : 23084885 : res->curOffset += nBytes;
594 : 23084885 : res->spaceLeft -= nBytes;
595 : 23084885 : return space;
596 : : }
597 : :
598 : : /*
599 : : * If the requested object is very large, give it its own block; this
600 : : * avoids wasting what might be most of the current block to start a new
601 : : * block. (We'd have to special-case requests bigger than the block size
602 : : * anyway.) The object is always given binary alignment in this case.
603 : : */
604 [ + + ]: 419008 : if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)
605 : : {
2552 606 : 2756 : size_t alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD;
607 : :
608 : 2756 : block = (PGresult_data *) malloc(alloc_size);
9601 bruce@momjian.us 609 [ - + ]: 2756 : if (!block)
9789 tgl@sss.pgh.pa.us 610 :UBC 0 : return NULL;
2552 tgl@sss.pgh.pa.us 611 :CBC 2756 : res->memorySize += alloc_size;
9614 612 : 2756 : space = block->space + PGRESULT_BLOCK_OVERHEAD;
9789 613 [ + + ]: 2756 : if (res->curBlock)
614 : : {
615 : : /*
616 : : * Tuck special block below the active block, so that we don't
617 : : * have to waste the free space in the active block.
618 : : */
619 : 2531 : block->next = res->curBlock->next;
620 : 2531 : res->curBlock->next = block;
621 : : }
622 : : else
623 : : {
624 : : /* Must set up the new block as the first active block. */
625 : 225 : block->next = NULL;
626 : 225 : res->curBlock = block;
9601 bruce@momjian.us 627 : 225 : res->spaceLeft = 0; /* be sure it's marked full */
628 : : }
9789 tgl@sss.pgh.pa.us 629 : 2756 : return space;
630 : : }
631 : :
632 : : /* Otherwise, start a new block. */
633 : 416252 : block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);
9601 bruce@momjian.us 634 [ - + ]: 416252 : if (!block)
9789 tgl@sss.pgh.pa.us 635 :UBC 0 : return NULL;
2552 tgl@sss.pgh.pa.us 636 :CBC 416252 : res->memorySize += PGRESULT_DATA_BLOCKSIZE;
9789 637 : 416252 : block->next = res->curBlock;
638 : 416252 : res->curBlock = block;
639 [ + + ]: 416252 : if (isBinary)
640 : : {
641 : : /* object needs full alignment */
9614 642 : 372553 : res->curOffset = PGRESULT_BLOCK_OVERHEAD;
643 : 372553 : res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;
644 : : }
645 : : else
646 : : {
647 : : /* we can cram it right after the overhead pointer */
9789 648 : 43699 : res->curOffset = sizeof(PGresult_data);
649 : 43699 : res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data);
650 : : }
651 : :
652 : 416252 : space = block->space + res->curOffset;
653 : 416252 : res->curOffset += nBytes;
654 : 416252 : res->spaceLeft -= nBytes;
655 : 416252 : return space;
656 : : }
657 : :
658 : : /*
659 : : * PQresultMemorySize -
660 : : * Returns total space allocated for the PGresult.
661 : : */
662 : : size_t
2552 tgl@sss.pgh.pa.us 663 :UBC 0 : PQresultMemorySize(const PGresult *res)
664 : : {
665 [ # # ]: 0 : if (!res)
666 : 0 : return 0;
667 : 0 : return res->memorySize;
668 : : }
669 : :
670 : : /*
671 : : * pqResultStrdup -
672 : : * Like strdup, but the space is subsidiary PGresult space.
673 : : */
674 : : char *
9789 tgl@sss.pgh.pa.us 675 :CBC 561294 : pqResultStrdup(PGresult *res, const char *str)
676 : : {
2943 peter_e@gmx.net 677 : 561294 : char *space = (char *) pqResultAlloc(res, strlen(str) + 1, false);
678 : :
9789 tgl@sss.pgh.pa.us 679 [ + - ]: 561294 : if (space)
680 : 561294 : strcpy(space, str);
681 : 561294 : return space;
682 : : }
683 : :
684 : : /*
685 : : * pqSetResultError -
686 : : * assign a new error message to a PGresult
687 : : *
688 : : * Copy text from errorMessage buffer beginning at given offset
689 : : * (it's caller's responsibility that offset is valid)
690 : : */
691 : : void
1296 692 : 22698 : pqSetResultError(PGresult *res, PQExpBuffer errorMessage, int offset)
693 : : {
694 : : char *msg;
695 : :
9837 696 [ - + ]: 22698 : if (!res)
9837 tgl@sss.pgh.pa.us 697 :UBC 0 : return;
698 : :
699 : : /*
700 : : * We handle two OOM scenarios here. The errorMessage buffer might be
701 : : * marked "broken" due to having previously failed to allocate enough
702 : : * memory for the message, or it might be fine but pqResultStrdup fails
703 : : * and returns NULL. In either case, just make res->errMsg point directly
704 : : * at a constant "out of memory" string.
705 : : */
1500 tgl@sss.pgh.pa.us 706 [ + - + - ]:CBC 22698 : if (!PQExpBufferBroken(errorMessage))
1296 707 : 22698 : msg = pqResultStrdup(res, errorMessage->data + offset);
708 : : else
1500 tgl@sss.pgh.pa.us 709 :UBC 0 : msg = NULL;
1500 tgl@sss.pgh.pa.us 710 [ + - ]:CBC 22698 : if (msg)
711 : 22698 : res->errMsg = msg;
712 : : else
1500 tgl@sss.pgh.pa.us 713 :UBC 0 : res->errMsg = libpq_gettext("out of memory\n");
714 : : }
715 : :
716 : : /*
717 : : * PQclear -
718 : : * free's the memory associated with a PGresult
719 : : */
720 : : void
9985 bruce@momjian.us 721 :CBC 2007150 : PQclear(PGresult *res)
722 : : {
723 : : PGresult_data *block;
724 : : int i;
725 : :
726 : : /* As a convenience, do nothing for a NULL pointer */
727 [ + + ]: 2007150 : if (!res)
728 : 852379 : return;
729 : : /* Also, do nothing if the argument is OOM_result */
1296 tgl@sss.pgh.pa.us 730 [ - + ]: 1154771 : if ((const PGresult *) res == &OOM_result)
1296 tgl@sss.pgh.pa.us 731 :UBC 0 : return;
732 : :
733 : : /* Close down any events we may have */
6198 tgl@sss.pgh.pa.us 734 [ - + ]:CBC 1154771 : for (i = 0; i < res->nEvents; i++)
735 : : {
736 : : /* only send DESTROY to successfully-initialized event procs */
6196 tgl@sss.pgh.pa.us 737 [ # # ]:UBC 0 : if (res->events[i].resultInitialized)
738 : : {
739 : : PGEventResultDestroy evt;
740 : :
741 : 0 : evt.result = res;
742 : 0 : (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
743 : 0 : res->events[i].passThrough);
744 : : }
6198 745 : 0 : free(res->events[i].name);
746 : : }
747 : :
1178 peter@eisentraut.org 748 :CBC 1154771 : free(res->events);
749 : :
750 : : /* Free all the subsidiary blocks */
9601 bruce@momjian.us 751 [ + + ]: 1572158 : while ((block = res->curBlock) != NULL)
752 : : {
9789 tgl@sss.pgh.pa.us 753 : 417387 : res->curBlock = block->next;
754 : 417387 : free(block);
755 : : }
756 : :
757 : : /* Free the top-level tuple pointer array */
1178 peter@eisentraut.org 758 : 1154771 : free(res->tuples);
759 : :
760 : : /* zero out the pointer fields to catch programming errors */
7041 alvherre@alvh.no-ip. 761 : 1154771 : res->attDescs = NULL;
762 : 1154771 : res->tuples = NULL;
6959 tgl@sss.pgh.pa.us 763 : 1154771 : res->paramDescs = NULL;
7041 alvherre@alvh.no-ip. 764 : 1154771 : res->errFields = NULL;
6198 tgl@sss.pgh.pa.us 765 : 1154771 : res->events = NULL;
766 : 1154771 : res->nEvents = 0;
767 : : /* res->curBlock was zeroed out earlier */
768 : :
769 : : /* Free the PGresult structure itself */
9985 bruce@momjian.us 770 : 1154771 : free(res);
771 : : }
772 : :
773 : : /*
774 : : * Handy subroutine to deallocate any partially constructed async result.
775 : : *
776 : : * Any "saved" result gets cleared too.
777 : : */
778 : : void
9882 scrappy@hub.org 779 : 382132 : pqClearAsyncResult(PGconn *conn)
780 : : {
1161 peter@eisentraut.org 781 : 382132 : PQclear(conn->result);
9985 bruce@momjian.us 782 : 382132 : conn->result = NULL;
1296 tgl@sss.pgh.pa.us 783 : 382132 : conn->error_result = false;
518 784 : 382132 : PQclear(conn->saved_result);
785 : 382132 : conn->saved_result = NULL;
10651 scrappy@hub.org 786 : 382132 : }
787 : :
788 : : /*
789 : : * pqSaveErrorResult -
790 : : * remember that we have an error condition
791 : : *
792 : : * In much of libpq, reporting an error just requires appending text to
793 : : * conn->errorMessage and returning a failure code to one's caller.
794 : : * Where returning a failure code is impractical, instead call this
795 : : * function to remember that an error needs to be reported.
796 : : *
797 : : * (It might seem that appending text to conn->errorMessage should be
798 : : * sufficient, but we can't rely on that working under out-of-memory
799 : : * conditions. The OOM hazard is also why we don't try to make a new
800 : : * PGresult right here.)
801 : : */
802 : : void
8126 tgl@sss.pgh.pa.us 803 : 47 : pqSaveErrorResult(PGconn *conn)
804 : : {
805 : : /* Drop any pending result ... */
1699 806 : 47 : pqClearAsyncResult(conn);
807 : : /* ... and set flag to remember to make an error result later */
1296 808 : 47 : conn->error_result = true;
9503 809 : 47 : }
810 : :
811 : : /*
812 : : * pqSaveWriteError -
813 : : * report a write failure
814 : : *
815 : : * As above, after appending conn->write_err_msg to whatever other error we
816 : : * have. This is used when we've detected a write failure and have exhausted
817 : : * our chances of reporting something else instead.
818 : : */
819 : : static void
2363 820 : 3 : pqSaveWriteError(PGconn *conn)
821 : : {
822 : : /*
823 : : * If write_err_msg is null because of previous strdup failure, do what we
824 : : * can. (It's likely our machinations here will get OOM failures as well,
825 : : * but might as well try.)
826 : : */
1699 827 [ + - ]: 3 : if (conn->write_err_msg)
828 : : {
829 : 3 : appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg);
830 : : /* Avoid possibly appending the same message twice */
831 : 3 : conn->write_err_msg[0] = '\0';
832 : : }
833 : : else
1026 peter@eisentraut.org 834 :UBC 0 : libpq_append_conn_error(conn, "write to server failed");
835 : :
1699 tgl@sss.pgh.pa.us 836 :CBC 3 : pqSaveErrorResult(conn);
2363 837 : 3 : }
838 : :
839 : : /*
840 : : * pqPrepareAsyncResult -
841 : : * prepare the current async result object for return to the caller
842 : : *
843 : : * If there is not already an async result object, build an error object
844 : : * using whatever is in conn->errorMessage. In any case, clear the async
845 : : * result storage, and update our notion of how much error text has been
846 : : * returned to the application.
847 : : *
848 : : * Note that in no case (not even OOM) do we return NULL.
849 : : */
850 : : PGresult *
8126 851 : 340304 : pqPrepareAsyncResult(PGconn *conn)
852 : : {
853 : : PGresult *res;
854 : :
9503 855 : 340304 : res = conn->result;
1296 856 [ + + ]: 340304 : if (res)
857 : : {
858 : : /*
859 : : * If the pre-existing result is an ERROR (presumably something
860 : : * received from the server), assume that it represents whatever is in
861 : : * conn->errorMessage, and advance errorReported.
862 : : */
863 [ + + ]: 340257 : if (res->resultStatus == PGRES_FATAL_ERROR)
864 : 21773 : conn->errorReported = conn->errorMessage.len;
865 : : }
866 : : else
867 : : {
868 : : /*
869 : : * We get here after internal-to-libpq errors. We should probably
870 : : * always have error_result = true, but if we don't, gin up some error
871 : : * text.
872 : : */
873 [ - + ]: 47 : if (!conn->error_result)
1026 peter@eisentraut.org 874 :UBC 0 : libpq_append_conn_error(conn, "no error text available");
875 : :
876 : : /* Paranoia: be sure errorReported offset is sane */
1296 tgl@sss.pgh.pa.us 877 [ + - ]:CBC 47 : if (conn->errorReported < 0 ||
878 [ - + ]: 47 : conn->errorReported >= conn->errorMessage.len)
1296 tgl@sss.pgh.pa.us 879 :UBC 0 : conn->errorReported = 0;
880 : :
881 : : /*
882 : : * Make a PGresult struct for the error. We temporarily lie about the
883 : : * result status, so that PQmakeEmptyPGresult doesn't uselessly copy
884 : : * all of conn->errorMessage.
885 : : */
1296 tgl@sss.pgh.pa.us 886 :CBC 47 : res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
887 [ + - ]: 47 : if (res)
888 : : {
889 : : /*
890 : : * Report whatever new error text we have, and advance
891 : : * errorReported.
892 : : */
893 : 47 : res->resultStatus = PGRES_FATAL_ERROR;
894 : 47 : pqSetResultError(res, &conn->errorMessage, conn->errorReported);
895 : 47 : conn->errorReported = conn->errorMessage.len;
896 : : }
897 : : else
898 : : {
899 : : /*
900 : : * Ouch, not enough memory for a PGresult. Fortunately, we have a
901 : : * card up our sleeve: we can use the static OOM_result. Casting
902 : : * away const here is a bit ugly, but it seems best to declare
903 : : * OOM_result as const, in hopes it will be allocated in read-only
904 : : * storage.
905 : : */
1296 tgl@sss.pgh.pa.us 906 :UBC 0 : res = unconstify(PGresult *, &OOM_result);
907 : :
908 : : /*
909 : : * Don't advance errorReported. Perhaps we'll be able to report
910 : : * the text later.
911 : : */
912 : : }
913 : : }
914 : :
915 : : /*
916 : : * Replace conn->result with saved_result, if any. In the normal case
917 : : * there isn't a saved result and we're just dropping ownership of the
918 : : * current result. In partial-result mode this restores the situation to
919 : : * what it was before we created the current partial result.
920 : : */
518 tgl@sss.pgh.pa.us 921 :CBC 340304 : conn->result = conn->saved_result;
922 : 340304 : conn->error_result = false; /* saved_result is never an error */
923 : 340304 : conn->saved_result = NULL;
924 : :
9503 925 : 340304 : return res;
926 : : }
927 : :
928 : : /*
929 : : * pqInternalNotice - produce an internally-generated notice message
930 : : *
931 : : * A format string and optional arguments can be passed. Note that we do
932 : : * libpq_gettext() here, so callers need not.
933 : : *
934 : : * The supplied text is taken as primary message (ie., it should not include
935 : : * a trailing newline, and should not be more than one line).
936 : : */
937 : : void
7228 bruce@momjian.us 938 :UBC 0 : pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
939 : : {
940 : : char msgBuf[1024];
941 : : va_list args;
942 : : PGresult *res;
943 : :
8113 tgl@sss.pgh.pa.us 944 [ # # ]: 0 : if (hooks->noticeRec == NULL)
8111 945 : 0 : return; /* nobody home to receive notice? */
946 : :
947 : : /* Format the message */
948 : 0 : va_start(args, fmt);
949 : 0 : vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args);
950 : 0 : va_end(args);
8069 bruce@momjian.us 951 : 0 : msgBuf[sizeof(msgBuf) - 1] = '\0'; /* make real sure it's terminated */
952 : :
953 : : /* Make a PGresult to pass to the notice receiver */
8113 tgl@sss.pgh.pa.us 954 : 0 : res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
7391 neilc@samurai.com 955 [ # # ]: 0 : if (!res)
956 : 0 : return;
8113 tgl@sss.pgh.pa.us 957 : 0 : res->noticeHooks = *hooks;
958 : :
959 : : /*
960 : : * Set up fields of notice.
961 : : */
8046 peter_e@gmx.net 962 : 0 : pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);
963 : 0 : pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE"));
3298 tgl@sss.pgh.pa.us 964 : 0 : pqSaveMessageField(res, PG_DIAG_SEVERITY_NONLOCALIZED, "NOTICE");
965 : : /* XXX should provide a SQLSTATE too? */
966 : :
967 : : /*
968 : : * Result text is always just the primary message + newline. If we can't
969 : : * allocate it, substitute "out of memory", as in pqSetResultError.
970 : : */
2943 peter_e@gmx.net 971 : 0 : res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, false);
7391 neilc@samurai.com 972 [ # # ]: 0 : if (res->errMsg)
973 : 0 : sprintf(res->errMsg, "%s\n", msgBuf);
974 : : else
1500 tgl@sss.pgh.pa.us 975 : 0 : res->errMsg = libpq_gettext("out of memory\n");
976 : :
977 : : /*
978 : : * Pass to receiver, then free it.
979 : : */
980 : 0 : res->noticeHooks.noticeRec(res->noticeHooks.noticeRecArg, res);
8113 981 : 0 : PQclear(res);
982 : : }
983 : :
984 : : /*
985 : : * pqAddTuple
986 : : * add a row pointer to the PGresult structure, growing it if necessary
987 : : * Returns true if OK, false if an error prevented adding the row
988 : : *
989 : : * On error, *errmsgp can be set to an error string to be returned.
990 : : * If it is left NULL, the error is presumed to be "out of memory".
991 : : */
992 : : static bool
2930 tgl@sss.pgh.pa.us 993 :CBC 3927825 : pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp)
994 : : {
9985 bruce@momjian.us 995 [ + + ]: 3927825 : if (res->ntups >= res->tupArrSize)
996 : : {
997 : : /*
998 : : * Try to grow the array.
999 : : *
1000 : : * We can use realloc because shallow copying of the structure is
1001 : : * okay. Note that the first time through, res->tuples is NULL. While
1002 : : * ANSI says that realloc() should act like malloc() in that case,
1003 : : * some old C libraries (like SunOS 4.1.x) coredump instead. On
1004 : : * failure realloc is supposed to return NULL without damaging the
1005 : : * existing allocation. Note that the positions beyond res->ntups are
1006 : : * garbage, not necessarily NULL.
1007 : : */
1008 : : int newSize;
1009 : : PGresAttValue **newTuples;
1010 : :
1011 : : /*
1012 : : * Since we use integers for row numbers, we can't support more than
1013 : : * INT_MAX rows. Make sure we allow that many, though.
1014 : : */
2930 tgl@sss.pgh.pa.us 1015 [ + - ]: 140809 : if (res->tupArrSize <= INT_MAX / 2)
1016 [ + + ]: 140809 : newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
2930 tgl@sss.pgh.pa.us 1017 [ # # ]:UBC 0 : else if (res->tupArrSize < INT_MAX)
1018 : 0 : newSize = INT_MAX;
1019 : : else
1020 : : {
1021 : 0 : *errmsgp = libpq_gettext("PGresult cannot support more than INT_MAX tuples");
2943 peter_e@gmx.net 1022 : 0 : return false;
1023 : : }
1024 : :
1025 : : /*
1026 : : * Also, on 32-bit platforms we could, in theory, overflow size_t even
1027 : : * before newSize gets to INT_MAX. (In practice we'd doubtless hit
1028 : : * OOM long before that, but let's check.)
1029 : : */
1030 : : #if INT_MAX >= (SIZE_MAX / 2)
1031 : : if (newSize > SIZE_MAX / sizeof(PGresAttValue *))
1032 : : {
1033 : : *errmsgp = libpq_gettext("size_t overflow");
1034 : : return false;
1035 : : }
1036 : : #endif
1037 : :
9778 tgl@sss.pgh.pa.us 1038 [ + + ]:CBC 140809 : if (res->tuples == NULL)
1039 : : newTuples = (PGresAttValue **)
1040 : 133611 : malloc(newSize * sizeof(PGresAttValue *));
1041 : : else
1042 : : newTuples = (PGresAttValue **)
1043 : 7198 : realloc(res->tuples, newSize * sizeof(PGresAttValue *));
9601 bruce@momjian.us 1044 [ - + ]: 140809 : if (!newTuples)
2943 peter_e@gmx.net 1045 :UBC 0 : return false; /* malloc or realloc failed */
2552 tgl@sss.pgh.pa.us 1046 :CBC 140809 : res->memorySize +=
1047 : 140809 : (newSize - res->tupArrSize) * sizeof(PGresAttValue *);
9837 1048 : 140809 : res->tupArrSize = newSize;
1049 : 140809 : res->tuples = newTuples;
1050 : : }
10226 bruce@momjian.us 1051 : 3927825 : res->tuples[res->ntups] = tup;
1052 : 3927825 : res->ntups++;
2943 peter_e@gmx.net 1053 : 3927825 : return true;
1054 : : }
1055 : :
1056 : : /*
1057 : : * pqSaveMessageField - save one field of an error or notice message
1058 : : */
1059 : : void
8113 tgl@sss.pgh.pa.us 1060 : 265312 : pqSaveMessageField(PGresult *res, char code, const char *value)
1061 : : {
1062 : : PGMessageField *pfield;
1063 : :
1064 : : pfield = (PGMessageField *)
1065 : 265312 : pqResultAlloc(res,
1066 : : offsetof(PGMessageField, contents) +
3850 1067 : 265312 : strlen(value) + 1,
1068 : : true);
8113 1069 [ - + ]: 265312 : if (!pfield)
8113 tgl@sss.pgh.pa.us 1070 :UBC 0 : return; /* out of memory? */
8113 tgl@sss.pgh.pa.us 1071 :CBC 265312 : pfield->code = code;
1072 : 265312 : strcpy(pfield->contents, value);
1073 : 265312 : pfield->next = res->errFields;
1074 : 265312 : res->errFields = pfield;
1075 : : }
1076 : :
1077 : : /*
1078 : : * pqSaveParameterStatus - remember parameter status sent by backend
1079 : : *
1080 : : * Returns 1 on success, 0 on out-of-memory. (Note that on out-of-memory, we
1081 : : * have already released the old value of the parameter, if any. The only
1082 : : * really safe way to recover is to terminate the connection.)
1083 : : */
1084 : : int
8126 1085 : 191385 : pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
1086 : : {
1087 : : pgParameterStatus *pstatus;
1088 : : pgParameterStatus *prev;
1089 : :
1090 : : /*
1091 : : * Forget any old information about the parameter
1092 : : */
1093 : 191385 : for (pstatus = conn->pstatus, prev = NULL;
1094 [ + + ]: 1514005 : pstatus != NULL;
1095 : 1322620 : prev = pstatus, pstatus = pstatus->next)
1096 : : {
1097 [ + + ]: 1329505 : if (strcmp(pstatus->name, name) == 0)
1098 : : {
1099 [ + + ]: 6885 : if (prev)
1100 : 4766 : prev->next = pstatus->next;
1101 : : else
1102 : 2119 : conn->pstatus = pstatus->next;
1103 : 6885 : free(pstatus); /* frees name and value strings too */
1104 : 6885 : break;
1105 : : }
1106 : : }
1107 : :
1108 : : /*
1109 : : * Store new info as a single malloc block
1110 : : */
1111 : 191385 : pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
2999 1112 : 191385 : strlen(name) + strlen(value) + 2);
8126 1113 [ + - ]: 191385 : if (pstatus)
1114 : : {
1115 : : char *ptr;
1116 : :
1117 : 191385 : ptr = ((char *) pstatus) + sizeof(pgParameterStatus);
1118 : 191385 : pstatus->name = ptr;
1119 : 191385 : strcpy(ptr, name);
1120 : 191385 : ptr += strlen(name) + 1;
1121 : 191385 : pstatus->value = ptr;
1122 : 191385 : strcpy(ptr, value);
1123 : 191385 : pstatus->next = conn->pstatus;
1124 : 191385 : conn->pstatus = pstatus;
1125 : : }
1126 : : else
1127 : : {
1128 : : /* out of memory */
15 heikki.linnakangas@i 1129 :UBC 0 : return 0;
1130 : : }
1131 : :
1132 : : /*
1133 : : * Save values of settings that are of interest to libpq in fields of the
1134 : : * PGconn object. We keep client_encoding and standard_conforming_strings
1135 : : * in static variables as well, so that PQescapeString and PQescapeBytea
1136 : : * can behave somewhat sanely (at least in single-connection-using
1137 : : * programs).
1138 : : */
8126 tgl@sss.pgh.pa.us 1139 [ + + ]:CBC 191385 : if (strcmp(name, "client_encoding") == 0)
1140 : : {
1141 : 12333 : conn->client_encoding = pg_char_to_encoding(value);
1142 : : /* if we don't recognize the encoding name, fall back to SQL_ASCII */
6538 1143 [ - + ]: 12333 : if (conn->client_encoding < 0)
6538 tgl@sss.pgh.pa.us 1144 :UBC 0 : conn->client_encoding = PG_SQL_ASCII;
7048 tgl@sss.pgh.pa.us 1145 :CBC 12333 : static_client_encoding = conn->client_encoding;
1146 : : }
1147 [ + + ]: 179052 : else if (strcmp(name, "standard_conforming_strings") == 0)
1148 : : {
1149 : 12333 : conn->std_strings = (strcmp(value, "on") == 0);
1150 : 12333 : static_std_strings = conn->std_strings;
1151 : : }
7855 1152 [ + + ]: 166719 : else if (strcmp(name, "server_version") == 0)
1153 : : {
1154 : : /* We convert the server version to numeric form. */
1155 : : int cnt;
1156 : : int vmaj,
1157 : : vmin,
1158 : : vrev;
1159 : :
1160 : 12300 : cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
1161 : :
3319 1162 [ - + ]: 12300 : if (cnt == 3)
1163 : : {
1164 : : /* old style, e.g. 9.6.1 */
7855 tgl@sss.pgh.pa.us 1165 :UBC 0 : conn->sversion = (100 * vmaj + vmin) * 100 + vrev;
1166 : : }
3319 tgl@sss.pgh.pa.us 1167 [ - + ]:CBC 12300 : else if (cnt == 2)
1168 : : {
3319 tgl@sss.pgh.pa.us 1169 [ # # ]:UBC 0 : if (vmaj >= 10)
1170 : : {
1171 : : /* new style, e.g. 10.1 */
1172 : 0 : conn->sversion = 100 * 100 * vmaj + vmin;
1173 : : }
1174 : : else
1175 : : {
1176 : : /* old style without minor version, e.g. 9.6devel */
1177 : 0 : conn->sversion = (100 * vmaj + vmin) * 100;
1178 : : }
1179 : : }
3319 tgl@sss.pgh.pa.us 1180 [ + - ]:CBC 12300 : else if (cnt == 1)
1181 : : {
1182 : : /* new style without minor version, e.g. 10devel */
1183 : 12300 : conn->sversion = 100 * 100 * vmaj;
1184 : : }
1185 : : else
3319 tgl@sss.pgh.pa.us 1186 :UBC 0 : conn->sversion = 0; /* unknown */
1187 : : }
1649 tgl@sss.pgh.pa.us 1188 [ + + ]:CBC 154419 : else if (strcmp(name, "default_transaction_read_only") == 0)
1189 : : {
1190 : 12320 : conn->default_transaction_read_only =
1191 [ + + ]: 12320 : (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
1192 : : }
1193 [ + + ]: 142099 : else if (strcmp(name, "in_hot_standby") == 0)
1194 : : {
1195 : 12303 : conn->in_hot_standby =
1196 [ + + ]: 12303 : (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
1197 : : }
894 dgustafsson@postgres 1198 [ + + ]: 129796 : else if (strcmp(name, "scram_iterations") == 0)
1199 : : {
1200 : 12306 : conn->scram_sha_256_iterations = atoi(value);
1201 : : }
1202 : :
15 heikki.linnakangas@i 1203 : 191385 : return 1;
1204 : : }
1205 : :
1206 : :
1207 : : /*
1208 : : * pqRowProcessor
1209 : : * Add the received row to the current async result (conn->result).
1210 : : * Returns 1 if OK, 0 if error occurred.
1211 : : *
1212 : : * On error, *errmsgp can be set to an error string to be returned.
1213 : : * (Such a string should already be translated via libpq_gettext().)
1214 : : * If it is left NULL, the error is presumed to be "out of memory".
1215 : : */
1216 : : int
4783 tgl@sss.pgh.pa.us 1217 : 3927825 : pqRowProcessor(PGconn *conn, const char **errmsgp)
1218 : : {
1219 : 3927825 : PGresult *res = conn->result;
4903 1220 : 3927825 : int nfields = res->numAttributes;
4783 1221 : 3927825 : const PGdataValue *columns = conn->rowBuf;
1222 : : PGresAttValue *tup;
1223 : : int i;
1224 : :
1225 : : /*
1226 : : * In partial-result mode, if we don't already have a partial PGresult
1227 : : * then make one by cloning conn->result (which should hold the correct
1228 : : * result metadata by now). Then the original conn->result is moved over
1229 : : * to saved_result so that we can re-use it as a reference for future
1230 : : * partial results. The saved result will become active again after
1231 : : * pqPrepareAsyncResult() returns the partial result to the application.
1232 : : */
518 1233 [ + + + + ]: 3927825 : if (conn->partialResMode && conn->saved_result == NULL)
1234 : : {
1235 : : /* Copy everything that should be in the result at this point */
4783 1236 : 2644 : res = PQcopyResult(res,
1237 : : PG_COPYRES_ATTRS | PG_COPYRES_EVENTS |
1238 : : PG_COPYRES_NOTICEHOOKS);
1239 [ - + ]: 2644 : if (!res)
4783 tgl@sss.pgh.pa.us 1240 :UBC 0 : return 0;
1241 : : /* Change result status to appropriate special value */
518 tgl@sss.pgh.pa.us 1242 [ + + ]:CBC 2644 : res->resultStatus = (conn->singleRowMode ? PGRES_SINGLE_TUPLE : PGRES_TUPLES_CHUNK);
1243 : : /* And stash it as the active result */
1244 : 2644 : conn->saved_result = conn->result;
1245 : 2644 : conn->result = res;
1246 : : }
1247 : :
1248 : : /*
1249 : : * Basically we just allocate space in the PGresult for each field and
1250 : : * copy the data over.
1251 : : *
1252 : : * Note: on malloc failure, we return 0 leaving *errmsgp still NULL, which
1253 : : * caller will take to mean "out of memory". This is preferable to trying
1254 : : * to set up such a message here, because evidently there's not enough
1255 : : * memory for gettext() to do anything.
1256 : : */
1257 : : tup = (PGresAttValue *)
2943 peter_e@gmx.net 1258 : 3927825 : pqResultAlloc(res, nfields * sizeof(PGresAttValue), true);
4903 tgl@sss.pgh.pa.us 1259 [ - + ]: 3927825 : if (tup == NULL)
518 tgl@sss.pgh.pa.us 1260 :UBC 0 : return 0;
1261 : :
4903 tgl@sss.pgh.pa.us 1262 [ + + ]:CBC 23587396 : for (i = 0; i < nfields; i++)
1263 : : {
4836 bruce@momjian.us 1264 : 19659571 : int clen = columns[i].len;
1265 : :
4903 tgl@sss.pgh.pa.us 1266 [ + + ]: 19659571 : if (clen < 0)
1267 : : {
1268 : : /* null field */
1269 : 1076448 : tup[i].len = NULL_LEN;
1270 : 1076448 : tup[i].value = res->null_field;
1271 : : }
1272 : : else
1273 : : {
1274 : 18583123 : bool isbinary = (res->attDescs[i].format != 0);
1275 : : char *val;
1276 : :
1277 : 18583123 : val = (char *) pqResultAlloc(res, clen + 1, isbinary);
1278 [ - + ]: 18583123 : if (val == NULL)
518 tgl@sss.pgh.pa.us 1279 :UBC 0 : return 0;
1280 : :
1281 : : /* copy and zero-terminate the data (even if it's binary) */
4903 tgl@sss.pgh.pa.us 1282 :CBC 18583123 : memcpy(val, columns[i].value, clen);
1283 : 18583123 : val[clen] = '\0';
1284 : :
1285 : 18583123 : tup[i].len = clen;
1286 : 18583123 : tup[i].value = val;
1287 : : }
1288 : : }
1289 : :
1290 : : /* And add the tuple to the PGresult's tuple array */
2930 1291 [ - + ]: 3927825 : if (!pqAddTuple(res, tup, errmsgp))
518 tgl@sss.pgh.pa.us 1292 :UBC 0 : return 0;
1293 : :
1294 : : /*
1295 : : * Success. In partial-result mode, if we have enough rows then make the
1296 : : * result available to the client immediately.
1297 : : */
518 tgl@sss.pgh.pa.us 1298 [ + + + + ]:CBC 3927825 : if (conn->partialResMode && res->ntups >= conn->maxChunkSize)
1636 alvherre@alvh.no-ip. 1299 : 2631 : conn->asyncStatus = PGASYNC_READY_MORE;
1300 : :
4903 tgl@sss.pgh.pa.us 1301 : 3927825 : return 1;
1302 : : }
1303 : :
1304 : :
1305 : : /*
1306 : : * pqAllocCmdQueueEntry
1307 : : * Get a command queue entry for caller to fill.
1308 : : *
1309 : : * If the recycle queue has a free element, that is returned; if not, a
1310 : : * fresh one is allocated. Caller is responsible for adding it to the
1311 : : * command queue (pqAppendCmdQueueEntry) once the struct is filled in, or
1312 : : * releasing the memory (pqRecycleCmdQueueEntry) if an error occurs.
1313 : : *
1314 : : * If allocation fails, sets the error message and returns NULL.
1315 : : */
1316 : : static PGcmdQueueEntry *
1636 alvherre@alvh.no-ip. 1317 : 311957 : pqAllocCmdQueueEntry(PGconn *conn)
1318 : : {
1319 : : PGcmdQueueEntry *entry;
1320 : :
1321 [ + + ]: 311957 : if (conn->cmd_queue_recycle == NULL)
1322 : : {
1323 : 13027 : entry = (PGcmdQueueEntry *) malloc(sizeof(PGcmdQueueEntry));
1324 [ - + ]: 13027 : if (entry == NULL)
1325 : : {
1026 peter@eisentraut.org 1326 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
1636 alvherre@alvh.no-ip. 1327 : 0 : return NULL;
1328 : : }
1329 : : }
1330 : : else
1331 : : {
1636 alvherre@alvh.no-ip. 1332 :CBC 298930 : entry = conn->cmd_queue_recycle;
1333 : 298930 : conn->cmd_queue_recycle = entry->next;
1334 : : }
1335 : 311957 : entry->next = NULL;
1336 : 311957 : entry->query = NULL;
1337 : :
1338 : 311957 : return entry;
1339 : : }
1340 : :
1341 : : /*
1342 : : * pqAppendCmdQueueEntry
1343 : : * Append a caller-allocated entry to the command queue, and update
1344 : : * conn->asyncStatus to account for it.
1345 : : *
1346 : : * The query itself must already have been put in the output buffer by the
1347 : : * caller.
1348 : : */
1349 : : static void
1350 : 311957 : pqAppendCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
1351 : : {
1352 [ - + ]: 311957 : Assert(entry->next == NULL);
1353 : :
1354 [ + + ]: 311957 : if (conn->cmd_queue_head == NULL)
1355 : 309689 : conn->cmd_queue_head = entry;
1356 : : else
1357 : 2268 : conn->cmd_queue_tail->next = entry;
1358 : :
1359 : 311957 : conn->cmd_queue_tail = entry;
1360 : :
1520 1361 [ + + - ]: 311957 : switch (conn->pipelineStatus)
1362 : : {
1363 : 311891 : case PQ_PIPELINE_OFF:
1364 : : case PQ_PIPELINE_ON:
1365 : :
1366 : : /*
1367 : : * When not in pipeline aborted state, if there's a result ready
1368 : : * to be consumed, let it be so (that is, don't change away from
1369 : : * READY or READY_MORE); otherwise set us busy to wait for
1370 : : * something to arrive from the server.
1371 : : */
1372 [ + + ]: 311891 : if (conn->asyncStatus == PGASYNC_IDLE)
1373 : 309679 : conn->asyncStatus = PGASYNC_BUSY;
1374 : 311891 : break;
1375 : :
1376 : 66 : case PQ_PIPELINE_ABORTED:
1377 : :
1378 : : /*
1379 : : * In aborted pipeline state, we don't expect anything from the
1380 : : * server (since we don't send any queries that are queued).
1381 : : * Therefore, if IDLE then do what PQgetResult would do to let
1382 : : * itself consume commands from the queue; if we're in any other
1383 : : * state, we don't have to do anything.
1384 : : */
1159 1385 [ + + ]: 66 : if (conn->asyncStatus == PGASYNC_IDLE ||
1386 [ - + ]: 56 : conn->asyncStatus == PGASYNC_PIPELINE_IDLE)
1520 1387 : 10 : pqPipelineProcessQueue(conn);
1388 : 66 : break;
1389 : : }
1636 1390 : 311957 : }
1391 : :
1392 : : /*
1393 : : * pqRecycleCmdQueueEntry
1394 : : * Push a command queue entry onto the freelist.
1395 : : */
1396 : : static void
1397 : 310858 : pqRecycleCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
1398 : : {
1399 [ - + ]: 310858 : if (entry == NULL)
1636 alvherre@alvh.no-ip. 1400 :UBC 0 : return;
1401 : :
1402 : : /* recyclable entries should not have a follow-on command */
1636 alvherre@alvh.no-ip. 1403 [ - + ]:CBC 310858 : Assert(entry->next == NULL);
1404 : :
1405 [ + + ]: 310858 : if (entry->query)
1406 : : {
1407 : 303411 : free(entry->query);
1408 : 303411 : entry->query = NULL;
1409 : : }
1410 : :
1411 : 310858 : entry->next = conn->cmd_queue_recycle;
1412 : 310858 : conn->cmd_queue_recycle = entry;
1413 : : }
1414 : :
1415 : :
1416 : : /*
1417 : : * PQsendQuery
1418 : : * Submit a query, but don't wait for it to finish
1419 : : *
1420 : : * Returns: 1 if successfully submitted
1421 : : * 0 if error (conn->errorMessage is set)
1422 : : *
1423 : : * PQsendQueryContinue is a non-exported version that behaves identically
1424 : : * except that it doesn't reset conn->errorMessage.
1425 : : */
1426 : : int
9985 bruce@momjian.us 1427 : 300240 : PQsendQuery(PGconn *conn, const char *query)
1428 : : {
1699 tgl@sss.pgh.pa.us 1429 : 300240 : return PQsendQueryInternal(conn, query, true);
1430 : : }
1431 : :
1432 : : int
1699 tgl@sss.pgh.pa.us 1433 :UBC 0 : PQsendQueryContinue(PGconn *conn, const char *query)
1434 : : {
1435 : 0 : return PQsendQueryInternal(conn, query, false);
1436 : : }
1437 : :
1438 : : static int
1699 tgl@sss.pgh.pa.us 1439 :CBC 300240 : PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
1440 : : {
1636 alvherre@alvh.no-ip. 1441 : 300240 : PGcmdQueueEntry *entry = NULL;
1442 : :
1699 tgl@sss.pgh.pa.us 1443 [ - + ]: 300240 : if (!PQsendQueryStart(conn, newQuery))
9985 bruce@momjian.us 1444 :UBC 0 : return 0;
1445 : :
1446 : : /* check the argument */
9985 bruce@momjian.us 1447 [ - + ]:CBC 300240 : if (!query)
1448 : : {
1026 peter@eisentraut.org 1449 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
9890 bruce@momjian.us 1450 : 0 : return 0;
1451 : : }
1452 : :
1159 alvherre@alvh.no-ip. 1453 [ + + ]:CBC 300240 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
1454 : : {
1026 peter@eisentraut.org 1455 : 1 : libpq_append_conn_error(conn, "%s not allowed in pipeline mode",
1456 : : "PQsendQuery");
1079 alvherre@alvh.no-ip. 1457 : 1 : return 0;
1458 : : }
1459 : :
1460 : 300239 : entry = pqAllocCmdQueueEntry(conn);
1461 [ - + ]: 300239 : if (entry == NULL)
1079 alvherre@alvh.no-ip. 1462 :UBC 0 : return 0; /* error msg already set */
1463 : :
1464 : : /* Send the query message(s) */
1465 : : /* construct the outgoing Query message */
746 nathan@postgresql.or 1466 [ + - + - ]:CBC 600478 : if (pqPutMsgStart(PqMsg_Query, conn) < 0 ||
1079 alvherre@alvh.no-ip. 1467 [ - + ]: 600478 : pqPuts(query, conn) < 0 ||
1468 : 300239 : pqPutMsgEnd(conn) < 0)
1469 : : {
1470 : : /* error message should be set up already */
1079 alvherre@alvh.no-ip. 1471 :UBC 0 : pqRecycleCmdQueueEntry(conn, entry);
1472 : 0 : return 0;
1473 : : }
1474 : :
1475 : : /* remember we are using simple query protocol */
1079 alvherre@alvh.no-ip. 1476 :CBC 300239 : entry->queryclass = PGQUERY_SIMPLE;
1477 : : /* and remember the query text too, if possible */
1478 : 300239 : entry->query = strdup(query);
1479 : :
1480 : : /*
1481 : : * Give the data a push. In nonblock mode, don't complain if we're unable
1482 : : * to send it all; PQgetResult() will do any additional flushing needed.
1483 : : */
1484 [ - + ]: 300239 : if (pqFlush(conn) < 0)
1636 alvherre@alvh.no-ip. 1485 :UBC 0 : goto sendFailed;
1486 : :
1487 : : /* OK, it's launched! */
1636 alvherre@alvh.no-ip. 1488 :CBC 300239 : pqAppendCmdQueueEntry(conn, entry);
1489 : :
9985 bruce@momjian.us 1490 : 300239 : return 1;
1491 : :
1636 alvherre@alvh.no-ip. 1492 :UBC 0 : sendFailed:
1493 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1494 : : /* error message should be set up already */
1495 : 0 : return 0;
1496 : : }
1497 : :
1498 : : /*
1499 : : * PQsendQueryParams
1500 : : * Like PQsendQuery, but use extended query protocol so we can pass parameters
1501 : : */
1502 : : int
8113 tgl@sss.pgh.pa.us 1503 :CBC 2878 : PQsendQueryParams(PGconn *conn,
1504 : : const char *command,
1505 : : int nParams,
1506 : : const Oid *paramTypes,
1507 : : const char *const *paramValues,
1508 : : const int *paramLengths,
1509 : : const int *paramFormats,
1510 : : int resultFormat)
1511 : : {
1699 1512 [ - + ]: 2878 : if (!PQsendQueryStart(conn, true))
8060 tgl@sss.pgh.pa.us 1513 :UBC 0 : return 0;
1514 : :
1515 : : /* check the arguments */
8060 tgl@sss.pgh.pa.us 1516 [ - + ]:CBC 2878 : if (!command)
1517 : : {
1026 peter@eisentraut.org 1518 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
8060 tgl@sss.pgh.pa.us 1519 : 0 : return 0;
1520 : : }
1551 tomas.vondra@postgre 1521 [ + - - + ]:CBC 2878 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1522 : : {
1026 peter@eisentraut.org 1523 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1524 : : PQ_QUERY_PARAM_MAX_LIMIT);
4772 heikki.linnakangas@i 1525 : 0 : return 0;
1526 : : }
1527 : :
8060 tgl@sss.pgh.pa.us 1528 :CBC 2878 : return PQsendQueryGuts(conn,
1529 : : command,
1530 : : "", /* use unnamed statement */
1531 : : nParams,
1532 : : paramTypes,
1533 : : paramValues,
1534 : : paramLengths,
1535 : : paramFormats,
1536 : : resultFormat);
1537 : : }
1538 : :
1539 : : /*
1540 : : * PQsendPrepare
1541 : : * Submit a Parse message, but don't wait for it to finish
1542 : : *
1543 : : * Returns: 1 if successfully submitted
1544 : : * 0 if error (conn->errorMessage is set)
1545 : : */
1546 : : int
7628 1547 : 1383 : PQsendPrepare(PGconn *conn,
1548 : : const char *stmtName, const char *query,
1549 : : int nParams, const Oid *paramTypes)
1550 : : {
1636 alvherre@alvh.no-ip. 1551 : 1383 : PGcmdQueueEntry *entry = NULL;
1552 : :
1699 tgl@sss.pgh.pa.us 1553 [ - + ]: 1383 : if (!PQsendQueryStart(conn, true))
7628 tgl@sss.pgh.pa.us 1554 :UBC 0 : return 0;
1555 : :
1556 : : /* check the arguments */
7628 tgl@sss.pgh.pa.us 1557 [ - + ]:CBC 1383 : if (!stmtName)
1558 : : {
1026 peter@eisentraut.org 1559 :UBC 0 : libpq_append_conn_error(conn, "statement name is a null pointer");
7628 tgl@sss.pgh.pa.us 1560 : 0 : return 0;
1561 : : }
7628 tgl@sss.pgh.pa.us 1562 [ - + ]:CBC 1383 : if (!query)
1563 : : {
1026 peter@eisentraut.org 1564 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
7628 tgl@sss.pgh.pa.us 1565 : 0 : return 0;
1566 : : }
1551 tomas.vondra@postgre 1567 [ + - - + ]:CBC 1383 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1568 : : {
1026 peter@eisentraut.org 1569 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1570 : : PQ_QUERY_PARAM_MAX_LIMIT);
4772 heikki.linnakangas@i 1571 : 0 : return 0;
1572 : : }
1573 : :
1636 alvherre@alvh.no-ip. 1574 :CBC 1383 : entry = pqAllocCmdQueueEntry(conn);
1575 [ - + ]: 1383 : if (entry == NULL)
1636 alvherre@alvh.no-ip. 1576 :UBC 0 : return 0; /* error msg already set */
1577 : :
1578 : : /* construct the Parse message */
746 nathan@postgresql.or 1579 [ + - + - ]:CBC 2766 : if (pqPutMsgStart(PqMsg_Parse, conn) < 0 ||
7628 tgl@sss.pgh.pa.us 1580 [ - + ]: 2766 : pqPuts(stmtName, conn) < 0 ||
1581 : 1383 : pqPuts(query, conn) < 0)
7628 tgl@sss.pgh.pa.us 1582 :UBC 0 : goto sendFailed;
1583 : :
7628 tgl@sss.pgh.pa.us 1584 [ + + + + ]:CBC 1383 : if (nParams > 0 && paramTypes)
1585 : 3 : {
1586 : : int i;
1587 : :
1588 [ - + ]: 3 : if (pqPutInt(nParams, 2, conn) < 0)
7628 tgl@sss.pgh.pa.us 1589 :UBC 0 : goto sendFailed;
7628 tgl@sss.pgh.pa.us 1590 [ + + ]:CBC 8 : for (i = 0; i < nParams; i++)
1591 : : {
1592 [ - + ]: 5 : if (pqPutInt(paramTypes[i], 4, conn) < 0)
7628 tgl@sss.pgh.pa.us 1593 :UBC 0 : goto sendFailed;
1594 : : }
1595 : : }
1596 : : else
1597 : : {
7628 tgl@sss.pgh.pa.us 1598 [ - + ]:CBC 1380 : if (pqPutInt(0, 2, conn) < 0)
7628 tgl@sss.pgh.pa.us 1599 :UBC 0 : goto sendFailed;
1600 : : }
7628 tgl@sss.pgh.pa.us 1601 [ - + ]:CBC 1383 : if (pqPutMsgEnd(conn) < 0)
7628 tgl@sss.pgh.pa.us 1602 :UBC 0 : goto sendFailed;
1603 : :
1604 : : /* Add a Sync, unless in pipeline mode. */
1636 alvherre@alvh.no-ip. 1605 [ + + ]:CBC 1383 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
1606 : : {
746 nathan@postgresql.or 1607 [ + - - + ]: 2694 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1636 alvherre@alvh.no-ip. 1608 : 1347 : pqPutMsgEnd(conn) < 0)
1636 alvherre@alvh.no-ip. 1609 :UBC 0 : goto sendFailed;
1610 : : }
1611 : :
1612 : : /* remember we are doing just a Parse */
1636 alvherre@alvh.no-ip. 1613 :CBC 1383 : entry->queryclass = PGQUERY_PREPARE;
1614 : :
1615 : : /* and remember the query text too, if possible */
1616 : : /* if insufficient memory, query just winds up NULL */
1617 : 1383 : entry->query = strdup(query);
1618 : :
1619 : : /*
1620 : : * Give the data a push (in pipeline mode, only if we're past the size
1621 : : * threshold). In nonblock mode, don't complain if we're unable to send
1622 : : * it all; PQgetResult() will do any additional flushing needed.
1623 : : */
1624 [ - + ]: 1383 : if (pqPipelineFlush(conn) < 0)
7628 tgl@sss.pgh.pa.us 1625 :UBC 0 : goto sendFailed;
1626 : :
1627 : : /* OK, it's launched! */
1520 alvherre@alvh.no-ip. 1628 :CBC 1383 : pqAppendCmdQueueEntry(conn, entry);
1629 : :
7628 tgl@sss.pgh.pa.us 1630 : 1383 : return 1;
1631 : :
7628 tgl@sss.pgh.pa.us 1632 :UBC 0 : sendFailed:
1636 alvherre@alvh.no-ip. 1633 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1634 : : /* error message should be set up already */
7628 tgl@sss.pgh.pa.us 1635 : 0 : return 0;
1636 : : }
1637 : :
1638 : : /*
1639 : : * PQsendQueryPrepared
1640 : : * Like PQsendQuery, but execute a previously prepared statement,
1641 : : * using extended query protocol so we can pass parameters
1642 : : */
1643 : : int
8060 tgl@sss.pgh.pa.us 1644 :CBC 7109 : PQsendQueryPrepared(PGconn *conn,
1645 : : const char *stmtName,
1646 : : int nParams,
1647 : : const char *const *paramValues,
1648 : : const int *paramLengths,
1649 : : const int *paramFormats,
1650 : : int resultFormat)
1651 : : {
1699 1652 [ - + ]: 7109 : if (!PQsendQueryStart(conn, true))
8113 tgl@sss.pgh.pa.us 1653 :UBC 0 : return 0;
1654 : :
1655 : : /* check the arguments */
8060 tgl@sss.pgh.pa.us 1656 [ - + ]:CBC 7109 : if (!stmtName)
1657 : : {
1026 peter@eisentraut.org 1658 :UBC 0 : libpq_append_conn_error(conn, "statement name is a null pointer");
8113 tgl@sss.pgh.pa.us 1659 : 0 : return 0;
1660 : : }
1551 tomas.vondra@postgre 1661 [ + - - + ]:CBC 7109 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1662 : : {
1026 peter@eisentraut.org 1663 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1664 : : PQ_QUERY_PARAM_MAX_LIMIT);
4772 heikki.linnakangas@i 1665 : 0 : return 0;
1666 : : }
1667 : :
8060 tgl@sss.pgh.pa.us 1668 :CBC 7109 : return PQsendQueryGuts(conn,
1669 : : NULL, /* no command to parse */
1670 : : stmtName,
1671 : : nParams,
1672 : : NULL, /* no param types */
1673 : : paramValues,
1674 : : paramLengths,
1675 : : paramFormats,
1676 : : resultFormat);
1677 : : }
1678 : :
1679 : : /*
1680 : : * PQsendQueryStart
1681 : : * Common startup code for PQsendQuery and sibling routines
1682 : : */
1683 : : static bool
1699 1684 : 311685 : PQsendQueryStart(PGconn *conn, bool newQuery)
1685 : : {
8060 1686 [ - + ]: 311685 : if (!conn)
8060 tgl@sss.pgh.pa.us 1687 :UBC 0 : return false;
1688 : :
1689 : : /*
1690 : : * If this is the beginning of a query cycle, reset the error state.
1691 : : * However, in pipeline mode with something already queued, the error
1692 : : * buffer belongs to that command and we shouldn't clear it.
1693 : : */
1286 tgl@sss.pgh.pa.us 1694 [ + - + + ]:CBC 311685 : if (newQuery && conn->cmd_queue_head == NULL)
1296 1695 : 309658 : pqClearConnErrorState(conn);
1696 : :
1697 : : /* Don't try to send if we know there's no live connection. */
8060 1698 [ - + ]: 311685 : if (conn->status != CONNECTION_OK)
1699 : : {
1026 peter@eisentraut.org 1700 :UBC 0 : libpq_append_conn_error(conn, "no connection to the server");
8060 tgl@sss.pgh.pa.us 1701 : 0 : return false;
1702 : : }
1703 : :
1704 : : /* Can't send while already busy, either, unless enqueuing for later */
1636 alvherre@alvh.no-ip. 1705 [ + + ]:CBC 311685 : if (conn->asyncStatus != PGASYNC_IDLE &&
1706 [ - + ]: 2027 : conn->pipelineStatus == PQ_PIPELINE_OFF)
1707 : : {
1026 peter@eisentraut.org 1708 :UBC 0 : libpq_append_conn_error(conn, "another command is already in progress");
8060 tgl@sss.pgh.pa.us 1709 : 0 : return false;
1710 : : }
1711 : :
1636 alvherre@alvh.no-ip. 1712 [ + + ]:CBC 311685 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
1713 : : {
1714 : : /*
1715 : : * When enqueuing commands we don't change much of the connection
1716 : : * state since it's already in use for the current command. The
1717 : : * connection state will get updated when pqPipelineProcessQueue()
1718 : : * advances to start processing the queued message.
1719 : : *
1720 : : * Just make sure we can safely enqueue given the current connection
1721 : : * state. We can enqueue behind another queue item, or behind a
1722 : : * non-queue command (one that sends its own sync), but we can't
1723 : : * enqueue if the connection is in a copy state.
1724 : : */
1725 [ + - - ]: 2247 : switch (conn->asyncStatus)
1726 : : {
1727 : 2247 : case PGASYNC_IDLE:
1728 : : case PGASYNC_PIPELINE_IDLE:
1729 : : case PGASYNC_READY:
1730 : : case PGASYNC_READY_MORE:
1731 : : case PGASYNC_BUSY:
1732 : : /* ok to queue */
1733 : 2247 : break;
1734 : :
1636 alvherre@alvh.no-ip. 1735 :UBC 0 : case PGASYNC_COPY_IN:
1736 : : case PGASYNC_COPY_OUT:
1737 : : case PGASYNC_COPY_BOTH:
1026 peter@eisentraut.org 1738 : 0 : libpq_append_conn_error(conn, "cannot queue commands during COPY");
1636 alvherre@alvh.no-ip. 1739 : 0 : return false;
1740 : : }
1741 : : }
1742 : : else
1743 : : {
1744 : : /*
1745 : : * This command's results will come in immediately. Initialize async
1746 : : * result-accumulation state
1747 : : */
1636 alvherre@alvh.no-ip. 1748 :CBC 309438 : pqClearAsyncResult(conn);
1749 : :
1750 : : /* reset partial-result mode */
518 tgl@sss.pgh.pa.us 1751 : 309438 : conn->partialResMode = false;
1636 alvherre@alvh.no-ip. 1752 : 309438 : conn->singleRowMode = false;
518 tgl@sss.pgh.pa.us 1753 : 309438 : conn->maxChunkSize = 0;
1754 : : }
1755 : :
1756 : : /* ready to send command message */
8060 1757 : 311685 : return true;
1758 : : }
1759 : :
1760 : : /*
1761 : : * PQsendQueryGuts
1762 : : * Common code for sending a query with extended query protocol
1763 : : * PQsendQueryStart should be done already
1764 : : *
1765 : : * command may be NULL to indicate we use an already-prepared statement
1766 : : */
1767 : : static int
1768 : 9987 : PQsendQueryGuts(PGconn *conn,
1769 : : const char *command,
1770 : : const char *stmtName,
1771 : : int nParams,
1772 : : const Oid *paramTypes,
1773 : : const char *const *paramValues,
1774 : : const int *paramLengths,
1775 : : const int *paramFormats,
1776 : : int resultFormat)
1777 : : {
1778 : : int i;
1779 : : PGcmdQueueEntry *entry;
1780 : :
1636 alvherre@alvh.no-ip. 1781 : 9987 : entry = pqAllocCmdQueueEntry(conn);
1782 [ - + ]: 9987 : if (entry == NULL)
1636 alvherre@alvh.no-ip. 1783 :UBC 0 : return 0; /* error msg already set */
1784 : :
1785 : : /*
1786 : : * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync
1787 : : * (if not in pipeline mode), using specified statement name and the
1788 : : * unnamed portal.
1789 : : */
1790 : :
8060 tgl@sss.pgh.pa.us 1791 [ + + ]:CBC 9987 : if (command)
1792 : : {
1793 : : /* construct the Parse message */
746 nathan@postgresql.or 1794 [ + - + - ]: 5756 : if (pqPutMsgStart(PqMsg_Parse, conn) < 0 ||
8060 tgl@sss.pgh.pa.us 1795 [ - + ]: 5756 : pqPuts(stmtName, conn) < 0 ||
1796 : 2878 : pqPuts(command, conn) < 0)
8113 tgl@sss.pgh.pa.us 1797 :UBC 0 : goto sendFailed;
8060 tgl@sss.pgh.pa.us 1798 [ + + + + ]:CBC 2878 : if (nParams > 0 && paramTypes)
1799 : : {
1800 [ - + ]: 45 : if (pqPutInt(nParams, 2, conn) < 0)
8113 tgl@sss.pgh.pa.us 1801 :UBC 0 : goto sendFailed;
8060 tgl@sss.pgh.pa.us 1802 [ + + ]:CBC 115 : for (i = 0; i < nParams; i++)
1803 : : {
1804 [ - + ]: 70 : if (pqPutInt(paramTypes[i], 4, conn) < 0)
8060 tgl@sss.pgh.pa.us 1805 :UBC 0 : goto sendFailed;
1806 : : }
1807 : : }
1808 : : else
1809 : : {
8060 tgl@sss.pgh.pa.us 1810 [ - + ]:CBC 2833 : if (pqPutInt(0, 2, conn) < 0)
8060 tgl@sss.pgh.pa.us 1811 :UBC 0 : goto sendFailed;
1812 : : }
8060 tgl@sss.pgh.pa.us 1813 [ - + ]:CBC 2878 : if (pqPutMsgEnd(conn) < 0)
8113 tgl@sss.pgh.pa.us 1814 :UBC 0 : goto sendFailed;
1815 : : }
1816 : :
1817 : : /* Construct the Bind message */
746 nathan@postgresql.or 1818 [ + - + - ]:CBC 19974 : if (pqPutMsgStart(PqMsg_Bind, conn) < 0 ||
8113 tgl@sss.pgh.pa.us 1819 [ - + ]: 19974 : pqPuts("", conn) < 0 ||
8060 1820 : 9987 : pqPuts(stmtName, conn) < 0)
8113 tgl@sss.pgh.pa.us 1821 :UBC 0 : goto sendFailed;
1822 : :
1823 : : /* Send parameter formats */
8113 tgl@sss.pgh.pa.us 1824 [ + + + + ]:CBC 9987 : if (nParams > 0 && paramFormats)
1825 : : {
1826 [ - + ]: 1277 : if (pqPutInt(nParams, 2, conn) < 0)
8113 tgl@sss.pgh.pa.us 1827 :UBC 0 : goto sendFailed;
8113 tgl@sss.pgh.pa.us 1828 [ + + ]:CBC 3009 : for (i = 0; i < nParams; i++)
1829 : : {
1830 [ - + ]: 1732 : if (pqPutInt(paramFormats[i], 2, conn) < 0)
8113 tgl@sss.pgh.pa.us 1831 :UBC 0 : goto sendFailed;
1832 : : }
1833 : : }
1834 : : else
1835 : : {
8113 tgl@sss.pgh.pa.us 1836 [ - + ]:CBC 8710 : if (pqPutInt(0, 2, conn) < 0)
8113 tgl@sss.pgh.pa.us 1837 :UBC 0 : goto sendFailed;
1838 : : }
1839 : :
8113 tgl@sss.pgh.pa.us 1840 [ - + ]:CBC 9987 : if (pqPutInt(nParams, 2, conn) < 0)
8113 tgl@sss.pgh.pa.us 1841 :UBC 0 : goto sendFailed;
1842 : :
1843 : : /* Send parameters */
8113 tgl@sss.pgh.pa.us 1844 [ + + ]:CBC 23103 : for (i = 0; i < nParams; i++)
1845 : : {
1846 [ + - + + ]: 13116 : if (paramValues && paramValues[i])
1847 : 12519 : {
1848 : : int nbytes;
1849 : :
1850 [ + + + + ]: 12519 : if (paramFormats && paramFormats[i] != 0)
1851 : : {
1852 : : /* binary parameter */
7394 1853 [ + - ]: 13 : if (paramLengths)
1854 : 13 : nbytes = paramLengths[i];
1855 : : else
1856 : : {
1026 peter@eisentraut.org 1857 :UBC 0 : libpq_append_conn_error(conn, "length must be given for binary parameter");
7394 tgl@sss.pgh.pa.us 1858 : 0 : goto sendFailed;
1859 : : }
1860 : : }
1861 : : else
1862 : : {
1863 : : /* text parameter, do not use paramLengths */
8113 tgl@sss.pgh.pa.us 1864 :CBC 12506 : nbytes = strlen(paramValues[i]);
1865 : : }
1866 [ + - - + ]: 25038 : if (pqPutInt(nbytes, 4, conn) < 0 ||
1867 : 12519 : pqPutnchar(paramValues[i], nbytes, conn) < 0)
8113 tgl@sss.pgh.pa.us 1868 :UBC 0 : goto sendFailed;
1869 : : }
1870 : : else
1871 : : {
1872 : : /* take the param as NULL */
8113 tgl@sss.pgh.pa.us 1873 [ - + ]:CBC 597 : if (pqPutInt(-1, 4, conn) < 0)
8113 tgl@sss.pgh.pa.us 1874 :UBC 0 : goto sendFailed;
1875 : : }
1876 : : }
8113 tgl@sss.pgh.pa.us 1877 [ + - - + ]:CBC 19974 : if (pqPutInt(1, 2, conn) < 0 ||
1878 : 9987 : pqPutInt(resultFormat, 2, conn))
8113 tgl@sss.pgh.pa.us 1879 :UBC 0 : goto sendFailed;
8113 tgl@sss.pgh.pa.us 1880 [ - + ]:CBC 9987 : if (pqPutMsgEnd(conn) < 0)
8113 tgl@sss.pgh.pa.us 1881 :UBC 0 : goto sendFailed;
1882 : :
1883 : : /* construct the Describe Portal message */
746 nathan@postgresql.or 1884 [ + - + - ]:CBC 19974 : if (pqPutMsgStart(PqMsg_Describe, conn) < 0 ||
8113 tgl@sss.pgh.pa.us 1885 [ + - ]: 19974 : pqPutc('P', conn) < 0 ||
1886 [ - + ]: 19974 : pqPuts("", conn) < 0 ||
1887 : 9987 : pqPutMsgEnd(conn) < 0)
8113 tgl@sss.pgh.pa.us 1888 :UBC 0 : goto sendFailed;
1889 : :
1890 : : /* construct the Execute message */
746 nathan@postgresql.or 1891 [ + - + - ]:CBC 19974 : if (pqPutMsgStart(PqMsg_Execute, conn) < 0 ||
8113 tgl@sss.pgh.pa.us 1892 [ + - ]: 19974 : pqPuts("", conn) < 0 ||
1893 [ - + ]: 19974 : pqPutInt(0, 4, conn) < 0 ||
1894 : 9987 : pqPutMsgEnd(conn) < 0)
8113 tgl@sss.pgh.pa.us 1895 :UBC 0 : goto sendFailed;
1896 : :
1897 : : /* construct the Sync message if not in pipeline mode */
1636 alvherre@alvh.no-ip. 1898 [ + + ]:CBC 9987 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
1899 : : {
746 nathan@postgresql.or 1900 [ + - - + ]: 15580 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1636 alvherre@alvh.no-ip. 1901 : 7790 : pqPutMsgEnd(conn) < 0)
1636 alvherre@alvh.no-ip. 1902 :UBC 0 : goto sendFailed;
1903 : : }
1904 : :
1905 : : /* remember we are using extended query protocol */
1636 alvherre@alvh.no-ip. 1906 :CBC 9987 : entry->queryclass = PGQUERY_EXTENDED;
1907 : :
1908 : : /* and remember the query text too, if possible */
1909 : : /* if insufficient memory, query just winds up NULL */
7116 tgl@sss.pgh.pa.us 1910 [ + + ]: 9987 : if (command)
1636 alvherre@alvh.no-ip. 1911 : 2878 : entry->query = strdup(command);
1912 : :
1913 : : /*
1914 : : * Give the data a push (in pipeline mode, only if we're past the size
1915 : : * threshold). In nonblock mode, don't complain if we're unable to send
1916 : : * it all; PQgetResult() will do any additional flushing needed.
1917 : : */
1918 [ - + ]: 9987 : if (pqPipelineFlush(conn) < 0)
8113 tgl@sss.pgh.pa.us 1919 :UBC 0 : goto sendFailed;
1920 : :
1921 : : /* OK, it's launched! */
1636 alvherre@alvh.no-ip. 1922 :CBC 9987 : pqAppendCmdQueueEntry(conn, entry);
1923 : :
8113 tgl@sss.pgh.pa.us 1924 : 9987 : return 1;
1925 : :
8113 tgl@sss.pgh.pa.us 1926 :UBC 0 : sendFailed:
1636 alvherre@alvh.no-ip. 1927 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1928 : : /* error message should be set up already */
8113 tgl@sss.pgh.pa.us 1929 : 0 : return 0;
1930 : : }
1931 : :
1932 : : /*
1933 : : * Is it OK to change partial-result mode now?
1934 : : */
1935 : : static bool
518 tgl@sss.pgh.pa.us 1936 :CBC 88 : canChangeResultMode(PGconn *conn)
1937 : : {
1938 : : /*
1939 : : * Only allow changing the mode when we have launched a query and not yet
1940 : : * received any results.
1941 : : */
4783 1942 [ - + ]: 88 : if (!conn)
518 tgl@sss.pgh.pa.us 1943 :UBC 0 : return false;
4783 tgl@sss.pgh.pa.us 1944 [ + + ]:CBC 88 : if (conn->asyncStatus != PGASYNC_BUSY)
518 1945 : 3 : return false;
1636 alvherre@alvh.no-ip. 1946 [ + - ]: 85 : if (!conn->cmd_queue_head ||
1947 [ + + ]: 85 : (conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE &&
1948 [ - + ]: 20 : conn->cmd_queue_head->queryclass != PGQUERY_EXTENDED))
518 tgl@sss.pgh.pa.us 1949 :UBC 0 : return false;
1234 tgl@sss.pgh.pa.us 1950 [ + - - + ]:CBC 85 : if (pgHavePendingResult(conn))
518 tgl@sss.pgh.pa.us 1951 :UBC 0 : return false;
518 tgl@sss.pgh.pa.us 1952 :CBC 85 : return true;
1953 : : }
1954 : :
1955 : : /*
1956 : : * Select row-by-row processing mode
1957 : : */
1958 : : int
1959 : 35 : PQsetSingleRowMode(PGconn *conn)
1960 : : {
1961 [ + - ]: 35 : if (canChangeResultMode(conn))
1962 : : {
1963 : 35 : conn->partialResMode = true;
1964 : 35 : conn->singleRowMode = true;
1965 : 35 : conn->maxChunkSize = 1;
1966 : 35 : return 1;
1967 : : }
1968 : : else
4783 tgl@sss.pgh.pa.us 1969 :UBC 0 : return 0;
1970 : : }
1971 : :
1972 : : /*
1973 : : * Select chunked results processing mode
1974 : : */
1975 : : int
518 tgl@sss.pgh.pa.us 1976 :CBC 53 : PQsetChunkedRowsMode(PGconn *conn, int chunkSize)
1977 : : {
1978 [ + - + + ]: 53 : if (chunkSize > 0 && canChangeResultMode(conn))
1979 : : {
1980 : 50 : conn->partialResMode = true;
1981 : 50 : conn->singleRowMode = false;
1982 : 50 : conn->maxChunkSize = chunkSize;
1983 : 50 : return 1;
1984 : : }
1985 : : else
1986 : 3 : return 0;
1987 : : }
1988 : :
1989 : : /*
1990 : : * Consume any available input from the backend
1991 : : * 0 return: some kind of trouble
1992 : : * 1 return: no problem
1993 : : */
1994 : : int
9985 bruce@momjian.us 1995 : 460088 : PQconsumeInput(PGconn *conn)
1996 : : {
1997 [ - + ]: 460088 : if (!conn)
9865 bruce@momjian.us 1998 :UBC 0 : return 0;
1999 : :
2000 : : /*
2001 : : * for non-blocking connections try to flush the send-queue, otherwise we
2002 : : * may never get a response for something that may not have already been
2003 : : * sent because it's in our write buffer!
2004 : : */
8176 tgl@sss.pgh.pa.us 2005 [ + + ]:CBC 460088 : if (pqIsnonblocking(conn))
2006 : : {
2007 [ - + ]: 7 : if (pqFlush(conn) < 0)
8176 tgl@sss.pgh.pa.us 2008 :UBC 0 : return 0;
2009 : : }
2010 : :
2011 : : /*
2012 : : * Load more data, if available. We do this no matter what state we are
2013 : : * in, since we are probably getting called because the application wants
2014 : : * to get rid of a read-select condition. Note that we will NOT block
2015 : : * waiting for more input.
2016 : : */
9837 tgl@sss.pgh.pa.us 2017 [ + + ]:CBC 460088 : if (pqReadData(conn) < 0)
9865 bruce@momjian.us 2018 : 80 : return 0;
2019 : :
2020 : : /* Parsing of the data waits till later. */
2021 : 460008 : return 1;
2022 : : }
2023 : :
2024 : :
2025 : : /*
2026 : : * parseInput: if appropriate, parse input data from backend
2027 : : * until input is exhausted or a stopping state is reached.
2028 : : * Note that this function will NOT attempt to read more data from the backend.
2029 : : */
2030 : : static void
9985 2031 : 1740013 : parseInput(PGconn *conn)
2032 : : {
1647 heikki.linnakangas@i 2033 : 1740013 : pqParseInput3(conn);
10651 scrappy@hub.org 2034 : 1740013 : }
2035 : :
2036 : : /*
2037 : : * PQisBusy
2038 : : * Return true if PQgetResult would block waiting for input.
2039 : : */
2040 : :
2041 : : int
9985 bruce@momjian.us 2042 : 145903 : PQisBusy(PGconn *conn)
2043 : : {
10226 2044 [ - + ]: 145903 : if (!conn)
2943 peter_e@gmx.net 2045 :UBC 0 : return false;
2046 : :
2047 : : /* Parse any available data, if our state permits. */
9985 bruce@momjian.us 2048 :CBC 145903 : parseInput(conn);
2049 : :
2050 : : /*
2051 : : * PQgetResult will return immediately in all states except BUSY. Also,
2052 : : * if we've detected read EOF and dropped the connection, we can expect
2053 : : * that PQgetResult will fail immediately. Note that we do *not* check
2054 : : * conn->write_failed here --- once that's become set, we know we have
2055 : : * trouble, but we need to keep trying to read until we have a complete
2056 : : * server message or detect read EOF.
2057 : : */
1302 tgl@sss.pgh.pa.us 2058 [ + + + - ]: 145903 : return conn->asyncStatus == PGASYNC_BUSY && conn->status != CONNECTION_BAD;
2059 : : }
2060 : :
2061 : : /*
2062 : : * PQgetResult
2063 : : * Get the next PGresult produced by a query. Returns NULL if no
2064 : : * query work remains or an error has occurred (e.g. out of
2065 : : * memory).
2066 : : *
2067 : : * In pipeline mode, once all the result of a query have been returned,
2068 : : * PQgetResult returns NULL to let the user know that the next
2069 : : * query is being processed. At the end of the pipeline, returns a
2070 : : * result with PQresultStatus(result) == PGRES_PIPELINE_SYNC.
2071 : : */
2072 : : PGresult *
9985 bruce@momjian.us 2073 : 752534 : PQgetResult(PGconn *conn)
2074 : : {
2075 : : PGresult *res;
2076 : :
2077 [ - + ]: 752534 : if (!conn)
9985 bruce@momjian.us 2078 :UBC 0 : return NULL;
2079 : :
2080 : : /* Parse any available data, if our state permits. */
9985 bruce@momjian.us 2081 :CBC 752534 : parseInput(conn);
2082 : :
2083 : : /* If not ready to return something, block until we are. */
2084 [ + + ]: 1057673 : while (conn->asyncStatus == PGASYNC_BUSY)
2085 : : {
2086 : : int flushResult;
2087 : :
2088 : : /*
2089 : : * If data remains unsent, send it. Else we might be waiting for the
2090 : : * result of a command the backend hasn't even got yet.
2091 : : */
8113 tgl@sss.pgh.pa.us 2092 [ - + ]: 305186 : while ((flushResult = pqFlush(conn)) > 0)
2093 : : {
2943 peter_e@gmx.net 2094 [ # # ]:UBC 0 : if (pqWait(false, true, conn))
2095 : : {
8113 tgl@sss.pgh.pa.us 2096 : 0 : flushResult = -1;
2097 : 0 : break;
2098 : : }
2099 : : }
2100 : :
2101 : : /*
2102 : : * Wait for some more data, and load it. (Note: if the connection has
2103 : : * been lost, pqWait should return immediately because the socket
2104 : : * should be read-ready, either with the last server data or with an
2105 : : * EOF indication. We expect therefore that this won't result in any
2106 : : * undue delay in reporting a previous write failure.)
2107 : : */
8113 tgl@sss.pgh.pa.us 2108 [ + - + + ]:CBC 610372 : if (flushResult ||
2943 peter_e@gmx.net 2109 [ + + ]: 610338 : pqWait(true, false, conn) ||
9985 bruce@momjian.us 2110 : 305152 : pqReadData(conn) < 0)
2111 : : {
2112 : : /* Report the error saved by pqWait or pqReadData */
8126 tgl@sss.pgh.pa.us 2113 : 44 : pqSaveErrorResult(conn);
9985 bruce@momjian.us 2114 : 44 : conn->asyncStatus = PGASYNC_IDLE;
8126 tgl@sss.pgh.pa.us 2115 : 44 : return pqPrepareAsyncResult(conn);
2116 : : }
2117 : :
2118 : : /* Parse it. */
9985 bruce@momjian.us 2119 : 305142 : parseInput(conn);
2120 : :
2121 : : /*
2122 : : * If we had a write error, but nothing above obtained a query result
2123 : : * or detected a read error, report the write error.
2124 : : */
2363 tgl@sss.pgh.pa.us 2125 [ + + + - ]: 305142 : if (conn->write_failed && conn->asyncStatus == PGASYNC_BUSY)
2126 : : {
2127 : 3 : pqSaveWriteError(conn);
2128 : 3 : conn->asyncStatus = PGASYNC_IDLE;
2129 : 3 : return pqPrepareAsyncResult(conn);
2130 : : }
2131 : : }
2132 : :
2133 : : /* Return the appropriate thing. */
9985 bruce@momjian.us 2134 [ + + + + : 752487 : switch (conn->asyncStatus)
+ + + - ]
2135 : : {
2136 : 410877 : case PGASYNC_IDLE:
2137 : 410877 : res = NULL; /* query is complete */
2138 : 410877 : break;
1159 alvherre@alvh.no-ip. 2139 : 2236 : case PGASYNC_PIPELINE_IDLE:
2140 [ - + ]: 2236 : Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
2141 : :
2142 : : /*
2143 : : * We're about to return the NULL that terminates the round of
2144 : : * results from the current query; prepare to send the results of
2145 : : * the next query, if any, when we're called next. If there's no
2146 : : * next element in the command queue, this gets us in IDLE state.
2147 : : */
2148 : 2236 : pqPipelineProcessQueue(conn);
2149 : 2236 : res = NULL; /* query is complete */
2150 : 2236 : break;
2151 : :
9985 bruce@momjian.us 2152 : 330350 : case PGASYNC_READY:
1636 alvherre@alvh.no-ip. 2153 : 330350 : res = pqPrepareAsyncResult(conn);
2154 : :
2155 : : /*
2156 : : * Normally pqPrepareAsyncResult will have left conn->result
2157 : : * empty. Otherwise, "res" must be a not-full PGRES_TUPLES_CHUNK
2158 : : * result, which we want to return to the caller while staying in
2159 : : * PGASYNC_READY state. Then the next call here will return the
2160 : : * empty PGRES_TUPLES_OK result that was restored from
2161 : : * saved_result, after which we can proceed.
2162 : : */
518 tgl@sss.pgh.pa.us 2163 [ + + ]: 330350 : if (conn->result)
2164 : : {
2165 [ - + ]: 10 : Assert(res->resultStatus == PGRES_TUPLES_CHUNK);
2166 : 10 : break;
2167 : : }
2168 : :
2169 : : /* Advance the queue as appropriate */
641 alvherre@alvh.no-ip. 2170 : 330340 : pqCommandQueueAdvance(conn, false,
2171 : 330340 : res->resultStatus == PGRES_PIPELINE_SYNC);
2172 : :
1636 2173 [ + + ]: 330340 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
2174 : : {
2175 : : /*
2176 : : * We're about to send the results of the current query. Set
2177 : : * us idle now, and ...
2178 : : */
1159 2179 : 2500 : conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
2180 : :
2181 : : /*
2182 : : * ... in cases when we're sending a pipeline-sync result,
2183 : : * move queue processing forwards immediately, so that next
2184 : : * time we're called, we're prepared to return the next result
2185 : : * received from the server. In all other cases, leave the
2186 : : * queue state change for next time, so that a terminating
2187 : : * NULL result is sent.
2188 : : *
2189 : : * (In other words: we don't return a NULL after a pipeline
2190 : : * sync.)
2191 : : */
599 2192 [ + + ]: 2500 : if (res->resultStatus == PGRES_PIPELINE_SYNC)
1636 2193 : 263 : pqPipelineProcessQueue(conn);
2194 : : }
2195 : : else
2196 : : {
2197 : : /* Set the state back to BUSY, allowing parsing to proceed. */
2198 : 327840 : conn->asyncStatus = PGASYNC_BUSY;
2199 : : }
2200 : 330340 : break;
2201 : 2631 : case PGASYNC_READY_MORE:
8126 tgl@sss.pgh.pa.us 2202 : 2631 : res = pqPrepareAsyncResult(conn);
2203 : : /* Set the state back to BUSY, allowing parsing to proceed. */
9985 bruce@momjian.us 2204 : 2631 : conn->asyncStatus = PGASYNC_BUSY;
2205 : 2631 : break;
2206 : 575 : case PGASYNC_COPY_IN:
4224 tgl@sss.pgh.pa.us 2207 : 575 : res = getCopyResult(conn, PGRES_COPY_IN);
9985 bruce@momjian.us 2208 : 575 : break;
2209 : 5148 : case PGASYNC_COPY_OUT:
4224 tgl@sss.pgh.pa.us 2210 : 5148 : res = getCopyResult(conn, PGRES_COPY_OUT);
9985 bruce@momjian.us 2211 : 5148 : break;
5383 rhaas@postgresql.org 2212 : 670 : case PGASYNC_COPY_BOTH:
4224 tgl@sss.pgh.pa.us 2213 : 670 : res = getCopyResult(conn, PGRES_COPY_BOTH);
5383 rhaas@postgresql.org 2214 : 670 : break;
9985 bruce@momjian.us 2215 :UBC 0 : default:
1026 peter@eisentraut.org 2216 : 0 : libpq_append_conn_error(conn, "unexpected asyncStatus: %d", (int) conn->asyncStatus);
1296 tgl@sss.pgh.pa.us 2217 : 0 : pqSaveErrorResult(conn);
2218 : 0 : conn->asyncStatus = PGASYNC_IDLE; /* try to restore valid state */
2219 : 0 : res = pqPrepareAsyncResult(conn);
9985 bruce@momjian.us 2220 : 0 : break;
2221 : : }
2222 : :
2223 : : /* Time to fire PGEVT_RESULTCREATE events, if there are any */
1296 tgl@sss.pgh.pa.us 2224 [ + + - + ]:CBC 752487 : if (res && res->nEvents > 0)
1296 tgl@sss.pgh.pa.us 2225 :UBC 0 : (void) PQfireResultCreateEvents(conn, res);
2226 : :
9985 bruce@momjian.us 2227 :CBC 752487 : return res;
2228 : : }
2229 : :
2230 : : /*
2231 : : * getCopyResult
2232 : : * Helper for PQgetResult: generate result for COPY-in-progress cases
2233 : : */
2234 : : static PGresult *
4224 tgl@sss.pgh.pa.us 2235 : 6393 : getCopyResult(PGconn *conn, ExecStatusType copytype)
2236 : : {
2237 : : /*
2238 : : * If the server connection has been lost, don't pretend everything is
2239 : : * hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the
2240 : : * asyncStatus to idle (corresponding to what we'd do if we'd detected I/O
2241 : : * error in the earlier steps in PQgetResult). The text returned in the
2242 : : * result is whatever is in conn->errorMessage; we hope that was filled
2243 : : * with something relevant when the lost connection was detected.
2244 : : */
2245 [ - + ]: 6393 : if (conn->status != CONNECTION_OK)
2246 : : {
4224 tgl@sss.pgh.pa.us 2247 :UBC 0 : pqSaveErrorResult(conn);
2248 : 0 : conn->asyncStatus = PGASYNC_IDLE;
2249 : 0 : return pqPrepareAsyncResult(conn);
2250 : : }
2251 : :
2252 : : /* If we have an async result for the COPY, return that */
4224 tgl@sss.pgh.pa.us 2253 [ + + + - ]:CBC 6393 : if (conn->result && conn->result->resultStatus == copytype)
2254 : 6192 : return pqPrepareAsyncResult(conn);
2255 : :
2256 : : /* Otherwise, invent a suitable PGresult */
2257 : 201 : return PQmakeEmptyPGresult(conn, copytype);
2258 : : }
2259 : :
2260 : :
2261 : : /*
2262 : : * PQexec
2263 : : * send a query to the backend and package up the result in a PGresult
2264 : : *
2265 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2266 : : * a relevant message.
2267 : : * If the query was sent, a new PGresult is returned (which could indicate
2268 : : * either success or failure).
2269 : : * The user is responsible for freeing the PGresult via PQclear()
2270 : : * when done with it.
2271 : : */
2272 : : PGresult *
9985 bruce@momjian.us 2273 : 85096 : PQexec(PGconn *conn, const char *query)
2274 : : {
8113 tgl@sss.pgh.pa.us 2275 [ + + ]: 85096 : if (!PQexecStart(conn))
2276 : 1 : return NULL;
2277 [ - + ]: 85095 : if (!PQsendQuery(conn, query))
8113 tgl@sss.pgh.pa.us 2278 :UBC 0 : return NULL;
8113 tgl@sss.pgh.pa.us 2279 :CBC 85095 : return PQexecFinish(conn);
2280 : : }
2281 : :
2282 : : /*
2283 : : * PQexecParams
2284 : : * Like PQexec, but use extended query protocol so we can pass parameters
2285 : : */
2286 : : PGresult *
2287 : 983 : PQexecParams(PGconn *conn,
2288 : : const char *command,
2289 : : int nParams,
2290 : : const Oid *paramTypes,
2291 : : const char *const *paramValues,
2292 : : const int *paramLengths,
2293 : : const int *paramFormats,
2294 : : int resultFormat)
2295 : : {
2296 [ - + ]: 983 : if (!PQexecStart(conn))
8113 tgl@sss.pgh.pa.us 2297 :UBC 0 : return NULL;
8113 tgl@sss.pgh.pa.us 2298 [ - + ]:CBC 983 : if (!PQsendQueryParams(conn, command,
2299 : : nParams, paramTypes, paramValues, paramLengths,
2300 : : paramFormats, resultFormat))
9363 bruce@momjian.us 2301 :UBC 0 : return NULL;
8113 tgl@sss.pgh.pa.us 2302 :CBC 983 : return PQexecFinish(conn);
2303 : : }
2304 : :
2305 : : /*
2306 : : * PQprepare
2307 : : * Creates a prepared statement by issuing a Parse message.
2308 : : *
2309 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2310 : : * a relevant message.
2311 : : * If the query was sent, a new PGresult is returned (which could indicate
2312 : : * either success or failure).
2313 : : * The user is responsible for freeing the PGresult via PQclear()
2314 : : * when done with it.
2315 : : */
2316 : : PGresult *
7628 2317 : 1153 : PQprepare(PGconn *conn,
2318 : : const char *stmtName, const char *query,
2319 : : int nParams, const Oid *paramTypes)
2320 : : {
2321 [ + + ]: 1153 : if (!PQexecStart(conn))
2322 : 9 : return NULL;
2323 [ - + ]: 1144 : if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
7628 tgl@sss.pgh.pa.us 2324 :UBC 0 : return NULL;
7628 tgl@sss.pgh.pa.us 2325 :CBC 1144 : return PQexecFinish(conn);
2326 : : }
2327 : :
2328 : : /*
2329 : : * PQexecPrepared
2330 : : * Like PQexec, but execute a previously prepared statement,
2331 : : * using extended query protocol so we can pass parameters
2332 : : */
2333 : : PGresult *
8060 2334 : 2752 : PQexecPrepared(PGconn *conn,
2335 : : const char *stmtName,
2336 : : int nParams,
2337 : : const char *const *paramValues,
2338 : : const int *paramLengths,
2339 : : const int *paramFormats,
2340 : : int resultFormat)
2341 : : {
2342 [ - + ]: 2752 : if (!PQexecStart(conn))
8060 tgl@sss.pgh.pa.us 2343 :UBC 0 : return NULL;
8060 tgl@sss.pgh.pa.us 2344 [ - + ]:CBC 2752 : if (!PQsendQueryPrepared(conn, stmtName,
2345 : : nParams, paramValues, paramLengths,
2346 : : paramFormats, resultFormat))
8060 tgl@sss.pgh.pa.us 2347 :UBC 0 : return NULL;
8060 tgl@sss.pgh.pa.us 2348 :CBC 2752 : return PQexecFinish(conn);
2349 : : }
2350 : :
2351 : : /*
2352 : : * Common code for PQexec and sibling routines: prepare to send command
2353 : : */
2354 : : static bool
8113 2355 : 90036 : PQexecStart(PGconn *conn)
2356 : : {
2357 : : PGresult *result;
2358 : :
2359 [ - + ]: 90036 : if (!conn)
8113 tgl@sss.pgh.pa.us 2360 :UBC 0 : return false;
2361 : :
2362 : : /*
2363 : : * Since this is the beginning of a query cycle, reset the error state.
2364 : : * However, in pipeline mode with something already queued, the error
2365 : : * buffer belongs to that command and we shouldn't clear it.
2366 : : */
1286 tgl@sss.pgh.pa.us 2367 [ + + ]:CBC 90036 : if (conn->cmd_queue_head == NULL)
2368 : 90023 : pqClearConnErrorState(conn);
2369 : :
1636 alvherre@alvh.no-ip. 2370 [ + + ]: 90036 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
2371 : : {
1026 peter@eisentraut.org 2372 : 10 : libpq_append_conn_error(conn, "synchronous command execution functions are not allowed in pipeline mode");
1636 alvherre@alvh.no-ip. 2373 : 10 : return false;
2374 : : }
2375 : :
2376 : : /*
2377 : : * Silently discard any prior query result that application didn't eat.
2378 : : * This is probably poor design, but it's here for backward compatibility.
2379 : : */
4783 tgl@sss.pgh.pa.us 2380 [ - + ]: 90026 : while ((result = PQgetResult(conn)) != NULL)
2381 : : {
8060 tgl@sss.pgh.pa.us 2382 :UBC 0 : ExecStatusType resultStatus = result->resultStatus;
2383 : :
2384 : 0 : PQclear(result); /* only need its status */
2385 [ # # ]: 0 : if (resultStatus == PGRES_COPY_IN)
2386 : : {
2387 : : /* get out of a COPY IN state */
1647 heikki.linnakangas@i 2388 [ # # ]: 0 : if (PQputCopyEnd(conn,
2389 : 0 : libpq_gettext("COPY terminated by new PQexec")) < 0)
8106 tgl@sss.pgh.pa.us 2390 : 0 : return false;
2391 : : /* keep waiting to swallow the copy's failure message */
2392 : : }
8060 2393 [ # # ]: 0 : else if (resultStatus == PGRES_COPY_OUT)
2394 : : {
2395 : : /*
2396 : : * Get out of a COPY OUT state: we just switch back to BUSY and
2397 : : * allow the remaining COPY data to be dropped on the floor.
2398 : : */
1647 heikki.linnakangas@i 2399 : 0 : conn->asyncStatus = PGASYNC_BUSY;
2400 : : /* keep waiting to swallow the copy's completion message */
2401 : : }
5383 rhaas@postgresql.org 2402 [ # # ]: 0 : else if (resultStatus == PGRES_COPY_BOTH)
2403 : : {
2404 : : /* We don't allow PQexec during COPY BOTH */
1026 peter@eisentraut.org 2405 : 0 : libpq_append_conn_error(conn, "PQexec not allowed during COPY BOTH");
5263 bruce@momjian.us 2406 : 0 : return false;
2407 : : }
2408 : : /* check for loss of connection, too */
7923 tgl@sss.pgh.pa.us 2409 [ # # ]: 0 : if (conn->status == CONNECTION_BAD)
2410 : 0 : return false;
2411 : : }
2412 : :
2413 : : /* OK to send a command */
8113 tgl@sss.pgh.pa.us 2414 :CBC 90026 : return true;
2415 : : }
2416 : :
2417 : : /*
2418 : : * Common code for PQexec and sibling routines: wait for command result
2419 : : */
2420 : : static PGresult *
2421 : 90026 : PQexecFinish(PGconn *conn)
2422 : : {
2423 : : PGresult *result;
2424 : : PGresult *lastResult;
2425 : :
2426 : : /*
2427 : : * For backwards compatibility, return the last result if there are more
2428 : : * than one. (We used to have logic here to concatenate successive error
2429 : : * messages, but now that happens automatically, since conn->errorMessage
2430 : : * will continue to accumulate errors throughout this loop.)
2431 : : *
2432 : : * We have to stop if we see copy in/out/both, however. We will resume
2433 : : * parsing after application performs the data transfer.
2434 : : *
2435 : : * Also stop if the connection is lost (else we'll loop infinitely).
2436 : : */
9985 bruce@momjian.us 2437 : 90026 : lastResult = NULL;
2438 [ + + ]: 191948 : while ((result = PQgetResult(conn)) != NULL)
2439 : : {
1161 peter@eisentraut.org 2440 : 106462 : PQclear(lastResult);
9985 bruce@momjian.us 2441 : 106462 : lastResult = result;
2442 [ + + ]: 106462 : if (result->resultStatus == PGRES_COPY_IN ||
7923 tgl@sss.pgh.pa.us 2443 [ + + ]: 106391 : result->resultStatus == PGRES_COPY_OUT ||
5383 rhaas@postgresql.org 2444 [ + + ]: 102064 : result->resultStatus == PGRES_COPY_BOTH ||
7923 tgl@sss.pgh.pa.us 2445 [ + - ]: 101922 : conn->status == CONNECTION_BAD)
2446 : : break;
2447 : : }
2448 : :
9985 bruce@momjian.us 2449 : 90026 : return lastResult;
2450 : : }
2451 : :
2452 : : /*
2453 : : * PQdescribePrepared
2454 : : * Obtain information about a previously prepared statement
2455 : : *
2456 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2457 : : * a relevant message.
2458 : : * If the query was sent, a new PGresult is returned (which could indicate
2459 : : * either success or failure). On success, the PGresult contains status
2460 : : * PGRES_COMMAND_OK, and its parameter and column-heading fields describe
2461 : : * the statement's inputs and outputs respectively.
2462 : : * The user is responsible for freeing the PGresult via PQclear()
2463 : : * when done with it.
2464 : : */
2465 : : PGresult *
6959 tgl@sss.pgh.pa.us 2466 : 49 : PQdescribePrepared(PGconn *conn, const char *stmt)
2467 : : {
2468 [ - + ]: 49 : if (!PQexecStart(conn))
6959 tgl@sss.pgh.pa.us 2469 :UBC 0 : return NULL;
746 nathan@postgresql.or 2470 [ - + ]:CBC 49 : if (!PQsendTypedCommand(conn, PqMsg_Describe, 'S', stmt))
6959 tgl@sss.pgh.pa.us 2471 :UBC 0 : return NULL;
6959 tgl@sss.pgh.pa.us 2472 :CBC 49 : return PQexecFinish(conn);
2473 : : }
2474 : :
2475 : : /*
2476 : : * PQdescribePortal
2477 : : * Obtain information about a previously created portal
2478 : : *
2479 : : * This is much like PQdescribePrepared, except that no parameter info is
2480 : : * returned. Note that at the moment, libpq doesn't really expose portals
2481 : : * to the client; but this can be used with a portal created by a SQL
2482 : : * DECLARE CURSOR command.
2483 : : */
2484 : : PGresult *
2485 : 1 : PQdescribePortal(PGconn *conn, const char *portal)
2486 : : {
2487 [ - + ]: 1 : if (!PQexecStart(conn))
6959 tgl@sss.pgh.pa.us 2488 :UBC 0 : return NULL;
746 nathan@postgresql.or 2489 [ - + ]:CBC 1 : if (!PQsendTypedCommand(conn, PqMsg_Describe, 'P', portal))
6959 tgl@sss.pgh.pa.us 2490 :UBC 0 : return NULL;
6959 tgl@sss.pgh.pa.us 2491 :CBC 1 : return PQexecFinish(conn);
2492 : : }
2493 : :
2494 : : /*
2495 : : * PQsendDescribePrepared
2496 : : * Submit a Describe Statement command, but don't wait for it to finish
2497 : : *
2498 : : * Returns: 1 if successfully submitted
2499 : : * 0 if error (conn->errorMessage is set)
2500 : : */
2501 : : int
2502 : 1 : PQsendDescribePrepared(PGconn *conn, const char *stmt)
2503 : : {
746 nathan@postgresql.or 2504 : 1 : return PQsendTypedCommand(conn, PqMsg_Describe, 'S', stmt);
2505 : : }
2506 : :
2507 : : /*
2508 : : * PQsendDescribePortal
2509 : : * Submit a Describe Portal command, but don't wait for it to finish
2510 : : *
2511 : : * Returns: 1 if successfully submitted
2512 : : * 0 if error (conn->errorMessage is set)
2513 : : */
2514 : : int
6959 tgl@sss.pgh.pa.us 2515 : 1 : PQsendDescribePortal(PGconn *conn, const char *portal)
2516 : : {
746 nathan@postgresql.or 2517 : 1 : return PQsendTypedCommand(conn, PqMsg_Describe, 'P', portal);
2518 : : }
2519 : :
2520 : : /*
2521 : : * PQclosePrepared
2522 : : * Close a previously prepared statement
2523 : : *
2524 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2525 : : * a relevant message.
2526 : : * If the query was sent, a new PGresult is returned (which could indicate
2527 : : * either success or failure). On success, the PGresult contains status
2528 : : * PGRES_COMMAND_OK. The user is responsible for freeing the PGresult via
2529 : : * PQclear() when done with it.
2530 : : */
2531 : : PGresult *
795 michael@paquier.xyz 2532 : 1 : PQclosePrepared(PGconn *conn, const char *stmt)
2533 : : {
2534 [ - + ]: 1 : if (!PQexecStart(conn))
795 michael@paquier.xyz 2535 :UBC 0 : return NULL;
746 nathan@postgresql.or 2536 [ - + ]:CBC 1 : if (!PQsendTypedCommand(conn, PqMsg_Close, 'S', stmt))
795 michael@paquier.xyz 2537 :UBC 0 : return NULL;
795 michael@paquier.xyz 2538 :CBC 1 : return PQexecFinish(conn);
2539 : : }
2540 : :
2541 : : /*
2542 : : * PQclosePortal
2543 : : * Close a previously created portal
2544 : : *
2545 : : * This is exactly like PQclosePrepared, but for portals. Note that at the
2546 : : * moment, libpq doesn't really expose portals to the client; but this can be
2547 : : * used with a portal created by a SQL DECLARE CURSOR command.
2548 : : */
2549 : : PGresult *
2550 : 1 : PQclosePortal(PGconn *conn, const char *portal)
2551 : : {
2552 [ - + ]: 1 : if (!PQexecStart(conn))
795 michael@paquier.xyz 2553 :UBC 0 : return NULL;
746 nathan@postgresql.or 2554 [ - + ]:CBC 1 : if (!PQsendTypedCommand(conn, PqMsg_Close, 'P', portal))
795 michael@paquier.xyz 2555 :UBC 0 : return NULL;
795 michael@paquier.xyz 2556 :CBC 1 : return PQexecFinish(conn);
2557 : : }
2558 : :
2559 : : /*
2560 : : * PQsendClosePrepared
2561 : : * Submit a Close Statement command, but don't wait for it to finish
2562 : : *
2563 : : * Returns: 1 if successfully submitted
2564 : : * 0 if error (conn->errorMessage is set)
2565 : : */
2566 : : int
2567 : 20 : PQsendClosePrepared(PGconn *conn, const char *stmt)
2568 : : {
746 nathan@postgresql.or 2569 : 20 : return PQsendTypedCommand(conn, PqMsg_Close, 'S', stmt);
2570 : : }
2571 : :
2572 : : /*
2573 : : * PQsendClosePortal
2574 : : * Submit a Close Portal command, but don't wait for it to finish
2575 : : *
2576 : : * Returns: 1 if successfully submitted
2577 : : * 0 if error (conn->errorMessage is set)
2578 : : */
2579 : : int
795 michael@paquier.xyz 2580 : 1 : PQsendClosePortal(PGconn *conn, const char *portal)
2581 : : {
746 nathan@postgresql.or 2582 : 1 : return PQsendTypedCommand(conn, PqMsg_Close, 'P', portal);
2583 : : }
2584 : :
2585 : : /*
2586 : : * PQsendTypedCommand
2587 : : * Common code to send a Describe or Close command
2588 : : *
2589 : : * Available options for "command" are
2590 : : * PqMsg_Close for Close; or
2591 : : * PqMsg_Describe for Describe.
2592 : : *
2593 : : * Available options for "type" are
2594 : : * 'S' to run a command on a prepared statement; or
2595 : : * 'P' to run a command on a portal.
2596 : : *
2597 : : * Returns 1 on success and 0 on failure.
2598 : : */
2599 : : static int
795 michael@paquier.xyz 2600 : 75 : PQsendTypedCommand(PGconn *conn, char command, char type, const char *target)
2601 : : {
1636 alvherre@alvh.no-ip. 2602 : 75 : PGcmdQueueEntry *entry = NULL;
2603 : :
2604 : : /* Treat null target as empty string */
795 michael@paquier.xyz 2605 [ - + ]: 75 : if (!target)
795 michael@paquier.xyz 2606 :UBC 0 : target = "";
2607 : :
1699 tgl@sss.pgh.pa.us 2608 [ - + ]:CBC 75 : if (!PQsendQueryStart(conn, true))
6959 tgl@sss.pgh.pa.us 2609 :UBC 0 : return 0;
2610 : :
1636 alvherre@alvh.no-ip. 2611 :CBC 75 : entry = pqAllocCmdQueueEntry(conn);
2612 [ - + ]: 75 : if (entry == NULL)
1636 alvherre@alvh.no-ip. 2613 :UBC 0 : return 0; /* error msg already set */
2614 : :
2615 : : /* construct the Close message */
795 michael@paquier.xyz 2616 [ + - + - ]:CBC 150 : if (pqPutMsgStart(command, conn) < 0 ||
2617 [ + - ]: 150 : pqPutc(type, conn) < 0 ||
2618 [ - + ]: 150 : pqPuts(target, conn) < 0 ||
6959 tgl@sss.pgh.pa.us 2619 : 75 : pqPutMsgEnd(conn) < 0)
6959 tgl@sss.pgh.pa.us 2620 :UBC 0 : goto sendFailed;
2621 : :
2622 : : /* construct the Sync message */
1636 alvherre@alvh.no-ip. 2623 [ + + ]:CBC 75 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
2624 : : {
746 nathan@postgresql.or 2625 [ + - - + ]: 124 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1636 alvherre@alvh.no-ip. 2626 : 62 : pqPutMsgEnd(conn) < 0)
1636 alvherre@alvh.no-ip. 2627 :UBC 0 : goto sendFailed;
2628 : : }
2629 : :
2630 : : /* remember if we are doing a Close or a Describe */
746 nathan@postgresql.or 2631 [ + + ]:CBC 75 : if (command == PqMsg_Close)
2632 : : {
795 michael@paquier.xyz 2633 : 23 : entry->queryclass = PGQUERY_CLOSE;
2634 : : }
746 nathan@postgresql.or 2635 [ + - ]: 52 : else if (command == PqMsg_Describe)
2636 : : {
795 michael@paquier.xyz 2637 : 52 : entry->queryclass = PGQUERY_DESCRIBE;
2638 : : }
2639 : : else
2640 : : {
450 peter@eisentraut.org 2641 :UBC 0 : libpq_append_conn_error(conn, "unrecognized message type \"%c\"", command);
795 michael@paquier.xyz 2642 : 0 : goto sendFailed;
2643 : : }
2644 : :
2645 : : /*
2646 : : * Give the data a push (in pipeline mode, only if we're past the size
2647 : : * threshold). In nonblock mode, don't complain if we're unable to send
2648 : : * it all; PQgetResult() will do any additional flushing needed.
2649 : : */
1636 alvherre@alvh.no-ip. 2650 [ - + ]:CBC 75 : if (pqPipelineFlush(conn) < 0)
6959 tgl@sss.pgh.pa.us 2651 :UBC 0 : goto sendFailed;
2652 : :
2653 : : /* OK, it's launched! */
1636 alvherre@alvh.no-ip. 2654 :CBC 75 : pqAppendCmdQueueEntry(conn, entry);
2655 : :
6959 tgl@sss.pgh.pa.us 2656 : 75 : return 1;
2657 : :
6959 tgl@sss.pgh.pa.us 2658 :UBC 0 : sendFailed:
1636 alvherre@alvh.no-ip. 2659 : 0 : pqRecycleCmdQueueEntry(conn, entry);
2660 : : /* error message should be set up already */
6959 tgl@sss.pgh.pa.us 2661 : 0 : return 0;
2662 : : }
2663 : :
2664 : : /*
2665 : : * PQnotifies
2666 : : * returns a PGnotify* structure of the latest async notification
2667 : : * that has not yet been handled
2668 : : *
2669 : : * returns NULL, if there is currently
2670 : : * no unhandled async notification from the backend
2671 : : *
2672 : : * the CALLER is responsible for FREE'ing the structure returned
2673 : : *
2674 : : * Note that this function does not read any new data from the socket;
2675 : : * so usually, caller should call PQconsumeInput() first.
2676 : : */
2677 : : PGnotify *
9985 bruce@momjian.us 2678 :CBC 187364 : PQnotifies(PGconn *conn)
2679 : : {
2680 : : PGnotify *event;
2681 : :
2682 [ - + ]: 187364 : if (!conn)
9985 bruce@momjian.us 2683 :UBC 0 : return NULL;
2684 : :
2685 : : /* Parse any available data to see if we can extract NOTIFY messages. */
9985 bruce@momjian.us 2686 :CBC 187364 : parseInput(conn);
2687 : :
7630 tgl@sss.pgh.pa.us 2688 : 187364 : event = conn->notifyHead;
2689 [ + + ]: 187364 : if (event)
2690 : : {
2691 : 31 : conn->notifyHead = event->next;
2692 [ + + ]: 31 : if (!conn->notifyHead)
2693 : 19 : conn->notifyTail = NULL;
2694 : 31 : event->next = NULL; /* don't let app see the internal state */
2695 : : }
9985 bruce@momjian.us 2696 : 187364 : return event;
2697 : : }
2698 : :
2699 : : /*
2700 : : * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH
2701 : : *
2702 : : * Returns 1 if successful, 0 if data could not be sent (only possible
2703 : : * in nonblock mode), or -1 if an error occurs.
2704 : : */
2705 : : int
8113 tgl@sss.pgh.pa.us 2706 : 349070 : PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
2707 : : {
2708 [ - + ]: 349070 : if (!conn)
8113 tgl@sss.pgh.pa.us 2709 :UBC 0 : return -1;
5383 rhaas@postgresql.org 2710 [ + + ]:CBC 349070 : if (conn->asyncStatus != PGASYNC_COPY_IN &&
2711 [ - + ]: 148300 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2712 : : {
1026 peter@eisentraut.org 2713 :UBC 0 : libpq_append_conn_error(conn, "no COPY in progress");
8113 tgl@sss.pgh.pa.us 2714 : 0 : return -1;
2715 : : }
2716 : :
2717 : : /*
2718 : : * Process any NOTICE or NOTIFY messages that might be pending in the
2719 : : * input buffer. Since the server might generate many notices during the
2720 : : * COPY, we want to clean those out reasonably promptly to prevent
2721 : : * indefinite expansion of the input buffer. (Note: the actual read of
2722 : : * input data into the input buffer happens down inside pqSendSome, but
2723 : : * it's not authorized to get rid of the data again.)
2724 : : */
7993 tgl@sss.pgh.pa.us 2725 :CBC 349070 : parseInput(conn);
2726 : :
8113 2727 [ + - ]: 349070 : if (nbytes > 0)
2728 : : {
2729 : : /*
2730 : : * Try to flush any previously sent data in preference to growing the
2731 : : * output buffer. If we can't enlarge the buffer enough to hold the
2732 : : * data, return 0 in the nonblock case, else hard error. (For
2733 : : * simplicity, always assume 5 bytes of overhead.)
2734 : : */
2735 [ + + ]: 349070 : if ((conn->outBufSize - conn->outCount - 5) < nbytes)
2736 : : {
2737 [ - + ]: 21 : if (pqFlush(conn) < 0)
8113 tgl@sss.pgh.pa.us 2738 :UBC 0 : return -1;
6309 tgl@sss.pgh.pa.us 2739 [ - + ]:CBC 21 : if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes,
2740 : : conn))
8113 tgl@sss.pgh.pa.us 2741 [ # # ]:UBC 0 : return pqIsnonblocking(conn) ? 0 : -1;
2742 : : }
2743 : : /* Send the data (too simple to delegate to fe-protocol files) */
746 nathan@postgresql.or 2744 [ + - + - ]:CBC 698140 : if (pqPutMsgStart(PqMsg_CopyData, conn) < 0 ||
1647 heikki.linnakangas@i 2745 [ - + ]: 698140 : pqPutnchar(buffer, nbytes, conn) < 0 ||
2746 : 349070 : pqPutMsgEnd(conn) < 0)
1647 heikki.linnakangas@i 2747 :UBC 0 : return -1;
2748 : : }
8113 tgl@sss.pgh.pa.us 2749 :CBC 349070 : return 1;
2750 : : }
2751 : :
2752 : : /*
2753 : : * PQputCopyEnd - send EOF indication to the backend during COPY IN
2754 : : *
2755 : : * After calling this, use PQgetResult() to check command completion status.
2756 : : *
2757 : : * Returns 1 if successful, or -1 if an error occurs.
2758 : : */
2759 : : int
2760 : 922 : PQputCopyEnd(PGconn *conn, const char *errormsg)
2761 : : {
2762 [ - + ]: 922 : if (!conn)
8113 tgl@sss.pgh.pa.us 2763 :UBC 0 : return -1;
4650 heikki.linnakangas@i 2764 [ + + ]:CBC 922 : if (conn->asyncStatus != PGASYNC_COPY_IN &&
2765 [ + + ]: 355 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2766 : : {
1026 peter@eisentraut.org 2767 : 32 : libpq_append_conn_error(conn, "no COPY in progress");
8113 tgl@sss.pgh.pa.us 2768 : 32 : return -1;
2769 : : }
2770 : :
2771 : : /*
2772 : : * Send the COPY END indicator. This is simple enough that we don't
2773 : : * bother delegating it to the fe-protocol files.
2774 : : */
1647 heikki.linnakangas@i 2775 [ - + ]: 890 : if (errormsg)
2776 : : {
2777 : : /* Send COPY FAIL */
746 nathan@postgresql.or 2778 [ # # # # ]:UBC 0 : if (pqPutMsgStart(PqMsg_CopyFail, conn) < 0 ||
1647 heikki.linnakangas@i 2779 [ # # ]: 0 : pqPuts(errormsg, conn) < 0 ||
2780 : 0 : pqPutMsgEnd(conn) < 0)
2781 : 0 : return -1;
2782 : : }
2783 : : else
2784 : : {
2785 : : /* Send COPY DONE */
746 nathan@postgresql.or 2786 [ + - - + ]:CBC 1780 : if (pqPutMsgStart(PqMsg_CopyDone, conn) < 0 ||
1647 heikki.linnakangas@i 2787 : 890 : pqPutMsgEnd(conn) < 0)
1647 heikki.linnakangas@i 2788 :UBC 0 : return -1;
2789 : : }
2790 : :
2791 : : /*
2792 : : * If we sent the COPY command in extended-query mode, we must issue a
2793 : : * Sync as well.
2794 : : */
1636 alvherre@alvh.no-ip. 2795 [ + - ]:CBC 890 : if (conn->cmd_queue_head &&
2796 [ - + ]: 890 : conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
2797 : : {
746 nathan@postgresql.or 2798 [ # # # # ]:UBC 0 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1647 heikki.linnakangas@i 2799 : 0 : pqPutMsgEnd(conn) < 0)
8113 tgl@sss.pgh.pa.us 2800 : 0 : return -1;
2801 : : }
2802 : :
2803 : : /* Return to active duty */
4650 heikki.linnakangas@i 2804 [ + + ]:CBC 890 : if (conn->asyncStatus == PGASYNC_COPY_BOTH)
2805 : 323 : conn->asyncStatus = PGASYNC_COPY_OUT;
2806 : : else
2807 : 567 : conn->asyncStatus = PGASYNC_BUSY;
2808 : :
2809 : : /* Try to flush data */
8113 tgl@sss.pgh.pa.us 2810 [ - + ]: 890 : if (pqFlush(conn) < 0)
8113 tgl@sss.pgh.pa.us 2811 :UBC 0 : return -1;
2812 : :
8113 tgl@sss.pgh.pa.us 2813 :CBC 890 : return 1;
2814 : : }
2815 : :
2816 : : /*
2817 : : * PQgetCopyData - read a row of data from the backend during COPY OUT
2818 : : * or COPY BOTH
2819 : : *
2820 : : * If successful, sets *buffer to point to a malloc'd row of data, and
2821 : : * returns row length (always > 0) as result.
2822 : : * Returns 0 if no row available yet (only possible if async is true),
2823 : : * -1 if end of copy (consult PQgetResult), or -2 if error (consult
2824 : : * PQerrorMessage).
2825 : : */
2826 : : int
2827 : 2777093 : PQgetCopyData(PGconn *conn, char **buffer, int async)
2828 : : {
2829 : 2777093 : *buffer = NULL; /* for all failure cases */
2830 [ - + ]: 2777093 : if (!conn)
8113 tgl@sss.pgh.pa.us 2831 :UBC 0 : return -2;
5383 rhaas@postgresql.org 2832 [ + + ]:CBC 2777093 : if (conn->asyncStatus != PGASYNC_COPY_OUT &&
2833 [ - + ]: 604021 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2834 : : {
1026 peter@eisentraut.org 2835 :UBC 0 : libpq_append_conn_error(conn, "no COPY in progress");
8113 tgl@sss.pgh.pa.us 2836 : 0 : return -2;
2837 : : }
1647 heikki.linnakangas@i 2838 :CBC 2777093 : return pqGetCopyData3(conn, buffer, async);
2839 : : }
2840 : :
2841 : : /*
2842 : : * PQgetline - gets a newline-terminated string from the backend.
2843 : : *
2844 : : * Chiefly here so that applications can use "COPY <rel> to stdout"
2845 : : * and read the output string. Returns a null-terminated string in `buffer`.
2846 : : *
2847 : : * XXX this routine is now deprecated, because it can't handle binary data.
2848 : : * If called during a COPY BINARY we return EOF.
2849 : : *
2850 : : * PQgetline reads up to `length`-1 characters (like fgets(3)) but strips
2851 : : * the terminating \n (like gets(3)).
2852 : : *
2853 : : * CAUTION: the caller is responsible for detecting the end-of-copy signal
2854 : : * (a line containing just "\.") when using this routine.
2855 : : *
2856 : : * RETURNS:
2857 : : * EOF if error (eg, invalid arguments are given)
2858 : : * 0 if EOL is reached (i.e., \n has been read)
2859 : : * (this is required for backward-compatibility -- this
2860 : : * routine used to always return EOF or 0, assuming that
2861 : : * the line ended within `length` bytes.)
2862 : : * 1 in other cases (i.e., the buffer was filled before \n is reached)
2863 : : */
2864 : : int
1082 pg@bowt.ie 2865 :UBC 0 : PQgetline(PGconn *conn, char *buffer, int length)
2866 : : {
2867 [ # # # # ]: 0 : if (!buffer || length <= 0)
8126 tgl@sss.pgh.pa.us 2868 : 0 : return EOF;
1082 pg@bowt.ie 2869 : 0 : *buffer = '\0';
2870 : : /* length must be at least 3 to hold the \. terminator! */
2871 [ # # ]: 0 : if (length < 3)
9985 bruce@momjian.us 2872 : 0 : return EOF;
2873 : :
8126 tgl@sss.pgh.pa.us 2874 [ # # ]: 0 : if (!conn)
9985 bruce@momjian.us 2875 : 0 : return EOF;
2876 : :
1082 pg@bowt.ie 2877 : 0 : return pqGetline3(conn, buffer, length);
2878 : : }
2879 : :
2880 : : /*
2881 : : * PQgetlineAsync - gets a COPY data row without blocking.
2882 : : *
2883 : : * This routine is for applications that want to do "COPY <rel> to stdout"
2884 : : * asynchronously, that is without blocking. Having issued the COPY command
2885 : : * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput
2886 : : * and this routine until the end-of-data signal is detected. Unlike
2887 : : * PQgetline, this routine takes responsibility for detecting end-of-data.
2888 : : *
2889 : : * On each call, PQgetlineAsync will return data if a complete data row
2890 : : * is available in libpq's input buffer. Otherwise, no data is returned
2891 : : * until the rest of the row arrives.
2892 : : *
2893 : : * If -1 is returned, the end-of-data signal has been recognized (and removed
2894 : : * from libpq's input buffer). The caller *must* next call PQendcopy and
2895 : : * then return to normal processing.
2896 : : *
2897 : : * RETURNS:
2898 : : * -1 if the end-of-copy-data marker has been recognized
2899 : : * 0 if no data is available
2900 : : * >0 the number of bytes returned.
2901 : : *
2902 : : * The data returned will not extend beyond a data-row boundary. If possible
2903 : : * a whole row will be returned at one time. But if the buffer offered by
2904 : : * the caller is too small to hold a row sent by the backend, then a partial
2905 : : * data row will be returned. In text mode this can be detected by testing
2906 : : * whether the last returned byte is '\n' or not.
2907 : : *
2908 : : * The returned data is *not* null-terminated.
2909 : : */
2910 : :
2911 : : int
9865 bruce@momjian.us 2912 : 0 : PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
2913 : : {
8126 tgl@sss.pgh.pa.us 2914 [ # # ]: 0 : if (!conn)
8173 2915 : 0 : return -1;
2916 : :
1647 heikki.linnakangas@i 2917 : 0 : return pqGetlineAsync3(conn, buffer, bufsize);
2918 : : }
2919 : :
2920 : : /*
2921 : : * PQputline -- sends a string to the backend during COPY IN.
2922 : : * Returns 0 if OK, EOF if not.
2923 : : *
2924 : : * This is deprecated primarily because the return convention doesn't allow
2925 : : * caller to tell the difference between a hard error and a nonblock-mode
2926 : : * send failure.
2927 : : */
2928 : : int
1082 pg@bowt.ie 2929 :CBC 200028 : PQputline(PGconn *conn, const char *string)
2930 : : {
2931 : 200028 : return PQputnbytes(conn, string, strlen(string));
2932 : : }
2933 : :
2934 : : /*
2935 : : * PQputnbytes -- like PQputline, but buffer need not be null-terminated.
2936 : : * Returns 0 if OK, EOF if not.
2937 : : */
2938 : : int
9882 scrappy@hub.org 2939 : 200028 : PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
2940 : : {
8113 tgl@sss.pgh.pa.us 2941 [ + - ]: 200028 : if (PQputCopyData(conn, buffer, nbytes) > 0)
2942 : 200028 : return 0;
2943 : : else
9865 bruce@momjian.us 2944 :UBC 0 : return EOF;
2945 : : }
2946 : :
2947 : : /*
2948 : : * PQendcopy
2949 : : * After completing the data transfer portion of a copy in/out,
2950 : : * the application must call this routine to finish the command protocol.
2951 : : *
2952 : : * This is deprecated; it's cleaner to use PQgetResult to get the transfer
2953 : : * status.
2954 : : *
2955 : : * RETURNS:
2956 : : * 0 on success
2957 : : * 1 on failure
2958 : : */
2959 : : int
9985 bruce@momjian.us 2960 :CBC 189 : PQendcopy(PGconn *conn)
2961 : : {
2962 [ - + ]: 189 : if (!conn)
9985 bruce@momjian.us 2963 :UBC 0 : return 0;
2964 : :
1647 heikki.linnakangas@i 2965 :CBC 189 : return pqEndcopy3(conn);
2966 : : }
2967 : :
2968 : :
2969 : : /* ----------------
2970 : : * PQfn - Send a function call to the POSTGRES backend.
2971 : : *
2972 : : * conn : backend connection
2973 : : * fnid : OID of function to be called
2974 : : * result_buf : pointer to result buffer
2975 : : * result_len : actual length of result is returned here
2976 : : * result_is_int : If the result is an integer, this must be 1,
2977 : : * otherwise this should be 0
2978 : : * args : pointer to an array of function arguments
2979 : : * (each has length, if integer, and value/pointer)
2980 : : * nargs : # of arguments in args array.
2981 : : *
2982 : : * RETURNS
2983 : : * PGresult with status = PGRES_COMMAND_OK if successful.
2984 : : * *result_len is > 0 if there is a return value, 0 if not.
2985 : : * PGresult with status = PGRES_FATAL_ERROR if backend returns an error.
2986 : : * NULL on communications failure. conn->errorMessage will be set.
2987 : : * ----------------
2988 : : */
2989 : :
2990 : : PGresult *
10225 bruce@momjian.us 2991 : 1084 : PQfn(PGconn *conn,
2992 : : int fnid,
2993 : : int *result_buf,
2994 : : int *result_len,
2995 : : int result_is_int,
2996 : : const PQArgBlock *args,
2997 : : int nargs)
2998 : : {
3835 tgl@sss.pgh.pa.us 2999 : 1084 : *result_len = 0;
3000 : :
10226 bruce@momjian.us 3001 [ - + ]: 1084 : if (!conn)
10226 bruce@momjian.us 3002 :UBC 0 : return NULL;
3003 : :
3004 : : /*
3005 : : * Since this is the beginning of a query cycle, reset the error state.
3006 : : * However, in pipeline mode with something already queued, the error
3007 : : * buffer belongs to that command and we shouldn't clear it.
3008 : : */
1286 tgl@sss.pgh.pa.us 3009 [ + - ]:CBC 1084 : if (conn->cmd_queue_head == NULL)
3010 : 1084 : pqClearConnErrorState(conn);
3011 : :
1636 alvherre@alvh.no-ip. 3012 [ - + ]: 1084 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
3013 : : {
1026 peter@eisentraut.org 3014 :UBC 0 : libpq_append_conn_error(conn, "%s not allowed in pipeline mode", "PQfn");
1636 alvherre@alvh.no-ip. 3015 : 0 : return NULL;
3016 : : }
3017 : :
4161 bruce@momjian.us 3018 [ + - + - ]:CBC 1084 : if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
1234 tgl@sss.pgh.pa.us 3019 [ + - - + ]: 1084 : pgHavePendingResult(conn))
3020 : : {
1026 peter@eisentraut.org 3021 :UBC 0 : libpq_append_conn_error(conn, "connection in wrong state");
9985 bruce@momjian.us 3022 : 0 : return NULL;
3023 : : }
3024 : :
1647 heikki.linnakangas@i 3025 :CBC 1084 : return pqFunctionCall3(conn, fnid,
3026 : : result_buf, result_len,
3027 : : result_is_int,
3028 : : args, nargs);
3029 : : }
3030 : :
3031 : : /* ====== Pipeline mode support ======== */
3032 : :
3033 : : /*
3034 : : * PQenterPipelineMode
3035 : : * Put an idle connection in pipeline mode.
3036 : : *
3037 : : * Returns 1 on success. On failure, errorMessage is set and 0 is returned.
3038 : : *
3039 : : * Commands submitted after this can be pipelined on the connection;
3040 : : * there's no requirement to wait for one to finish before the next is
3041 : : * dispatched.
3042 : : *
3043 : : * Queuing of a new query or syncing during COPY is not allowed.
3044 : : *
3045 : : * A set of commands is terminated by a PQpipelineSync. Multiple sync
3046 : : * points can be established while in pipeline mode. Pipeline mode can
3047 : : * be exited by calling PQexitPipelineMode() once all results are processed.
3048 : : *
3049 : : * This doesn't actually send anything on the wire, it just puts libpq
3050 : : * into a state where it can pipeline work.
3051 : : */
3052 : : int
1636 alvherre@alvh.no-ip. 3053 : 218 : PQenterPipelineMode(PGconn *conn)
3054 : : {
3055 [ - + ]: 218 : if (!conn)
1636 alvherre@alvh.no-ip. 3056 :UBC 0 : return 0;
3057 : :
3058 : : /* succeed with no action if already in pipeline mode */
1636 alvherre@alvh.no-ip. 3059 [ + + ]:CBC 218 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
3060 : 4 : return 1;
3061 : :
3062 [ - + ]: 214 : if (conn->asyncStatus != PGASYNC_IDLE)
3063 : : {
1026 peter@eisentraut.org 3064 :UBC 0 : libpq_append_conn_error(conn, "cannot enter pipeline mode, connection not idle");
1636 alvherre@alvh.no-ip. 3065 : 0 : return 0;
3066 : : }
3067 : :
1636 alvherre@alvh.no-ip. 3068 :CBC 214 : conn->pipelineStatus = PQ_PIPELINE_ON;
3069 : :
3070 : 214 : return 1;
3071 : : }
3072 : :
3073 : : /*
3074 : : * PQexitPipelineMode
3075 : : * End pipeline mode and return to normal command mode.
3076 : : *
3077 : : * Returns 1 in success (pipeline mode successfully ended, or not in pipeline
3078 : : * mode).
3079 : : *
3080 : : * Returns 0 if in pipeline mode and cannot be ended yet. Error message will
3081 : : * be set.
3082 : : */
3083 : : int
3084 : 205 : PQexitPipelineMode(PGconn *conn)
3085 : : {
3086 [ - + ]: 205 : if (!conn)
1636 alvherre@alvh.no-ip. 3087 :UBC 0 : return 0;
3088 : :
1159 alvherre@alvh.no-ip. 3089 [ + + ]:CBC 205 : if (conn->pipelineStatus == PQ_PIPELINE_OFF &&
3090 [ - + ]: 1 : (conn->asyncStatus == PGASYNC_IDLE ||
1159 alvherre@alvh.no-ip. 3091 [ # # ]:UBC 0 : conn->asyncStatus == PGASYNC_PIPELINE_IDLE) &&
1159 alvherre@alvh.no-ip. 3092 [ + - ]:CBC 1 : conn->cmd_queue_head == NULL)
1636 3093 : 1 : return 1;
3094 : :
3095 [ - + + - : 204 : switch (conn->asyncStatus)
- ]
3096 : : {
1636 alvherre@alvh.no-ip. 3097 :UBC 0 : case PGASYNC_READY:
3098 : : case PGASYNC_READY_MORE:
3099 : : /* there are some uncollected results */
1026 peter@eisentraut.org 3100 : 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode with uncollected results");
1636 alvherre@alvh.no-ip. 3101 : 0 : return 0;
3102 : :
1636 alvherre@alvh.no-ip. 3103 :CBC 5 : case PGASYNC_BUSY:
1026 peter@eisentraut.org 3104 : 5 : libpq_append_conn_error(conn, "cannot exit pipeline mode while busy");
1636 alvherre@alvh.no-ip. 3105 : 5 : return 0;
3106 : :
1159 3107 : 199 : case PGASYNC_IDLE:
3108 : : case PGASYNC_PIPELINE_IDLE:
3109 : : /* OK */
1636 3110 : 199 : break;
3111 : :
1159 alvherre@alvh.no-ip. 3112 :UBC 0 : case PGASYNC_COPY_IN:
3113 : : case PGASYNC_COPY_OUT:
3114 : : case PGASYNC_COPY_BOTH:
1026 peter@eisentraut.org 3115 : 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode while in COPY");
3116 : : }
3117 : :
3118 : : /* still work to process */
1636 alvherre@alvh.no-ip. 3119 [ - + ]:CBC 199 : if (conn->cmd_queue_head != NULL)
3120 : : {
1026 peter@eisentraut.org 3121 :UBC 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode with uncollected results");
1636 alvherre@alvh.no-ip. 3122 : 0 : return 0;
3123 : : }
3124 : :
1636 alvherre@alvh.no-ip. 3125 :CBC 199 : conn->pipelineStatus = PQ_PIPELINE_OFF;
3126 : 199 : conn->asyncStatus = PGASYNC_IDLE;
3127 : :
3128 : : /* Flush any pending data in out buffer */
3129 [ - + ]: 199 : if (pqFlush(conn) < 0)
1636 alvherre@alvh.no-ip. 3130 :UBC 0 : return 0; /* error message is setup already */
1636 alvherre@alvh.no-ip. 3131 :CBC 199 : return 1;
3132 : : }
3133 : :
3134 : : /*
3135 : : * pqCommandQueueAdvance
3136 : : * Remove one query from the command queue, if appropriate.
3137 : : *
3138 : : * If we have received all results corresponding to the head element
3139 : : * in the command queue, remove it.
3140 : : *
3141 : : * In simple query protocol we must not advance the command queue until the
3142 : : * ReadyForQuery message has been received. This is because in simple mode a
3143 : : * command can have multiple queries, and we must process result for all of
3144 : : * them before moving on to the next command.
3145 : : *
3146 : : * Another consideration is synchronization during error processing in
3147 : : * extended query protocol: we refuse to advance the queue past a SYNC queue
3148 : : * element, unless the result we've received is also a SYNC. In particular
3149 : : * this protects us from advancing when an error is received at an
3150 : : * inappropriate moment.
3151 : : */
3152 : : void
641 3153 : 650998 : pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery, bool gotSync)
3154 : : {
3155 : : PGcmdQueueEntry *prevquery;
3156 : :
1636 3157 [ + + ]: 650998 : if (conn->cmd_queue_head == NULL)
3158 : 21516 : return;
3159 : :
3160 : : /*
3161 : : * If processing a query of simple query protocol, we only advance the
3162 : : * queue when we receive the ReadyForQuery message for it.
3163 : : */
641 3164 [ + + + + ]: 629482 : if (conn->cmd_queue_head->queryclass == PGQUERY_SIMPLE && !isReadyForQuery)
3165 : 318624 : return;
3166 : :
3167 : : /*
3168 : : * If we're waiting for a SYNC, don't advance the queue until we get one.
3169 : : */
3170 [ + + - + ]: 310858 : if (conn->cmd_queue_head->queryclass == PGQUERY_SYNC && !gotSync)
641 alvherre@alvh.no-ip. 3171 :UBC 0 : return;
3172 : :
3173 : : /* delink element from queue */
1636 alvherre@alvh.no-ip. 3174 :CBC 310858 : prevquery = conn->cmd_queue_head;
3175 : 310858 : conn->cmd_queue_head = conn->cmd_queue_head->next;
3176 : :
3177 : : /* If the queue is now empty, reset the tail too */
1159 3178 [ + + ]: 310858 : if (conn->cmd_queue_head == NULL)
3179 : 308602 : conn->cmd_queue_tail = NULL;
3180 : :
3181 : : /* and make the queue element recyclable */
1636 3182 : 310858 : prevquery->next = NULL;
3183 : 310858 : pqRecycleCmdQueueEntry(conn, prevquery);
3184 : : }
3185 : :
3186 : : /*
3187 : : * pqPipelineProcessQueue: subroutine for PQgetResult
3188 : : * In pipeline mode, start processing the results of the next query in the queue.
3189 : : */
3190 : : static void
3191 : 2509 : pqPipelineProcessQueue(PGconn *conn)
3192 : : {
3193 [ - + + - ]: 2509 : switch (conn->asyncStatus)
3194 : : {
1636 alvherre@alvh.no-ip. 3195 :UBC 0 : case PGASYNC_COPY_IN:
3196 : : case PGASYNC_COPY_OUT:
3197 : : case PGASYNC_COPY_BOTH:
3198 : : case PGASYNC_READY:
3199 : : case PGASYNC_READY_MORE:
3200 : : case PGASYNC_BUSY:
3201 : : /* client still has to process current query or results */
3202 : 0 : return;
3203 : :
1636 alvherre@alvh.no-ip. 3204 :CBC 10 : case PGASYNC_IDLE:
3205 : :
3206 : : /*
3207 : : * If we're in IDLE mode and there's some command in the queue,
3208 : : * get us into PIPELINE_IDLE mode and process normally. Otherwise
3209 : : * there's nothing for us to do.
3210 : : */
1159 3211 [ + - ]: 10 : if (conn->cmd_queue_head != NULL)
3212 : : {
3213 : 10 : conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
3214 : 10 : break;
3215 : : }
1159 alvherre@alvh.no-ip. 3216 :UBC 0 : return;
3217 : :
1159 alvherre@alvh.no-ip. 3218 :CBC 2499 : case PGASYNC_PIPELINE_IDLE:
3219 [ - + ]: 2499 : Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
3220 : : /* next query please */
1636 3221 : 2499 : break;
3222 : : }
3223 : :
3224 : : /*
3225 : : * Reset partial-result mode. (Client has to set it up for each query, if
3226 : : * desired.)
3227 : : */
518 tgl@sss.pgh.pa.us 3228 : 2509 : conn->partialResMode = false;
1058 alvherre@alvh.no-ip. 3229 : 2509 : conn->singleRowMode = false;
518 tgl@sss.pgh.pa.us 3230 : 2509 : conn->maxChunkSize = 0;
3231 : :
3232 : : /*
3233 : : * If there are no further commands to process in the queue, get us in
3234 : : * "real idle" mode now.
3235 : : */
1159 alvherre@alvh.no-ip. 3236 [ + + ]: 2509 : if (conn->cmd_queue_head == NULL)
3237 : : {
3238 : 243 : conn->asyncStatus = PGASYNC_IDLE;
1636 3239 : 243 : return;
3240 : : }
3241 : :
3242 : : /*
3243 : : * Reset the error state. This and the next couple of steps correspond to
3244 : : * what PQsendQueryStart didn't do for this query.
3245 : : */
1286 tgl@sss.pgh.pa.us 3246 : 2266 : pqClearConnErrorState(conn);
3247 : :
3248 : : /* Initialize async result-accumulation state */
1636 alvherre@alvh.no-ip. 3249 : 2266 : pqClearAsyncResult(conn);
3250 : :
3251 [ + + ]: 2266 : if (conn->pipelineStatus == PQ_PIPELINE_ABORTED &&
3252 [ + + ]: 300 : conn->cmd_queue_head->queryclass != PGQUERY_SYNC)
3253 : : {
3254 : : /*
3255 : : * In an aborted pipeline we don't get anything from the server for
3256 : : * each result; we're just discarding commands from the queue until we
3257 : : * get to the next sync from the server.
3258 : : *
3259 : : * The PGRES_PIPELINE_ABORTED results tell the client that its queries
3260 : : * got aborted.
3261 : : */
3262 : 253 : conn->result = PQmakeEmptyPGresult(conn, PGRES_PIPELINE_ABORTED);
3263 [ - + ]: 253 : if (!conn->result)
3264 : : {
1026 peter@eisentraut.org 3265 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
1636 alvherre@alvh.no-ip. 3266 : 0 : pqSaveErrorResult(conn);
3267 : 0 : return;
3268 : : }
1636 alvherre@alvh.no-ip. 3269 :CBC 253 : conn->asyncStatus = PGASYNC_READY;
3270 : : }
3271 : : else
3272 : : {
3273 : : /* allow parsing to continue */
3274 : 2013 : conn->asyncStatus = PGASYNC_BUSY;
3275 : : }
3276 : : }
3277 : :
3278 : : /*
3279 : : * PQpipelineSync
3280 : : * Send a Sync message as part of a pipeline, and flush to server
3281 : : */
3282 : : int
599 michael@paquier.xyz 3283 : 216 : PQpipelineSync(PGconn *conn)
3284 : : {
3285 : 216 : return pqPipelineSyncInternal(conn, true);
3286 : : }
3287 : :
3288 : : /*
3289 : : * PQsendPipelineSync
3290 : : * Send a Sync message as part of a pipeline, without flushing to server
3291 : : */
3292 : : int
3293 : 60 : PQsendPipelineSync(PGconn *conn)
3294 : : {
3295 : 60 : return pqPipelineSyncInternal(conn, false);
3296 : : }
3297 : :
3298 : : /*
3299 : : * Workhorse function for PQpipelineSync and PQsendPipelineSync.
3300 : : *
3301 : : * immediate_flush controls if the flush happens immediately after sending the
3302 : : * Sync message or not.
3303 : : */
3304 : : static int
3305 : 276 : pqPipelineSyncInternal(PGconn *conn, bool immediate_flush)
3306 : : {
3307 : : PGcmdQueueEntry *entry;
3308 : :
1636 alvherre@alvh.no-ip. 3309 [ - + ]: 276 : if (!conn)
1636 alvherre@alvh.no-ip. 3310 :UBC 0 : return 0;
3311 : :
1636 alvherre@alvh.no-ip. 3312 [ + + ]:CBC 276 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
3313 : : {
1026 peter@eisentraut.org 3314 : 3 : libpq_append_conn_error(conn, "cannot send pipeline when not in pipeline mode");
1636 alvherre@alvh.no-ip. 3315 : 3 : return 0;
3316 : : }
3317 : :
3318 [ - + - ]: 273 : switch (conn->asyncStatus)
3319 : : {
1636 alvherre@alvh.no-ip. 3320 :UBC 0 : case PGASYNC_COPY_IN:
3321 : : case PGASYNC_COPY_OUT:
3322 : : case PGASYNC_COPY_BOTH:
3323 : : /* should be unreachable */
3324 : 0 : appendPQExpBufferStr(&conn->errorMessage,
3325 : : "internal error: cannot send pipeline while in COPY\n");
3326 : 0 : return 0;
1636 alvherre@alvh.no-ip. 3327 :CBC 273 : case PGASYNC_READY:
3328 : : case PGASYNC_READY_MORE:
3329 : : case PGASYNC_BUSY:
3330 : : case PGASYNC_IDLE:
3331 : : case PGASYNC_PIPELINE_IDLE:
3332 : : /* OK to send sync */
3333 : 273 : break;
3334 : : }
3335 : :
3336 : 273 : entry = pqAllocCmdQueueEntry(conn);
3337 [ - + ]: 273 : if (entry == NULL)
1636 alvherre@alvh.no-ip. 3338 :UBC 0 : return 0; /* error msg already set */
3339 : :
1636 alvherre@alvh.no-ip. 3340 :CBC 273 : entry->queryclass = PGQUERY_SYNC;
3341 : 273 : entry->query = NULL;
3342 : :
3343 : : /* construct the Sync message */
746 nathan@postgresql.or 3344 [ + - - + ]: 546 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1636 alvherre@alvh.no-ip. 3345 : 273 : pqPutMsgEnd(conn) < 0)
1636 alvherre@alvh.no-ip. 3346 :UBC 0 : goto sendFailed;
3347 : :
3348 : : /*
3349 : : * Give the data a push. In nonblock mode, don't complain if we're unable
3350 : : * to send it all; PQgetResult() will do any additional flushing needed.
3351 : : * If immediate_flush is disabled, the data is pushed if we are past the
3352 : : * size threshold.
3353 : : */
599 michael@paquier.xyz 3354 [ + + ]:CBC 273 : if (immediate_flush)
3355 : : {
3356 [ - + ]: 213 : if (pqFlush(conn) < 0)
599 michael@paquier.xyz 3357 :UBC 0 : goto sendFailed;
3358 : : }
3359 : : else
3360 : : {
599 michael@paquier.xyz 3361 [ - + ]:CBC 60 : if (pqPipelineFlush(conn) < 0)
599 michael@paquier.xyz 3362 :UBC 0 : goto sendFailed;
3363 : : }
3364 : :
3365 : : /* OK, it's launched! */
1520 alvherre@alvh.no-ip. 3366 :CBC 273 : pqAppendCmdQueueEntry(conn, entry);
3367 : :
1636 3368 : 273 : return 1;
3369 : :
1636 alvherre@alvh.no-ip. 3370 :UBC 0 : sendFailed:
3371 : 0 : pqRecycleCmdQueueEntry(conn, entry);
3372 : : /* error message should be set up already */
3373 : 0 : return 0;
3374 : : }
3375 : :
3376 : : /*
3377 : : * PQsendFlushRequest
3378 : : * Send request for server to flush its buffer. Useful in pipeline
3379 : : * mode when a sync point is not desired.
3380 : : */
3381 : : int
1530 alvherre@alvh.no-ip. 3382 :CBC 35 : PQsendFlushRequest(PGconn *conn)
3383 : : {
3384 [ - + ]: 35 : if (!conn)
1530 alvherre@alvh.no-ip. 3385 :UBC 0 : return 0;
3386 : :
3387 : : /* Don't try to send if we know there's no live connection. */
1530 alvherre@alvh.no-ip. 3388 [ - + ]:CBC 35 : if (conn->status != CONNECTION_OK)
3389 : : {
1026 peter@eisentraut.org 3390 :UBC 0 : libpq_append_conn_error(conn, "no connection to the server");
1530 alvherre@alvh.no-ip. 3391 : 0 : return 0;
3392 : : }
3393 : :
3394 : : /* Can't send while already busy, either, unless enqueuing for later */
1530 alvherre@alvh.no-ip. 3395 [ + - ]:CBC 35 : if (conn->asyncStatus != PGASYNC_IDLE &&
3396 [ - + ]: 35 : conn->pipelineStatus == PQ_PIPELINE_OFF)
3397 : : {
1026 peter@eisentraut.org 3398 :UBC 0 : libpq_append_conn_error(conn, "another command is already in progress");
1520 alvherre@alvh.no-ip. 3399 : 0 : return 0;
3400 : : }
3401 : :
746 nathan@postgresql.or 3402 [ + - - + ]:CBC 70 : if (pqPutMsgStart(PqMsg_Flush, conn) < 0 ||
1530 alvherre@alvh.no-ip. 3403 : 35 : pqPutMsgEnd(conn) < 0)
3404 : : {
1530 alvherre@alvh.no-ip. 3405 :UBC 0 : return 0;
3406 : : }
3407 : :
3408 : : /*
3409 : : * Give the data a push (in pipeline mode, only if we're past the size
3410 : : * threshold). In nonblock mode, don't complain if we're unable to send
3411 : : * it all; PQgetResult() will do any additional flushing needed.
3412 : : */
668 alvherre@alvh.no-ip. 3413 [ - + ]:CBC 35 : if (pqPipelineFlush(conn) < 0)
668 alvherre@alvh.no-ip. 3414 :UBC 0 : return 0;
3415 : :
1530 alvherre@alvh.no-ip. 3416 :CBC 35 : return 1;
3417 : : }
3418 : :
3419 : : /* ====== accessor funcs for PGresult ======== */
3420 : :
3421 : : ExecStatusType
9431 bruce@momjian.us 3422 : 1179138 : PQresultStatus(const PGresult *res)
3423 : : {
10226 3424 [ + + ]: 1179138 : if (!res)
8113 tgl@sss.pgh.pa.us 3425 : 115 : return PGRES_FATAL_ERROR;
10226 bruce@momjian.us 3426 : 1179023 : return res->resultStatus;
3427 : : }
3428 : :
3429 : : char *
9708 tgl@sss.pgh.pa.us 3430 : 22 : PQresStatus(ExecStatusType status)
3431 : : {
1636 alvherre@alvh.no-ip. 3432 [ - + ]: 22 : if ((unsigned int) status >= lengthof(pgresStatus))
8819 peter_e@gmx.net 3433 :UBC 0 : return libpq_gettext("invalid ExecStatusType code");
9708 tgl@sss.pgh.pa.us 3434 :CBC 22 : return pgresStatus[status];
3435 : : }
3436 : :
3437 : : char *
9431 bruce@momjian.us 3438 : 32832 : PQresultErrorMessage(const PGresult *res)
3439 : : {
9837 tgl@sss.pgh.pa.us 3440 [ + - + + ]: 32832 : if (!res || !res->errMsg)
3441 : 1 : return "";
3442 : 32831 : return res->errMsg;
3443 : : }
3444 : :
3445 : : char *
3443 3446 : 3 : PQresultVerboseErrorMessage(const PGresult *res,
3447 : : PGVerbosity verbosity,
3448 : : PGContextVisibility show_context)
3449 : : {
3450 : : PQExpBufferData workBuf;
3451 : :
3452 : : /*
3453 : : * Because the caller is expected to free the result string, we must
3454 : : * strdup any constant result. We use plain strdup and document that
3455 : : * callers should expect NULL if out-of-memory.
3456 : : */
3457 [ + - ]: 3 : if (!res ||
3458 [ - + ]: 3 : (res->resultStatus != PGRES_FATAL_ERROR &&
3443 tgl@sss.pgh.pa.us 3459 [ # # ]:UBC 0 : res->resultStatus != PGRES_NONFATAL_ERROR))
3460 : 0 : return strdup(libpq_gettext("PGresult is not an error result\n"));
3461 : :
3443 tgl@sss.pgh.pa.us 3462 :CBC 3 : initPQExpBuffer(&workBuf);
3463 : :
3464 : 3 : pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
3465 : :
3466 : : /* If insufficient memory to format the message, fail cleanly */
3467 [ - + ]: 3 : if (PQExpBufferDataBroken(workBuf))
3468 : : {
3443 tgl@sss.pgh.pa.us 3469 :UBC 0 : termPQExpBuffer(&workBuf);
3470 : 0 : return strdup(libpq_gettext("out of memory\n"));
3471 : : }
3472 : :
3443 tgl@sss.pgh.pa.us 3473 :CBC 3 : return workBuf.data;
3474 : : }
3475 : :
3476 : : char *
8113 3477 : 297107 : PQresultErrorField(const PGresult *res, int fieldcode)
3478 : : {
3479 : : PGMessageField *pfield;
3480 : :
3481 [ + + ]: 297107 : if (!res)
3482 : 20 : return NULL;
3483 [ + + ]: 2226733 : for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
3484 : : {
3485 [ + + ]: 2055096 : if (pfield->code == fieldcode)
3486 : 125450 : return pfield->contents;
3487 : : }
3488 : 171637 : return NULL;
3489 : : }
3490 : :
3491 : : int
9431 bruce@momjian.us 3492 : 291164 : PQntuples(const PGresult *res)
3493 : : {
10226 3494 [ + + ]: 291164 : if (!res)
9985 3495 : 2189 : return 0;
10226 3496 : 288975 : return res->ntups;
3497 : : }
3498 : :
3499 : : int
9431 3500 : 174391 : PQnfields(const PGresult *res)
3501 : : {
10226 3502 [ - + ]: 174391 : if (!res)
9985 bruce@momjian.us 3503 :UBC 0 : return 0;
10226 bruce@momjian.us 3504 :CBC 174391 : return res->numAttributes;
3505 : : }
3506 : :
3507 : : int
9431 3508 : 469 : PQbinaryTuples(const PGresult *res)
3509 : : {
9865 3510 [ - + ]: 469 : if (!res)
9865 bruce@momjian.us 3511 :UBC 0 : return 0;
9865 bruce@momjian.us 3512 :CBC 469 : return res->binary;
3513 : : }
3514 : :
3515 : : /*
3516 : : * Helper routines to range-check field numbers and tuple numbers.
3517 : : * Return true if OK, false if not
3518 : : */
3519 : :
3520 : : static int
8819 peter_e@gmx.net 3521 : 272122 : check_field_number(const PGresult *res, int field_num)
3522 : : {
10226 bruce@momjian.us 3523 [ - + ]: 272122 : if (!res)
2943 peter_e@gmx.net 3524 :UBC 0 : return false; /* no way to display error message... */
9890 bruce@momjian.us 3525 [ + - - + ]:CBC 272122 : if (field_num < 0 || field_num >= res->numAttributes)
3526 : : {
8111 tgl@sss.pgh.pa.us 3527 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3528 : : "column number %d is out of range 0..%d",
3529 : 0 : field_num, res->numAttributes - 1);
2943 peter_e@gmx.net 3530 : 0 : return false;
3531 : : }
2943 peter_e@gmx.net 3532 :CBC 272122 : return true;
3533 : : }
3534 : :
3535 : : static int
8819 3536 : 21829210 : check_tuple_field_number(const PGresult *res,
3537 : : int tup_num, int field_num)
3538 : : {
9890 bruce@momjian.us 3539 [ - + ]: 21829210 : if (!res)
2943 peter_e@gmx.net 3540 :UBC 0 : return false; /* no way to display error message... */
9890 bruce@momjian.us 3541 [ + - - + ]:CBC 21829210 : if (tup_num < 0 || tup_num >= res->ntups)
3542 : : {
8111 tgl@sss.pgh.pa.us 3543 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3544 : : "row number %d is out of range 0..%d",
3545 : 0 : tup_num, res->ntups - 1);
2943 peter_e@gmx.net 3546 : 0 : return false;
3547 : : }
9985 bruce@momjian.us 3548 [ + - - + ]:CBC 21829210 : if (field_num < 0 || field_num >= res->numAttributes)
3549 : : {
8111 tgl@sss.pgh.pa.us 3550 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3551 : : "column number %d is out of range 0..%d",
3552 : 0 : field_num, res->numAttributes - 1);
2943 peter_e@gmx.net 3553 : 0 : return false;
3554 : : }
2943 peter_e@gmx.net 3555 :CBC 21829210 : return true;
3556 : : }
3557 : :
3558 : : static int
6959 tgl@sss.pgh.pa.us 3559 :UBC 0 : check_param_number(const PGresult *res, int param_num)
3560 : : {
3561 [ # # ]: 0 : if (!res)
2943 peter_e@gmx.net 3562 : 0 : return false; /* no way to display error message... */
6959 tgl@sss.pgh.pa.us 3563 [ # # # # ]: 0 : if (param_num < 0 || param_num >= res->numParameters)
3564 : : {
3565 : 0 : pqInternalNotice(&res->noticeHooks,
3566 : : "parameter number %d is out of range 0..%d",
3567 : 0 : param_num, res->numParameters - 1);
2943 peter_e@gmx.net 3568 : 0 : return false;
3569 : : }
3570 : :
3571 : 0 : return true;
3572 : : }
3573 : :
3574 : : /*
3575 : : * returns NULL if the field_num is invalid
3576 : : */
3577 : : char *
9431 bruce@momjian.us 3578 :CBC 133998 : PQfname(const PGresult *res, int field_num)
3579 : : {
8819 peter_e@gmx.net 3580 [ - + ]: 133998 : if (!check_field_number(res, field_num))
9890 bruce@momjian.us 3581 :UBC 0 : return NULL;
10226 bruce@momjian.us 3582 [ + - ]:CBC 133998 : if (res->attDescs)
3583 : 133998 : return res->attDescs[field_num].name;
3584 : : else
10226 bruce@momjian.us 3585 :UBC 0 : return NULL;
3586 : : }
3587 : :
3588 : : /*
3589 : : * PQfnumber: find column number given column name
3590 : : *
3591 : : * The column name is parsed as if it were in a SQL statement, including
3592 : : * case-folding and double-quote processing. But note a possible gotcha:
3593 : : * downcasing in the frontend might follow different locale rules than
3594 : : * downcasing in the backend...
3595 : : *
3596 : : * Returns -1 if no match. In the present backend it is also possible
3597 : : * to have multiple matches, in which case the first one is found.
3598 : : */
3599 : : int
9431 bruce@momjian.us 3600 :CBC 261535 : PQfnumber(const PGresult *res, const char *field_name)
3601 : : {
3602 : : char *field_case;
3603 : : bool in_quotes;
4207 sfrost@snowman.net 3604 : 261535 : bool all_lower = true;
3605 : : const char *iptr;
3606 : : char *optr;
3607 : : int i;
3608 : :
10226 bruce@momjian.us 3609 [ + + ]: 261535 : if (!res)
3610 : 18513 : return -1;
3611 : :
3612 : : /*
3613 : : * Note: it is correct to reject a zero-length input string; the proper
3614 : : * input to match a zero-length field name would be "".
3615 : : */
3616 [ + - ]: 243022 : if (field_name == NULL ||
3617 [ + - ]: 243022 : field_name[0] == '\0' ||
3618 [ - + ]: 243022 : res->attDescs == NULL)
10226 bruce@momjian.us 3619 :UBC 0 : return -1;
3620 : :
3621 : : /*
3622 : : * Check if we can avoid the strdup() and related work because the
3623 : : * passed-in string wouldn't be changed before we do the check anyway.
3624 : : */
4207 sfrost@snowman.net 3625 [ + + ]:CBC 2753030 : for (iptr = field_name; *iptr; iptr++)
3626 : : {
3627 : 2510008 : char c = *iptr;
3628 : :
3629 [ + - - + ]: 2510008 : if (c == '"' || c != pg_tolower((unsigned char) c))
3630 : : {
4207 sfrost@snowman.net 3631 :UBC 0 : all_lower = false;
3632 : 0 : break;
3633 : : }
3634 : : }
3635 : :
4207 sfrost@snowman.net 3636 [ + - ]:CBC 243022 : if (all_lower)
3637 [ + - ]: 1852730 : for (i = 0; i < res->numAttributes; i++)
3638 [ + + ]: 1852730 : if (strcmp(field_name, res->attDescs[i].name) == 0)
3639 : 243022 : return i;
3640 : :
3641 : : /* Fall through to the normal check if that didn't work out. */
3642 : :
3643 : : /*
3644 : : * Note: this code will not reject partially quoted strings, eg
3645 : : * foo"BAR"foo will become fooBARfoo when it probably ought to be an error
3646 : : * condition.
3647 : : */
10162 bruce@momjian.us 3648 :UBC 0 : field_case = strdup(field_name);
8008 tgl@sss.pgh.pa.us 3649 [ # # ]: 0 : if (field_case == NULL)
3650 : 0 : return -1; /* grotty */
3651 : :
3652 : 0 : in_quotes = false;
3653 : 0 : optr = field_case;
3654 [ # # ]: 0 : for (iptr = field_case; *iptr; iptr++)
3655 : : {
7678 bruce@momjian.us 3656 : 0 : char c = *iptr;
3657 : :
8008 tgl@sss.pgh.pa.us 3658 [ # # ]: 0 : if (in_quotes)
3659 : : {
3660 [ # # ]: 0 : if (c == '"')
3661 : : {
3662 [ # # ]: 0 : if (iptr[1] == '"')
3663 : : {
3664 : : /* doubled quotes become a single quote */
3665 : 0 : *optr++ = '"';
3666 : 0 : iptr++;
3667 : : }
3668 : : else
3669 : 0 : in_quotes = false;
3670 : : }
3671 : : else
3672 : 0 : *optr++ = c;
3673 : : }
3674 [ # # ]: 0 : else if (c == '"')
3675 : 0 : in_quotes = true;
3676 : : else
3677 : : {
7792 3678 : 0 : c = pg_tolower((unsigned char) c);
8008 3679 : 0 : *optr++ = c;
3680 : : }
3681 : : }
3682 : 0 : *optr = '\0';
3683 : :
10226 bruce@momjian.us 3684 [ # # ]: 0 : for (i = 0; i < res->numAttributes; i++)
3685 : : {
9712 3686 [ # # ]: 0 : if (strcmp(field_case, res->attDescs[i].name) == 0)
3687 : : {
10162 3688 : 0 : free(field_case);
10226 3689 : 0 : return i;
3690 : : }
3691 : : }
10162 3692 : 0 : free(field_case);
10226 3693 : 0 : return -1;
3694 : : }
3695 : :
3696 : : Oid
8113 tgl@sss.pgh.pa.us 3697 : 0 : PQftable(const PGresult *res, int field_num)
3698 : : {
3699 [ # # ]: 0 : if (!check_field_number(res, field_num))
3700 : 0 : return InvalidOid;
3701 [ # # ]: 0 : if (res->attDescs)
3702 : 0 : return res->attDescs[field_num].tableid;
3703 : : else
3704 : 0 : return InvalidOid;
3705 : : }
3706 : :
3707 : : int
3708 : 0 : PQftablecol(const PGresult *res, int field_num)
3709 : : {
3710 [ # # ]: 0 : if (!check_field_number(res, field_num))
3711 : 0 : return 0;
3712 [ # # ]: 0 : if (res->attDescs)
3713 : 0 : return res->attDescs[field_num].columnid;
3714 : : else
3715 : 0 : return 0;
3716 : : }
3717 : :
3718 : : int
8113 tgl@sss.pgh.pa.us 3719 :CBC 4688 : PQfformat(const PGresult *res, int field_num)
3720 : : {
3721 [ - + ]: 4688 : if (!check_field_number(res, field_num))
8113 tgl@sss.pgh.pa.us 3722 :UBC 0 : return 0;
8113 tgl@sss.pgh.pa.us 3723 [ + - ]:CBC 4688 : if (res->attDescs)
3724 : 4688 : return res->attDescs[field_num].format;
3725 : : else
8113 tgl@sss.pgh.pa.us 3726 :UBC 0 : return 0;
3727 : : }
3728 : :
3729 : : Oid
9431 bruce@momjian.us 3730 :CBC 133207 : PQftype(const PGresult *res, int field_num)
3731 : : {
8819 peter_e@gmx.net 3732 [ - + ]: 133207 : if (!check_field_number(res, field_num))
10226 bruce@momjian.us 3733 :UBC 0 : return InvalidOid;
10226 bruce@momjian.us 3734 [ + - ]:CBC 133207 : if (res->attDescs)
9917 3735 : 133207 : return res->attDescs[field_num].typid;
3736 : : else
10226 bruce@momjian.us 3737 :UBC 0 : return InvalidOid;
3738 : : }
3739 : :
3740 : : int
9431 bruce@momjian.us 3741 :CBC 80 : PQfsize(const PGresult *res, int field_num)
3742 : : {
8819 peter_e@gmx.net 3743 [ - + ]: 80 : if (!check_field_number(res, field_num))
9985 bruce@momjian.us 3744 :UBC 0 : return 0;
10226 bruce@momjian.us 3745 [ + - ]:CBC 80 : if (res->attDescs)
9917 3746 : 80 : return res->attDescs[field_num].typlen;
3747 : : else
10226 bruce@momjian.us 3748 :UBC 0 : return 0;
3749 : : }
3750 : :
3751 : : int
9431 bruce@momjian.us 3752 :CBC 149 : PQfmod(const PGresult *res, int field_num)
3753 : : {
8819 peter_e@gmx.net 3754 [ - + ]: 149 : if (!check_field_number(res, field_num))
9985 bruce@momjian.us 3755 :UBC 0 : return 0;
9985 bruce@momjian.us 3756 [ + - ]:CBC 149 : if (res->attDescs)
9917 3757 : 149 : return res->attDescs[field_num].atttypmod;
3758 : : else
9985 bruce@momjian.us 3759 :UBC 0 : return 0;
3760 : : }
3761 : :
3762 : : char *
9343 peter_e@gmx.net 3763 :CBC 305419 : PQcmdStatus(PGresult *res)
3764 : : {
10226 bruce@momjian.us 3765 [ - + ]: 305419 : if (!res)
10226 bruce@momjian.us 3766 :UBC 0 : return NULL;
10226 bruce@momjian.us 3767 :CBC 305419 : return res->cmdStatus;
3768 : : }
3769 : :
3770 : : /*
3771 : : * PQoidStatus -
3772 : : * if the last command was an INSERT, return the oid string
3773 : : * if not, return ""
3774 : : */
3775 : : char *
9431 bruce@momjian.us 3776 :UBC 0 : PQoidStatus(const PGresult *res)
3777 : : {
3778 : : /*
3779 : : * This must be enough to hold the result. Don't laugh, this is better
3780 : : * than what this function used to do.
3781 : : */
3782 : : static char buf[24];
3783 : :
3784 : : size_t len;
3785 : :
4207 sfrost@snowman.net 3786 [ # # # # ]: 0 : if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0)
9882 scrappy@hub.org 3787 : 0 : return "";
3788 : :
9431 bruce@momjian.us 3789 : 0 : len = strspn(res->cmdStatus + 7, "0123456789");
3878 tgl@sss.pgh.pa.us 3790 [ # # ]: 0 : if (len > sizeof(buf) - 1)
3791 : 0 : len = sizeof(buf) - 1;
3792 : 0 : memcpy(buf, res->cmdStatus + 7, len);
8978 3793 : 0 : buf[len] = '\0';
3794 : :
9431 bruce@momjian.us 3795 : 0 : return buf;
3796 : : }
3797 : :
3798 : : /*
3799 : : * PQoidValue -
3800 : : * a perhaps preferable form of the above which just returns
3801 : : * an Oid type
3802 : : */
3803 : : Oid
9431 bruce@momjian.us 3804 :CBC 87117 : PQoidValue(const PGresult *res)
3805 : : {
9278 3806 : 87117 : char *endptr = NULL;
3807 : : unsigned long result;
3808 : :
7266 3809 [ + - ]: 87117 : if (!res ||
3810 [ + + ]: 87117 : strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||
3811 [ + - ]: 15963 : res->cmdStatus[7] < '0' ||
3812 [ - + ]: 15963 : res->cmdStatus[7] > '9')
9278 3813 : 71154 : return InvalidOid;
3814 : :
3815 : 15963 : result = strtoul(res->cmdStatus + 7, &endptr, 10);
3816 : :
7319 3817 [ + - - + : 15963 : if (!endptr || (*endptr != ' ' && *endptr != '\0'))
- - ]
9278 bruce@momjian.us 3818 :UBC 0 : return InvalidOid;
3819 : : else
9278 bruce@momjian.us 3820 :CBC 15963 : return (Oid) result;
3821 : : }
3822 : :
3823 : :
3824 : : /*
3825 : : * PQcmdTuples -
3826 : : * If the last command was INSERT/UPDATE/DELETE/MERGE/MOVE/FETCH/COPY,
3827 : : * return a string containing the number of inserted/affected tuples.
3828 : : * If not, return "".
3829 : : *
3830 : : * XXX: this should probably return an int
3831 : : */
3832 : : char *
9343 peter_e@gmx.net 3833 : 153379 : PQcmdTuples(PGresult *res)
3834 : : {
3835 : : char *p,
3836 : : *c;
3837 : :
10226 bruce@momjian.us 3838 [ + + ]: 153379 : if (!res)
9890 3839 : 244 : return "";
3840 : :
8235 3841 [ + + ]: 153135 : if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
3842 : : {
7127 tgl@sss.pgh.pa.us 3843 : 16767 : p = res->cmdStatus + 7;
3844 : : /* INSERT: skip oid and space */
3845 [ + - + + ]: 33534 : while (*p && *p != ' ')
8235 bruce@momjian.us 3846 : 16767 : p++;
7127 tgl@sss.pgh.pa.us 3847 [ - + ]: 16767 : if (*p == 0)
2999 tgl@sss.pgh.pa.us 3848 :UBC 0 : goto interpret_error; /* no space? */
7127 tgl@sss.pgh.pa.us 3849 :CBC 16767 : p++;
3850 : : }
5681 bruce@momjian.us 3851 [ + + ]: 136368 : else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 ||
3852 [ + + ]: 78201 : strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
8235 3853 [ + + ]: 76794 : strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
7127 tgl@sss.pgh.pa.us 3854 : 61942 : p = res->cmdStatus + 7;
1258 alvherre@alvh.no-ip. 3855 [ + + ]: 74426 : else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0 ||
3856 [ + + ]: 73700 : strncmp(res->cmdStatus, "MERGE ", 6) == 0)
7127 tgl@sss.pgh.pa.us 3857 : 1202 : p = res->cmdStatus + 6;
3858 [ + + ]: 73224 : else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
3859 [ + + ]: 73195 : strncmp(res->cmdStatus, "COPY ", 5) == 0)
8235 bruce@momjian.us 3860 : 564 : p = res->cmdStatus + 5;
3861 : : else
3862 : 72660 : return "";
3863 : :
3864 : : /* check that we have an integer (at least one digit, nothing else) */
7127 tgl@sss.pgh.pa.us 3865 [ + + ]: 170386 : for (c = p; *c; c++)
3866 : : {
3867 [ - + ]: 89911 : if (!isdigit((unsigned char) *c))
7127 tgl@sss.pgh.pa.us 3868 :UBC 0 : goto interpret_error;
3869 : : }
7127 tgl@sss.pgh.pa.us 3870 [ - + ]:CBC 80475 : if (c == p)
7127 tgl@sss.pgh.pa.us 3871 :UBC 0 : goto interpret_error;
3872 : :
8235 bruce@momjian.us 3873 :CBC 80475 : return p;
3874 : :
7127 tgl@sss.pgh.pa.us 3875 :UBC 0 : interpret_error:
3876 : 0 : pqInternalNotice(&res->noticeHooks,
3877 : : "could not interpret result from server: %s",
3878 : 0 : res->cmdStatus);
3879 : 0 : return "";
3880 : : }
3881 : :
3882 : : /*
3883 : : * PQgetvalue:
3884 : : * return the value of field 'field_num' of row 'tup_num'
3885 : : */
3886 : : char *
9431 bruce@momjian.us 3887 :CBC 18454902 : PQgetvalue(const PGresult *res, int tup_num, int field_num)
3888 : : {
8819 peter_e@gmx.net 3889 [ - + ]: 18454902 : if (!check_tuple_field_number(res, tup_num, field_num))
10226 bruce@momjian.us 3890 :UBC 0 : return NULL;
10226 bruce@momjian.us 3891 :CBC 18454902 : return res->tuples[tup_num][field_num].value;
3892 : : }
3893 : :
3894 : : /* PQgetlength:
3895 : : * returns the actual length of a field value in bytes.
3896 : : */
3897 : : int
9431 3898 : 19105 : PQgetlength(const PGresult *res, int tup_num, int field_num)
3899 : : {
8819 peter_e@gmx.net 3900 [ - + ]: 19105 : if (!check_tuple_field_number(res, tup_num, field_num))
9985 bruce@momjian.us 3901 :UBC 0 : return 0;
10226 bruce@momjian.us 3902 [ + + ]:CBC 19105 : if (res->tuples[tup_num][field_num].len != NULL_LEN)
3903 : 18931 : return res->tuples[tup_num][field_num].len;
3904 : : else
3905 : 174 : return 0;
3906 : : }
3907 : :
3908 : : /* PQgetisnull:
3909 : : * returns the null status of a field value.
3910 : : */
3911 : : int
9431 3912 : 3355203 : PQgetisnull(const PGresult *res, int tup_num, int field_num)
3913 : : {
8819 peter_e@gmx.net 3914 [ - + ]: 3355203 : if (!check_tuple_field_number(res, tup_num, field_num))
9985 bruce@momjian.us 3915 :UBC 0 : return 1; /* pretend it is null */
10226 bruce@momjian.us 3916 [ + + ]:CBC 3355203 : if (res->tuples[tup_num][field_num].len == NULL_LEN)
3917 : 531029 : return 1;
3918 : : else
3919 : 2824174 : return 0;
3920 : : }
3921 : :
3922 : : /* PQnparams:
3923 : : * returns the number of input parameters of a prepared statement.
3924 : : */
3925 : : int
6959 tgl@sss.pgh.pa.us 3926 :UBC 0 : PQnparams(const PGresult *res)
3927 : : {
3928 [ # # ]: 0 : if (!res)
3929 : 0 : return 0;
3930 : 0 : return res->numParameters;
3931 : : }
3932 : :
3933 : : /* PQparamtype:
3934 : : * returns type Oid of the specified statement parameter.
3935 : : */
3936 : : Oid
3937 : 0 : PQparamtype(const PGresult *res, int param_num)
3938 : : {
3939 [ # # ]: 0 : if (!check_param_number(res, param_num))
3940 : 0 : return InvalidOid;
3941 [ # # ]: 0 : if (res->paramDescs)
3942 : 0 : return res->paramDescs[param_num].typid;
3943 : : else
3944 : 0 : return InvalidOid;
3945 : : }
3946 : :
3947 : :
3948 : : /* PQsetnonblocking:
3949 : : * sets the PGconn's database connection non-blocking if the arg is true
3950 : : * or makes it blocking if the arg is false, this will not protect
3951 : : * you from PQexec(), you'll only be safe when using the non-blocking API.
3952 : : * Needs to be called only on a connected database connection.
3953 : : */
3954 : : int
9363 bruce@momjian.us 3955 :CBC 5 : PQsetnonblocking(PGconn *conn, int arg)
3956 : : {
3957 : : bool barg;
3958 : :
8113 tgl@sss.pgh.pa.us 3959 [ + - - + ]: 5 : if (!conn || conn->status == CONNECTION_BAD)
8113 tgl@sss.pgh.pa.us 3960 :UBC 0 : return -1;
3961 : :
2943 peter_e@gmx.net 3962 :CBC 5 : barg = (arg ? true : false);
3963 : :
3964 : : /* early out if the socket is already in the state requested */
8060 tgl@sss.pgh.pa.us 3965 [ - + ]: 5 : if (barg == conn->nonblocking)
7178 neilc@samurai.com 3966 :UBC 0 : return 0;
3967 : :
3968 : : /*
3969 : : * to guarantee constancy for flushing/query/result-polling behavior we
3970 : : * need to flush the send queue at this point in order to guarantee proper
3971 : : * behavior. this is ok because either they are making a transition _from_
3972 : : * or _to_ blocking mode, either way we can block them.
3973 : : *
3974 : : * Clear error state in case pqFlush adds to it, unless we're actively
3975 : : * pipelining, in which case it seems best not to.
3976 : : */
1286 tgl@sss.pgh.pa.us 3977 [ + + ]:CBC 5 : if (conn->cmd_queue_head == NULL)
3978 : 4 : pqClearConnErrorState(conn);
3979 : :
3980 : : /* if we are going from blocking to non-blocking flush here */
9357 bruce@momjian.us 3981 [ - + ]: 5 : if (pqFlush(conn))
7178 neilc@samurai.com 3982 :UBC 0 : return -1;
3983 : :
8060 tgl@sss.pgh.pa.us 3984 :CBC 5 : conn->nonblocking = barg;
3985 : :
7178 neilc@samurai.com 3986 : 5 : return 0;
3987 : : }
3988 : :
3989 : : /*
3990 : : * return the blocking status of the database connection
3991 : : * true == nonblocking, false == blocking
3992 : : */
3993 : : int
9363 bruce@momjian.us 3994 : 2 : PQisnonblocking(const PGconn *conn)
3995 : : {
1118 tgl@sss.pgh.pa.us 3996 [ + - - + ]: 2 : if (!conn || conn->status == CONNECTION_BAD)
1118 tgl@sss.pgh.pa.us 3997 :UBC 0 : return false;
7178 neilc@samurai.com 3998 :CBC 2 : return pqIsnonblocking(conn);
3999 : : }
4000 : :
4001 : : /* libpq is thread-safe? */
4002 : : int
7046 bruce@momjian.us 4003 :UBC 0 : PQisthreadsafe(void)
4004 : : {
4005 : 0 : return true;
4006 : : }
4007 : :
4008 : :
4009 : : /* try to force data out, really only useful for non-blocking users */
4010 : : int
9363 bruce@momjian.us 4011 :CBC 159429 : PQflush(PGconn *conn)
4012 : : {
1118 tgl@sss.pgh.pa.us 4013 [ + - - + ]: 159429 : if (!conn || conn->status == CONNECTION_BAD)
1118 tgl@sss.pgh.pa.us 4014 :UBC 0 : return -1;
7435 bruce@momjian.us 4015 :CBC 159429 : return pqFlush(conn);
4016 : : }
4017 : :
4018 : : /*
4019 : : * pqPipelineFlush
4020 : : *
4021 : : * In pipeline mode, data will be flushed only when the out buffer reaches the
4022 : : * threshold value. In non-pipeline mode, it behaves as stock pqFlush.
4023 : : *
4024 : : * Returns 0 on success.
4025 : : */
4026 : : static int
1636 alvherre@alvh.no-ip. 4027 : 11540 : pqPipelineFlush(PGconn *conn)
4028 : : {
4029 [ + + ]: 11540 : if ((conn->pipelineStatus != PQ_PIPELINE_ON) ||
4030 [ - + ]: 2280 : (conn->outCount >= OUTBUFFER_THRESHOLD))
4031 : 9260 : return pqFlush(conn);
4032 : 2280 : return 0;
4033 : : }
4034 : :
4035 : :
4036 : : /*
4037 : : * PQfreemem - safely frees memory allocated
4038 : : *
4039 : : * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
4040 : : * Used for freeing memory from PQescapeBytea()/PQunescapeBytea()
4041 : : */
4042 : : void
8069 bruce@momjian.us 4043 : 2561150 : PQfreemem(void *ptr)
4044 : : {
8157 tgl@sss.pgh.pa.us 4045 : 2561150 : free(ptr);
4046 : 2561150 : }
4047 : :
4048 : : /*
4049 : : * PQfreeNotify - free's the memory associated with a PGnotify
4050 : : *
4051 : : * This function is here only for binary backward compatibility.
4052 : : * New code should use PQfreemem(). A macro will automatically map
4053 : : * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24
4054 : : */
4055 : :
4056 : : #undef PQfreeNotify
4057 : : void PQfreeNotify(PGnotify *notify);
4058 : :
4059 : : void
8201 bruce@momjian.us 4060 :UBC 0 : PQfreeNotify(PGnotify *notify)
4061 : : {
4062 : 0 : PQfreemem(notify);
4063 : 0 : }
4064 : :
4065 : :
4066 : : /*
4067 : : * Escaping arbitrary strings to get valid SQL literal strings.
4068 : : *
4069 : : * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\".
4070 : : *
4071 : : * length is the length of the source string. (Note: if a terminating NUL
4072 : : * is encountered sooner, PQescapeString stops short of "length"; the behavior
4073 : : * is thus rather like strncpy.)
4074 : : *
4075 : : * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
4076 : : * A terminating NUL character is added to the output string, whether the
4077 : : * input is NUL-terminated or not.
4078 : : *
4079 : : * Returns the actual length of the output (not counting the terminating NUL).
4080 : : */
4081 : : static size_t
7048 tgl@sss.pgh.pa.us 4082 :CBC 5486 : PQescapeStringInternal(PGconn *conn,
4083 : : char *to, const char *from, size_t length,
4084 : : int *error,
4085 : : int encoding, bool std_strings)
4086 : : {
8157 4087 : 5486 : const char *source = from;
4088 : 5486 : char *target = to;
208 andres@anarazel.de 4089 : 5486 : size_t remaining = strnlen(from, length);
203 tgl@sss.pgh.pa.us 4090 : 5486 : bool already_complained = false;
4091 : :
7048 4092 [ + + ]: 5486 : if (error)
4093 : 65 : *error = 0;
4094 : :
208 andres@anarazel.de 4095 [ + + ]: 78354 : while (remaining > 0)
4096 : : {
6912 bruce@momjian.us 4097 : 72868 : char c = *source;
4098 : : int charlen;
4099 : : int i;
4100 : :
4101 : : /* Fast path for plain ASCII */
7048 tgl@sss.pgh.pa.us 4102 [ + + ]: 72868 : if (!IS_HIGHBIT_SET(c))
4103 : : {
4104 : : /* Apply quoting if needed */
7041 4105 [ + + + + : 72016 : if (SQL_STR_DOUBLE(c, !std_strings))
- + ]
7048 4106 : 49 : *target++ = c;
4107 : : /* Copy the character */
4108 : 72016 : *target++ = c;
4109 : 72016 : source++;
4110 : 72016 : remaining--;
4111 : 72016 : continue;
4112 : : }
4113 : :
4114 : : /* Slow path for possible multibyte characters */
124 noah@leadboat.com 4115 : 852 : charlen = pg_encoding_mblen_or_incomplete(encoding,
4116 : : source, remaining);
4117 : :
203 tgl@sss.pgh.pa.us 4118 [ + + + + ]: 1648 : if (remaining < charlen ||
4119 : 796 : pg_encoding_verifymbchar(encoding, source, charlen) == -1)
4120 : : {
4121 : : /*
4122 : : * Multibyte character is invalid. It's important to verify that
4123 : : * as invalid multibyte characters could e.g. be used to "skip"
4124 : : * over quote characters, e.g. when parsing
4125 : : * character-by-character.
4126 : : *
4127 : : * Report an error if possible, and replace the character's first
4128 : : * byte with an invalid sequence. The invalid sequence ensures
4129 : : * that the escaped string will trigger an error on the
4130 : : * server-side, even if we can't directly report an error here.
4131 : : *
4132 : : * This isn't *that* crucial when we can report an error to the
4133 : : * caller; but if we can't or the caller ignores it, the caller
4134 : : * will use this string unmodified and it needs to be safe for
4135 : : * parsing.
4136 : : *
4137 : : * We know there's enough space for the invalid sequence because
4138 : : * the "to" buffer needs to be at least 2 * length + 1 long, and
4139 : : * at worst we're replacing a single input byte with two invalid
4140 : : * bytes.
4141 : : *
4142 : : * It would be a bit faster to verify the whole string the first
4143 : : * time we encounter a set highbit, but this way we can replace
4144 : : * just the invalid data, which probably makes it easier for users
4145 : : * to find the invalidly encoded portion of a larger string.
4146 : : */
7048 4147 [ + + ]: 82 : if (error)
4148 : 41 : *error = 1;
203 4149 [ + + + - ]: 82 : if (conn && !already_complained)
4150 : : {
4151 [ + + ]: 41 : if (remaining < charlen)
4152 : 28 : libpq_append_conn_error(conn, "incomplete multibyte character");
4153 : : else
4154 : 13 : libpq_append_conn_error(conn, "invalid multibyte character");
4155 : : /* Issue a complaint only once per string */
4156 : 41 : already_complained = true;
4157 : : }
4158 : :
208 andres@anarazel.de 4159 : 82 : pg_encoding_set_invalid(encoding, target);
4160 : 82 : target += 2;
4161 : :
4162 : : /*
4163 : : * Handle the following bytes as if this byte didn't exist. That's
4164 : : * safer in case the subsequent bytes contain important characters
4165 : : * for the caller (e.g. '>' in html).
4166 : : */
203 tgl@sss.pgh.pa.us 4167 : 82 : source++;
4168 : 82 : remaining--;
4169 : : }
4170 : : else
4171 : : {
4172 : : /* Copy the character */
208 andres@anarazel.de 4173 [ + + ]: 1566 : for (i = 0; i < charlen; i++)
4174 : : {
4175 : 796 : *target++ = *source++;
4176 : 796 : remaining--;
4177 : : }
4178 : : }
4179 : : }
4180 : :
4181 : : /* Write the terminating NUL character. */
8157 tgl@sss.pgh.pa.us 4182 : 5486 : *target = '\0';
4183 : :
4184 : 5486 : return target - to;
4185 : : }
4186 : :
4187 : : size_t
7048 4188 : 5421 : PQescapeStringConn(PGconn *conn,
4189 : : char *to, const char *from, size_t length,
4190 : : int *error)
4191 : : {
4192 [ - + ]: 5421 : if (!conn)
4193 : : {
4194 : : /* force empty-string result */
7048 tgl@sss.pgh.pa.us 4195 :UBC 0 : *to = '\0';
4196 [ # # ]: 0 : if (error)
4197 : 0 : *error = 1;
4198 : 0 : return 0;
4199 : : }
4200 : :
1286 tgl@sss.pgh.pa.us 4201 [ + - ]:CBC 5421 : if (conn->cmd_queue_head == NULL)
4202 : 5421 : pqClearConnErrorState(conn);
4203 : :
7048 4204 : 5421 : return PQescapeStringInternal(conn, to, from, length, error,
4205 : : conn->client_encoding,
4206 : 5421 : conn->std_strings);
4207 : : }
4208 : :
4209 : : size_t
4210 : 65 : PQescapeString(char *to, const char *from, size_t length)
4211 : : {
4212 : 65 : return PQescapeStringInternal(NULL, to, from, length, NULL,
4213 : : static_client_encoding,
4214 : : static_std_strings);
4215 : : }
4216 : :
4217 : :
4218 : : /*
4219 : : * Escape arbitrary strings. If as_ident is true, we escape the result
4220 : : * as an identifier; if false, as a literal. The result is returned in
4221 : : * a newly allocated buffer. If we fail due to an encoding violation or out
4222 : : * of memory condition, we return NULL, storing an error message into conn.
4223 : : */
4224 : : static char *
5707 rhaas@postgresql.org 4225 : 1853 : PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
4226 : : {
4227 : : const char *s;
4228 : : char *result;
4229 : : char *rp;
5671 bruce@momjian.us 4230 : 1853 : int num_quotes = 0; /* single or double, depending on as_ident */
4231 : 1853 : int num_backslashes = 0;
204 andres@anarazel.de 4232 : 1853 : size_t input_len = strnlen(str, len);
4233 : : size_t result_size;
5671 bruce@momjian.us 4234 [ + + ]: 1853 : char quote_char = as_ident ? '"' : '\'';
208 andres@anarazel.de 4235 : 1853 : bool validated_mb = false;
4236 : :
4237 : : /* We must have a connection, else fail immediately. */
5707 rhaas@postgresql.org 4238 [ - + ]: 1853 : if (!conn)
5707 rhaas@postgresql.org 4239 :UBC 0 : return NULL;
4240 : :
1286 tgl@sss.pgh.pa.us 4241 [ + - ]:CBC 1853 : if (conn->cmd_queue_head == NULL)
4242 : 1853 : pqClearConnErrorState(conn);
4243 : :
4244 : : /*
4245 : : * Scan the string for characters that must be escaped and for invalidly
4246 : : * encoded data.
4247 : : */
208 andres@anarazel.de 4248 : 1853 : s = str;
4249 [ + + ]: 483983 : for (size_t remaining = input_len; remaining > 0; remaining--, s++)
4250 : : {
5707 rhaas@postgresql.org 4251 [ + + ]: 482213 : if (*s == quote_char)
4252 : 93 : ++num_quotes;
4253 [ + + ]: 482120 : else if (*s == '\\')
4254 : 154 : ++num_backslashes;
4255 [ + + ]: 481966 : else if (IS_HIGHBIT_SET(*s))
4256 : : {
4257 : : int charlen;
4258 : :
4259 : : /* Slow path for possible multibyte characters */
124 noah@leadboat.com 4260 : 111 : charlen = pg_encoding_mblen_or_incomplete(conn->client_encoding,
4261 : : s, remaining);
4262 : :
208 andres@anarazel.de 4263 [ + + ]: 111 : if (charlen > remaining)
4264 : : {
4265 : : /* Multibyte character overruns allowable length. */
1026 peter@eisentraut.org 4266 : 57 : libpq_append_conn_error(conn, "incomplete multibyte character");
5707 rhaas@postgresql.org 4267 : 57 : return NULL;
4268 : : }
4269 : :
4270 : : /*
4271 : : * If we haven't already, check that multibyte characters are
4272 : : * valid. It's important to verify that as invalid multi-byte
4273 : : * characters could e.g. be used to "skip" over quote characters,
4274 : : * e.g. when parsing character-by-character.
4275 : : *
4276 : : * We check validity once, for the whole remainder of the string,
4277 : : * when we first encounter any multi-byte character. Some
4278 : : * encodings have optimized implementations for longer strings.
4279 : : */
208 andres@anarazel.de 4280 [ + - ]: 54 : if (!validated_mb)
4281 : : {
4282 [ + + ]: 54 : if (pg_encoding_verifymbstr(conn->client_encoding, s, remaining)
4283 : : != remaining)
4284 : : {
4285 : 26 : libpq_append_conn_error(conn, "invalid multibyte character");
4286 : 26 : return NULL;
4287 : : }
4288 : 28 : validated_mb = true;
4289 : : }
4290 : :
4291 : : /* Adjust s, bearing in mind that for loop will increment it. */
5707 rhaas@postgresql.org 4292 : 28 : s += charlen - 1;
208 andres@anarazel.de 4293 : 28 : remaining -= charlen - 1;
4294 : : }
4295 : : }
4296 : :
4297 : : /* Allocate output buffer. */
5671 bruce@momjian.us 4298 : 1770 : result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */
5707 rhaas@postgresql.org 4299 [ + + + + ]: 1770 : if (!as_ident && num_backslashes > 0)
4300 : 28 : result_size += num_backslashes + 2;
4301 : 1770 : result = rp = (char *) malloc(result_size);
4302 [ - + ]: 1770 : if (rp == NULL)
4303 : : {
1026 peter@eisentraut.org 4304 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
5707 rhaas@postgresql.org 4305 : 0 : return NULL;
4306 : : }
4307 : :
4308 : : /*
4309 : : * If we are escaping a literal that contains backslashes, we use the
4310 : : * escape string syntax so that the result is correct under either value
4311 : : * of standard_conforming_strings. We also emit a leading space in this
4312 : : * case, to guard against the possibility that the result might be
4313 : : * interpolated immediately following an identifier.
4314 : : */
5707 rhaas@postgresql.org 4315 [ + + + + ]:CBC 1770 : if (!as_ident && num_backslashes > 0)
4316 : : {
4317 : 28 : *rp++ = ' ';
4318 : 28 : *rp++ = 'E';
4319 : : }
4320 : :
4321 : : /* Opening quote. */
4322 : 1770 : *rp++ = quote_char;
4323 : :
4324 : : /*
4325 : : * Use fast path if possible.
4326 : : *
4327 : : * We've already verified that the input string is well-formed in the
4328 : : * current encoding. If it contains no quotes and, in the case of
4329 : : * literal-escaping, no backslashes, then we can just copy it directly to
4330 : : * the output buffer, adding the necessary quotes.
4331 : : *
4332 : : * If not, we must rescan the input and process each character
4333 : : * individually.
4334 : : */
4335 [ + + + + : 1770 : if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
+ + ]
4336 : : {
4337 : 1718 : memcpy(rp, str, input_len);
4338 : 1718 : rp += input_len;
4339 : : }
4340 : : else
4341 : : {
208 andres@anarazel.de 4342 : 52 : s = str;
4343 [ + + ]: 3653 : for (size_t remaining = input_len; remaining > 0; remaining--, s++)
4344 : : {
5707 rhaas@postgresql.org 4345 [ + + + + : 3601 : if (*s == quote_char || (!as_ident && *s == '\\'))
+ + ]
4346 : : {
4347 : 225 : *rp++ = *s;
4348 : 225 : *rp++ = *s;
4349 : : }
4350 [ + + ]: 3376 : else if (!IS_HIGHBIT_SET(*s))
4351 : 3365 : *rp++ = *s;
4352 : : else
4353 : : {
5671 bruce@momjian.us 4354 : 11 : int i = pg_encoding_mblen(conn->client_encoding, s);
4355 : :
4356 : : while (1)
4357 : : {
5707 rhaas@postgresql.org 4358 : 21 : *rp++ = *s;
4359 [ + + ]: 21 : if (--i == 0)
4360 : 11 : break;
208 andres@anarazel.de 4361 : 10 : remaining--;
5671 bruce@momjian.us 4362 : 10 : ++s; /* for loop will provide the final increment */
4363 : : }
4364 : : }
4365 : : }
4366 : : }
4367 : :
4368 : : /* Closing quote and terminating NUL. */
5707 rhaas@postgresql.org 4369 : 1770 : *rp++ = quote_char;
4370 : 1770 : *rp = '\0';
4371 : :
4372 : 1770 : return result;
4373 : : }
4374 : :
4375 : : char *
4376 : 1031 : PQescapeLiteral(PGconn *conn, const char *str, size_t len)
4377 : : {
4378 : 1031 : return PQescapeInternal(conn, str, len, false);
4379 : : }
4380 : :
4381 : : char *
4382 : 822 : PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
4383 : : {
4384 : 822 : return PQescapeInternal(conn, str, len, true);
4385 : : }
4386 : :
4387 : : /* HEX encoding support for bytea */
4388 : : static const char hextbl[] = "0123456789abcdef";
4389 : :
4390 : : static const int8 hexlookup[128] = {
4391 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4392 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4393 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4394 : : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4395 : : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4396 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4397 : : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4398 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4399 : : };
4400 : :
4401 : : static inline char
5877 tgl@sss.pgh.pa.us 4402 :UBC 0 : get_hex(char c)
4403 : : {
4404 : 0 : int res = -1;
4405 : :
4406 [ # # # # ]: 0 : if (c > 0 && c < 127)
4407 : 0 : res = hexlookup[(unsigned char) c];
4408 : :
4409 : 0 : return (char) res;
4410 : : }
4411 : :
4412 : :
4413 : : /*
4414 : : * PQescapeBytea - converts from binary string to the
4415 : : * minimal encoding necessary to include the string in an SQL
4416 : : * INSERT statement with a bytea type column as the target.
4417 : : *
4418 : : * We can use either hex or escape (traditional) encoding.
4419 : : * In escape mode, the following transformations are applied:
4420 : : * '\0' == ASCII 0 == \000
4421 : : * '\'' == ASCII 39 == ''
4422 : : * '\\' == ASCII 92 == \\
4423 : : * anything < 0x20, or > 0x7e ---> \ooo
4424 : : * (where ooo is an octal expression)
4425 : : *
4426 : : * If not std_strings, all backslashes sent to the output are doubled.
4427 : : */
4428 : : static unsigned char *
7048 4429 : 0 : PQescapeByteaInternal(PGconn *conn,
4430 : : const unsigned char *from, size_t from_length,
4431 : : size_t *to_length, bool std_strings, bool use_hex)
4432 : : {
4433 : : const unsigned char *vp;
4434 : : unsigned char *rp;
4435 : : unsigned char *result;
4436 : : size_t i;
4437 : : size_t len;
4438 [ # # ]: 0 : size_t bslash_len = (std_strings ? 1 : 2);
4439 : :
4440 : : /*
4441 : : * empty string has 1 char ('\0')
4442 : : */
8157 4443 : 0 : len = 1;
4444 : :
5877 4445 [ # # ]: 0 : if (use_hex)
4446 : : {
4447 : 0 : len += bslash_len + 1 + 2 * from_length;
4448 : : }
4449 : : else
4450 : : {
4451 : 0 : vp = from;
4452 [ # # ]: 0 : for (i = from_length; i > 0; i--, vp++)
4453 : : {
4454 [ # # # # ]: 0 : if (*vp < 0x20 || *vp > 0x7e)
4455 : 0 : len += bslash_len + 3;
4456 [ # # ]: 0 : else if (*vp == '\'')
4457 : 0 : len += 2;
4458 [ # # ]: 0 : else if (*vp == '\\')
4459 : 0 : len += bslash_len + bslash_len;
4460 : : else
4461 : 0 : len++;
4462 : : }
4463 : : }
4464 : :
7048 4465 : 0 : *to_length = len;
8157 4466 : 0 : rp = result = (unsigned char *) malloc(len);
4467 [ # # ]: 0 : if (rp == NULL)
4468 : : {
7048 4469 [ # # ]: 0 : if (conn)
1026 peter@eisentraut.org 4470 : 0 : libpq_append_conn_error(conn, "out of memory");
8157 tgl@sss.pgh.pa.us 4471 : 0 : return NULL;
4472 : : }
4473 : :
5877 4474 [ # # ]: 0 : if (use_hex)
4475 : : {
4476 [ # # ]: 0 : if (!std_strings)
4477 : 0 : *rp++ = '\\';
4478 : 0 : *rp++ = '\\';
4479 : 0 : *rp++ = 'x';
4480 : : }
4481 : :
7048 4482 : 0 : vp = from;
4483 [ # # ]: 0 : for (i = from_length; i > 0; i--, vp++)
4484 : : {
5877 4485 : 0 : unsigned char c = *vp;
4486 : :
4487 [ # # ]: 0 : if (use_hex)
4488 : : {
4489 : 0 : *rp++ = hextbl[(c >> 4) & 0xF];
4490 : 0 : *rp++ = hextbl[c & 0xF];
4491 : : }
4492 [ # # # # ]: 0 : else if (c < 0x20 || c > 0x7e)
4493 : : {
7048 4494 [ # # ]: 0 : if (!std_strings)
4495 : 0 : *rp++ = '\\';
6205 4496 : 0 : *rp++ = '\\';
5877 4497 : 0 : *rp++ = (c >> 6) + '0';
4498 : 0 : *rp++ = ((c >> 3) & 07) + '0';
4499 : 0 : *rp++ = (c & 07) + '0';
4500 : : }
4501 [ # # ]: 0 : else if (c == '\'')
4502 : : {
7048 4503 : 0 : *rp++ = '\'';
4504 : 0 : *rp++ = '\'';
4505 : : }
5877 4506 [ # # ]: 0 : else if (c == '\\')
4507 : : {
7048 4508 [ # # ]: 0 : if (!std_strings)
4509 : : {
4510 : 0 : *rp++ = '\\';
4511 : 0 : *rp++ = '\\';
4512 : : }
4513 : 0 : *rp++ = '\\';
4514 : 0 : *rp++ = '\\';
4515 : : }
4516 : : else
5877 4517 : 0 : *rp++ = c;
4518 : : }
8157 4519 : 0 : *rp = '\0';
4520 : :
4521 : 0 : return result;
4522 : : }
4523 : :
4524 : : unsigned char *
7048 4525 : 0 : PQescapeByteaConn(PGconn *conn,
4526 : : const unsigned char *from, size_t from_length,
4527 : : size_t *to_length)
4528 : : {
4529 [ # # ]: 0 : if (!conn)
4530 : 0 : return NULL;
4531 : :
1286 4532 [ # # ]: 0 : if (conn->cmd_queue_head == NULL)
4533 : 0 : pqClearConnErrorState(conn);
4534 : :
7048 4535 : 0 : return PQescapeByteaInternal(conn, from, from_length, to_length,
5877 4536 : 0 : conn->std_strings,
5680 4537 : 0 : (conn->sversion >= 90000));
4538 : : }
4539 : :
4540 : : unsigned char *
7048 4541 : 0 : PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
4542 : : {
4543 : 0 : return PQescapeByteaInternal(NULL, from, from_length, to_length,
4544 : : static_std_strings,
4545 : : false /* can't use hex */ );
4546 : : }
4547 : :
4548 : :
4549 : : #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
4550 : : #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
4551 : : #define OCTVAL(CH) ((CH) - '0')
4552 : :
4553 : : /*
4554 : : * PQunescapeBytea - converts the null terminated string representation
4555 : : * of a bytea, strtext, into binary, filling a buffer. It returns a
4556 : : * pointer to the buffer (or NULL on error), and the size of the
4557 : : * buffer in retbuflen. The pointer may subsequently be used as an
4558 : : * argument to the function PQfreemem.
4559 : : *
4560 : : * The following transformations are made:
4561 : : * \\ == ASCII 92 == \
4562 : : * \ooo == a byte whose value = ooo (ooo is an octal number)
4563 : : * \x == x (x is any character not matched by the above transformations)
4564 : : */
4565 : : unsigned char *
8157 4566 : 0 : PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
4567 : : {
4568 : : size_t strtextlen,
4569 : : buflen;
4570 : : unsigned char *buffer,
4571 : : *tmpbuf;
4572 : : size_t i,
4573 : : j;
4574 : :
8069 bruce@momjian.us 4575 [ # # ]: 0 : if (strtext == NULL)
8157 tgl@sss.pgh.pa.us 4576 : 0 : return NULL;
4577 : :
7287 4578 : 0 : strtextlen = strlen((const char *) strtext);
4579 : :
5877 4580 [ # # # # ]: 0 : if (strtext[0] == '\\' && strtext[1] == 'x')
4581 : 0 : {
4582 : : const unsigned char *s;
4583 : : unsigned char *p;
4584 : :
5671 bruce@momjian.us 4585 : 0 : buflen = (strtextlen - 2) / 2;
4586 : : /* Avoid unportable malloc(0) */
5877 tgl@sss.pgh.pa.us 4587 [ # # ]: 0 : buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1);
4588 [ # # ]: 0 : if (buffer == NULL)
4589 : 0 : return NULL;
4590 : :
4591 : 0 : s = strtext + 2;
4592 : 0 : p = buffer;
4593 [ # # ]: 0 : while (*s)
4594 : : {
4595 : : char v1,
4596 : : v2;
4597 : :
4598 : : /*
4599 : : * Bad input is silently ignored. Note that this includes
4600 : : * whitespace between hex pairs, which is allowed by byteain.
4601 : : */
4602 : 0 : v1 = get_hex(*s++);
4603 [ # # # # ]: 0 : if (!*s || v1 == (char) -1)
4604 : 0 : continue;
4605 : 0 : v2 = get_hex(*s++);
4606 [ # # ]: 0 : if (v2 != (char) -1)
4607 : 0 : *p++ = (v1 << 4) | v2;
4608 : : }
4609 : :
4610 : 0 : buflen = p - buffer;
4611 : : }
4612 : : else
4613 : : {
4614 : : /*
4615 : : * Length of input is max length of output, but add one to avoid
4616 : : * unportable malloc(0) if input is zero-length.
4617 : : */
5671 bruce@momjian.us 4618 : 0 : buffer = (unsigned char *) malloc(strtextlen + 1);
4619 [ # # ]: 0 : if (buffer == NULL)
4620 : 0 : return NULL;
4621 : :
4622 [ # # ]: 0 : for (i = j = 0; i < strtextlen;)
4623 : : {
4624 [ # # ]: 0 : switch (strtext[i])
4625 : : {
4626 : 0 : case '\\':
4627 : 0 : i++;
4628 [ # # ]: 0 : if (strtext[i] == '\\')
4629 : 0 : buffer[j++] = strtext[i++];
4630 : : else
4631 : : {
4632 [ # # # # ]: 0 : if ((ISFIRSTOCTDIGIT(strtext[i])) &&
4633 [ # # # # ]: 0 : (ISOCTDIGIT(strtext[i + 1])) &&
4634 [ # # # # ]: 0 : (ISOCTDIGIT(strtext[i + 2])))
4635 : : {
4636 : : int byte;
4637 : :
4638 : 0 : byte = OCTVAL(strtext[i++]);
5263 4639 : 0 : byte = (byte << 3) + OCTVAL(strtext[i++]);
4640 : 0 : byte = (byte << 3) + OCTVAL(strtext[i++]);
5671 4641 : 0 : buffer[j++] = byte;
4642 : : }
4643 : : }
4644 : :
4645 : : /*
4646 : : * Note: if we see '\' followed by something that isn't a
4647 : : * recognized escape sequence, we loop around having done
4648 : : * nothing except advance i. Therefore the something will
4649 : : * be emitted as ordinary data on the next cycle. Corner
4650 : : * case: '\' at end of string will just be discarded.
4651 : : */
4652 : 0 : break;
4653 : :
4654 : 0 : default:
4655 : 0 : buffer[j++] = strtext[i++];
4656 : 0 : break;
4657 : : }
4658 : : }
4659 : 0 : buflen = j; /* buflen is the length of the dequoted data */
4660 : : }
4661 : :
4662 : : /* Shrink the buffer to be no larger than necessary */
4663 : : /* +1 avoids unportable behavior when buflen==0 */
7981 tgl@sss.pgh.pa.us 4664 : 0 : tmpbuf = realloc(buffer, buflen + 1);
4665 : :
4666 : : /* It would only be a very brain-dead realloc that could fail, but... */
8122 bruce@momjian.us 4667 [ # # ]: 0 : if (!tmpbuf)
4668 : : {
4669 : 0 : free(buffer);
8010 tgl@sss.pgh.pa.us 4670 : 0 : return NULL;
4671 : : }
4672 : :
8157 4673 : 0 : *retbuflen = buflen;
8122 bruce@momjian.us 4674 : 0 : return tmpbuf;
4675 : : }
|