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-2024, 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 *
9355 bruce@momjian.us 159 :CBC 1186805 : PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
160 : : {
161 : : PGresult *result;
162 : :
9716 163 : 1186805 : result = (PGresult *) malloc(sizeof(PGresult));
6881 neilc@samurai.com 164 [ - + ]: 1186805 : if (!result)
6881 neilc@samurai.com 165 :UBC 0 : return NULL;
166 : :
9716 bruce@momjian.us 167 :CBC 1186805 : result->ntups = 0;
168 : 1186805 : result->numAttributes = 0;
169 : 1186805 : result->attDescs = NULL;
170 : 1186805 : result->tuples = NULL;
171 : 1186805 : result->tupArrSize = 0;
6449 tgl@sss.pgh.pa.us 172 : 1186805 : result->numParameters = 0;
173 : 1186805 : result->paramDescs = NULL;
9716 bruce@momjian.us 174 : 1186805 : result->resultStatus = status;
175 : 1186805 : result->cmdStatus[0] = '\0';
176 : 1186805 : result->binary = 0;
5688 tgl@sss.pgh.pa.us 177 : 1186805 : result->events = NULL;
178 : 1186805 : result->nEvents = 0;
9327 179 : 1186805 : result->errMsg = NULL;
7603 180 : 1186805 : result->errFields = NULL;
2933 181 : 1186805 : result->errQuery = NULL;
9279 182 : 1186805 : result->null_field[0] = '\0';
183 : 1186805 : result->curBlock = NULL;
184 : 1186805 : result->curOffset = 0;
185 : 1186805 : result->spaceLeft = 0;
2042 186 : 1186805 : result->memorySize = sizeof(PGresult);
187 : :
8800 188 [ + + ]: 1186805 : if (conn)
189 : : {
190 : : /* copy connection data we might need for operations on PGresult */
7603 191 : 384172 : result->noticeHooks = conn->noticeHooks;
8797 192 : 384172 : result->client_encoding = conn->client_encoding;
193 : :
194 : : /* consider copying conn's errorMessage */
9327 195 [ + + ]: 384172 : switch (status)
196 : : {
197 : 383923 : 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 : 383923 : break;
207 : 249 : default:
208 : : /* we intentionally do not use or modify errorReported here */
786 209 : 249 : pqSetResultError(result, &conn->errorMessage, 0);
9327 210 : 249 : break;
211 : : }
212 : :
213 : : /* copy events last; result must be valid if we need to PQclear */
5688 214 [ - + ]: 384172 : if (conn->nEvents > 0)
215 : : {
2042 tgl@sss.pgh.pa.us 216 :UBC 0 : result->events = dupEvents(conn->events, conn->nEvents,
217 : : &result->memorySize);
5688 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... */
7603 tgl@sss.pgh.pa.us 229 :CBC 802633 : result->noticeHooks.noticeRec = NULL;
230 : 802633 : result->noticeHooks.noticeRecArg = NULL;
231 : 802633 : result->noticeHooks.noticeProc = NULL;
232 : 802633 : result->noticeHooks.noticeProcArg = NULL;
233 : 802633 : result->client_encoding = PG_SQL_ASCII;
234 : : }
235 : :
9716 bruce@momjian.us 236 : 1186805 : 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
5688 tgl@sss.pgh.pa.us 249 : 2616 : PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
250 : : {
251 : : int i;
252 : :
253 : : /* Fail if argument is NULL or OOM_result */
786 254 [ + - - + ]: 2616 : if (!res || (const PGresult *) res == &OOM_result)
786 tgl@sss.pgh.pa.us 255 :UBC 0 : return false;
256 : :
257 : : /* If attrs already exist, they cannot be overwritten. */
786 tgl@sss.pgh.pa.us 258 [ - + ]:CBC 2616 : if (res->numAttributes > 0)
2433 peter_e@gmx.net 259 :UBC 0 : return false;
260 : :
261 : : /* ignore no-op request */
5688 tgl@sss.pgh.pa.us 262 [ + - - + ]:CBC 2616 : if (numAttributes <= 0 || !attDescs)
2433 peter_e@gmx.net 263 :UBC 0 : return true;
264 : :
5688 tgl@sss.pgh.pa.us 265 :CBC 2616 : res->attDescs = (PGresAttDesc *)
266 : 2616 : PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
267 : :
268 [ - + ]: 2616 : if (!res->attDescs)
2433 peter_e@gmx.net 269 :UBC 0 : return false;
270 : :
5688 tgl@sss.pgh.pa.us 271 :CBC 2616 : res->numAttributes = numAttributes;
272 : 2616 : memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
273 : :
274 : : /* deep-copy the attribute names, and determine format */
275 : 2616 : res->binary = 1;
276 [ + + ]: 10356 : for (i = 0; i < res->numAttributes; i++)
277 : : {
278 [ + - ]: 7740 : if (res->attDescs[i].name)
279 : 7740 : res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
280 : : else
5688 tgl@sss.pgh.pa.us 281 :UBC 0 : res->attDescs[i].name = res->null_field;
282 : :
5688 tgl@sss.pgh.pa.us 283 [ - + ]:CBC 7740 : if (!res->attDescs[i].name)
2433 peter_e@gmx.net 284 :UBC 0 : return false;
285 : :
5688 tgl@sss.pgh.pa.us 286 [ + + ]:CBC 7740 : if (res->attDescs[i].format == 0)
287 : 459 : res->binary = 0;
288 : : }
289 : :
2433 peter_e@gmx.net 290 : 2616 : 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 *
5688 tgl@sss.pgh.pa.us 318 : 2616 : PQcopyResult(const PGresult *src, int flags)
319 : : {
320 : : PGresult *dest;
321 : : int i;
322 : :
323 [ - + ]: 2616 : if (!src)
5688 tgl@sss.pgh.pa.us 324 :UBC 0 : return NULL;
325 : :
5688 tgl@sss.pgh.pa.us 326 :CBC 2616 : dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);
327 [ - + ]: 2616 : if (!dest)
5688 tgl@sss.pgh.pa.us 328 :UBC 0 : return NULL;
329 : :
330 : : /* Always copy these over. Is cmdStatus really useful here? */
5688 tgl@sss.pgh.pa.us 331 :CBC 2616 : dest->client_encoding = src->client_encoding;
332 : 2616 : strcpy(dest->cmdStatus, src->cmdStatus);
333 : :
334 : : /* Wants attrs? */
335 [ + - ]: 2616 : if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES))
336 : : {
337 [ - + ]: 2616 : if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
338 : : {
5688 tgl@sss.pgh.pa.us 339 :UBC 0 : PQclear(dest);
340 : 0 : return NULL;
341 : : }
342 : : }
343 : :
344 : : /* Wants to copy tuples? */
5688 tgl@sss.pgh.pa.us 345 [ - + ]:CBC 2616 : if (flags & PG_COPYRES_TUPLES)
346 : : {
347 : : int tup,
348 : : field;
349 : :
5688 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? */
5688 tgl@sss.pgh.pa.us 366 [ + - ]:CBC 2616 : if (flags & PG_COPYRES_NOTICEHOOKS)
367 : 2616 : dest->noticeHooks = src->noticeHooks;
368 : :
369 : : /* Wants to copy PGEvents? */
370 [ + - - + ]: 2616 : if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
371 : : {
2042 tgl@sss.pgh.pa.us 372 :UBC 0 : dest->events = dupEvents(src->events, src->nEvents,
373 : : &dest->memorySize);
5688 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 */
5688 tgl@sss.pgh.pa.us 383 [ - + ]:CBC 2616 : for (i = 0; i < dest->nEvents; i++)
384 : : {
385 : : /* We don't fire events that had some previous failure */
5686 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;
786 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 : :
5688 tgl@sss.pgh.pa.us 398 :CBC 2616 : 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 *
2042 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 : :
5688 414 [ # # # # ]: 0 : if (!events || count <= 0)
415 : 0 : return NULL;
416 : :
2042 417 : 0 : msize = count * sizeof(PGEvent);
418 : 0 : newEvents = (PGEvent *) malloc(msize);
5688 419 [ # # ]: 0 : if (!newEvents)
420 : 0 : return NULL;
421 : :
422 [ # # ]: 0 : for (i = 0; i < count; i++)
423 : : {
5686 424 : 0 : newEvents[i].proc = events[i].proc;
425 : 0 : newEvents[i].passThrough = events[i].passThrough;
5688 426 : 0 : newEvents[i].data = NULL;
2433 peter_e@gmx.net 427 : 0 : newEvents[i].resultInitialized = false;
5686 tgl@sss.pgh.pa.us 428 : 0 : newEvents[i].name = strdup(events[i].name);
5688 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 : : }
2042 436 : 0 : msize += strlen(events[i].name) + 1;
437 : : }
438 : :
439 : 0 : *memSize += msize;
5688 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;
2420 455 : 0 : const char *errmsg = NULL;
456 : :
457 : : /* Fail if argument is NULL or OOM_result */
786 458 [ # # # # ]: 0 : if (!res || (const PGresult *) res == &OOM_result)
459 : 0 : return false;
460 : :
461 : : /* Invalid field_num? */
5688 462 [ # # ]: 0 : if (!check_field_number(res, field_num))
2433 peter_e@gmx.net 463 : 0 : return false;
464 : :
465 : : /* Invalid tup_num, must be <= ntups */
5688 tgl@sss.pgh.pa.us 466 [ # # # # ]: 0 : if (tup_num < 0 || tup_num > res->ntups)
467 : : {
2420 468 : 0 : pqInternalNotice(&res->noticeHooks,
469 : : "row number %d is out of range 0..%d",
470 : : tup_num, res->ntups);
2433 peter_e@gmx.net 471 : 0 : return false;
472 : : }
473 : :
474 : : /* need to allocate a new tuple? */
4651 tgl@sss.pgh.pa.us 475 [ # # ]: 0 : if (tup_num == res->ntups)
476 : : {
477 : : PGresAttValue *tup;
478 : : int i;
479 : :
480 : : tup = (PGresAttValue *)
5688 481 : 0 : pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
482 : : true);
483 : :
484 [ # # ]: 0 : if (!tup)
2420 485 : 0 : goto fail;
486 : :
487 : : /* initialize each column to NULL */
5688 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 */
2420 495 [ # # ]: 0 : if (!pqAddTuple(res, tup, &errmsg))
496 : 0 : goto fail;
497 : : }
498 : :
5688 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 : : {
2433 peter_e@gmx.net 514 : 0 : attval->value = (char *) pqResultAlloc(res, len + 1, true);
5688 tgl@sss.pgh.pa.us 515 [ # # ]: 0 : if (!attval->value)
2420 516 : 0 : goto fail;
5688 517 : 0 : attval->len = len;
518 : 0 : memcpy(attval->value, value, len);
519 : 0 : attval->value[len] = '\0';
520 : : }
521 : :
2433 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 : : */
2420 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 : :
2433 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 *
5688 tgl@sss.pgh.pa.us 543 :CBC 2616 : PQresultAlloc(PGresult *res, size_t nBytes)
544 : : {
545 : : /* Fail if argument is NULL or OOM_result */
786 546 [ + - - + ]: 2616 : if (!res || (const PGresult *) res == &OOM_result)
786 tgl@sss.pgh.pa.us 547 :UBC 0 : return NULL;
548 : :
2433 peter_e@gmx.net 549 :CBC 2616 : 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 *
8921 bruce@momjian.us 563 : 19721704 : pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
564 : : {
565 : : char *space;
566 : : PGresult_data *block;
567 : :
9091 568 [ - + ]: 19721704 : if (!res)
9279 tgl@sss.pgh.pa.us 569 :UBC 0 : return NULL;
570 : :
9279 tgl@sss.pgh.pa.us 571 [ + + ]:CBC 19721704 : if (nBytes <= 0)
572 : 112 : return res->null_field;
573 : :
574 : : /*
575 : : * If alignment is needed, round up the current position to an alignment
576 : : * boundary.
577 : : */
578 [ + + ]: 19721592 : if (isBinary)
579 : : {
9091 bruce@momjian.us 580 : 4369983 : int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;
581 : :
9279 tgl@sss.pgh.pa.us 582 [ + + ]: 4369983 : if (offset)
583 : : {
584 : 3431066 : res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;
585 : 3431066 : res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset;
586 : : }
587 : : }
588 : :
589 : : /* If there's enough space in the current block, no problem. */
8207 bruce@momjian.us 590 [ + + ]: 19721592 : if (nBytes <= (size_t) res->spaceLeft)
591 : : {
9279 tgl@sss.pgh.pa.us 592 : 19307473 : space = res->curBlock->space + res->curOffset;
593 : 19307473 : res->curOffset += nBytes;
594 : 19307473 : res->spaceLeft -= nBytes;
595 : 19307473 : 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 [ + + ]: 414119 : if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)
605 : : {
2042 606 : 2643 : size_t alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD;
607 : :
608 : 2643 : block = (PGresult_data *) malloc(alloc_size);
9091 bruce@momjian.us 609 [ - + ]: 2643 : if (!block)
9279 tgl@sss.pgh.pa.us 610 :UBC 0 : return NULL;
2042 tgl@sss.pgh.pa.us 611 :CBC 2643 : res->memorySize += alloc_size;
9104 612 : 2643 : space = block->space + PGRESULT_BLOCK_OVERHEAD;
9279 613 [ + + ]: 2643 : 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 : 2452 : block->next = res->curBlock->next;
620 : 2452 : res->curBlock->next = block;
621 : : }
622 : : else
623 : : {
624 : : /* Must set up the new block as the first active block. */
625 : 191 : block->next = NULL;
626 : 191 : res->curBlock = block;
9091 bruce@momjian.us 627 : 191 : res->spaceLeft = 0; /* be sure it's marked full */
628 : : }
9279 tgl@sss.pgh.pa.us 629 : 2643 : return space;
630 : : }
631 : :
632 : : /* Otherwise, start a new block. */
633 : 411476 : block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);
9091 bruce@momjian.us 634 [ - + ]: 411476 : if (!block)
9279 tgl@sss.pgh.pa.us 635 :UBC 0 : return NULL;
2042 tgl@sss.pgh.pa.us 636 :CBC 411476 : res->memorySize += PGRESULT_DATA_BLOCKSIZE;
9279 637 : 411476 : block->next = res->curBlock;
638 : 411476 : res->curBlock = block;
639 [ + + ]: 411476 : if (isBinary)
640 : : {
641 : : /* object needs full alignment */
9104 642 : 372441 : res->curOffset = PGRESULT_BLOCK_OVERHEAD;
643 : 372441 : res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;
644 : : }
645 : : else
646 : : {
647 : : /* we can cram it right after the overhead pointer */
9279 648 : 39035 : res->curOffset = sizeof(PGresult_data);
649 : 39035 : res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data);
650 : : }
651 : :
652 : 411476 : space = block->space + res->curOffset;
653 : 411476 : res->curOffset += nBytes;
654 : 411476 : res->spaceLeft -= nBytes;
655 : 411476 : return space;
656 : : }
657 : :
658 : : /*
659 : : * PQresultMemorySize -
660 : : * Returns total space allocated for the PGresult.
661 : : */
662 : : size_t
2042 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 *
9279 tgl@sss.pgh.pa.us 675 :CBC 458259 : pqResultStrdup(PGresult *res, const char *str)
676 : : {
2433 peter_e@gmx.net 677 : 458259 : char *space = (char *) pqResultAlloc(res, strlen(str) + 1, false);
678 : :
9279 tgl@sss.pgh.pa.us 679 [ + - ]: 458259 : if (space)
680 : 458259 : strcpy(space, str);
681 : 458259 : 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
786 692 : 20574 : pqSetResultError(PGresult *res, PQExpBuffer errorMessage, int offset)
693 : : {
694 : : char *msg;
695 : :
9327 696 [ - + ]: 20574 : if (!res)
9327 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 : : */
990 tgl@sss.pgh.pa.us 706 [ + - + - ]:CBC 20574 : if (!PQExpBufferBroken(errorMessage))
786 707 : 20574 : msg = pqResultStrdup(res, errorMessage->data + offset);
708 : : else
990 tgl@sss.pgh.pa.us 709 :UBC 0 : msg = NULL;
990 tgl@sss.pgh.pa.us 710 [ + - ]:CBC 20574 : if (msg)
711 : 20574 : res->errMsg = msg;
712 : : else
990 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
9475 bruce@momjian.us 721 :CBC 1955720 : PQclear(PGresult *res)
722 : : {
723 : : PGresult_data *block;
724 : : int i;
725 : :
726 : : /* As a convenience, do nothing for a NULL pointer */
727 [ + + ]: 1955720 : if (!res)
728 : 770848 : return;
729 : : /* Also, do nothing if the argument is OOM_result */
786 tgl@sss.pgh.pa.us 730 [ - + ]: 1184872 : if ((const PGresult *) res == &OOM_result)
786 tgl@sss.pgh.pa.us 731 :UBC 0 : return;
732 : :
733 : : /* Close down any events we may have */
5688 tgl@sss.pgh.pa.us 734 [ - + ]:CBC 1184872 : for (i = 0; i < res->nEvents; i++)
735 : : {
736 : : /* only send DESTROY to successfully-initialized event procs */
5686 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 : : }
5688 745 : 0 : free(res->events[i].name);
746 : : }
747 : :
668 peter@eisentraut.org 748 :CBC 1184872 : free(res->events);
749 : :
750 : : /* Free all the subsidiary blocks */
9091 bruce@momjian.us 751 [ + + ]: 1597000 : while ((block = res->curBlock) != NULL)
752 : : {
9279 tgl@sss.pgh.pa.us 753 : 412128 : res->curBlock = block->next;
754 : 412128 : free(block);
755 : : }
756 : :
757 : : /* Free the top-level tuple pointer array */
668 peter@eisentraut.org 758 : 1184872 : free(res->tuples);
759 : :
760 : : /* zero out the pointer fields to catch programming errors */
6531 alvherre@alvh.no-ip. 761 : 1184872 : res->attDescs = NULL;
762 : 1184872 : res->tuples = NULL;
6449 tgl@sss.pgh.pa.us 763 : 1184872 : res->paramDescs = NULL;
6531 alvherre@alvh.no-ip. 764 : 1184872 : res->errFields = NULL;
5688 tgl@sss.pgh.pa.us 765 : 1184872 : res->events = NULL;
766 : 1184872 : res->nEvents = 0;
767 : : /* res->curBlock was zeroed out earlier */
768 : :
769 : : /* Free the PGresult structure itself */
9475 bruce@momjian.us 770 : 1184872 : 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
9372 scrappy@hub.org 779 : 344717 : pqClearAsyncResult(PGconn *conn)
780 : : {
651 peter@eisentraut.org 781 : 344717 : PQclear(conn->result);
9475 bruce@momjian.us 782 : 344717 : conn->result = NULL;
786 tgl@sss.pgh.pa.us 783 : 344717 : conn->error_result = false;
8 tgl@sss.pgh.pa.us 784 :GNC 344717 : PQclear(conn->saved_result);
785 : 344717 : conn->saved_result = NULL;
10141 scrappy@hub.org 786 :CBC 344717 : }
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
7616 tgl@sss.pgh.pa.us 803 : 17 : pqSaveErrorResult(PGconn *conn)
804 : : {
805 : : /* Drop any pending result ... */
1189 806 : 17 : pqClearAsyncResult(conn);
807 : : /* ... and set flag to remember to make an error result later */
786 808 : 17 : conn->error_result = true;
8993 809 : 17 : }
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
1853 tgl@sss.pgh.pa.us 820 :UBC 0 : 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 : : */
1189 827 [ # # ]: 0 : if (conn->write_err_msg)
828 : : {
829 : 0 : appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg);
830 : : /* Avoid possibly appending the same message twice */
831 : 0 : conn->write_err_msg[0] = '\0';
832 : : }
833 : : else
516 peter@eisentraut.org 834 : 0 : libpq_append_conn_error(conn, "write to server failed");
835 : :
1189 tgl@sss.pgh.pa.us 836 : 0 : pqSaveErrorResult(conn);
1853 837 : 0 : }
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 *
7616 tgl@sss.pgh.pa.us 851 :CBC 305504 : pqPrepareAsyncResult(PGconn *conn)
852 : : {
853 : : PGresult *res;
854 : :
8993 855 : 305504 : res = conn->result;
786 856 [ + + ]: 305504 : 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 [ + + ]: 305487 : if (res->resultStatus == PGRES_FATAL_ERROR)
864 : 20004 : 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 [ - + ]: 17 : if (!conn->error_result)
516 peter@eisentraut.org 874 :UBC 0 : libpq_append_conn_error(conn, "no error text available");
875 : :
876 : : /* Paranoia: be sure errorReported offset is sane */
786 tgl@sss.pgh.pa.us 877 [ + - ]:CBC 17 : if (conn->errorReported < 0 ||
878 [ - + ]: 17 : conn->errorReported >= conn->errorMessage.len)
786 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 : : */
786 tgl@sss.pgh.pa.us 886 :CBC 17 : res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
887 [ + - ]: 17 : if (res)
888 : : {
889 : : /*
890 : : * Report whatever new error text we have, and advance
891 : : * errorReported.
892 : : */
893 : 17 : res->resultStatus = PGRES_FATAL_ERROR;
894 : 17 : pqSetResultError(res, &conn->errorMessage, conn->errorReported);
895 : 17 : 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 : : */
786 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 : : */
8 tgl@sss.pgh.pa.us 921 :GNC 305504 : conn->result = conn->saved_result;
922 : 305504 : conn->error_result = false; /* saved_result is never an error */
923 : 305504 : conn->saved_result = NULL;
924 : :
8993 tgl@sss.pgh.pa.us 925 :CBC 305504 : 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
6718 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 : :
7603 tgl@sss.pgh.pa.us 944 [ # # ]: 0 : if (hooks->noticeRec == NULL)
7601 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);
7559 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 */
7603 tgl@sss.pgh.pa.us 954 : 0 : res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
6881 neilc@samurai.com 955 [ # # ]: 0 : if (!res)
956 : 0 : return;
7603 tgl@sss.pgh.pa.us 957 : 0 : res->noticeHooks = *hooks;
958 : :
959 : : /*
960 : : * Set up fields of notice.
961 : : */
7536 peter_e@gmx.net 962 : 0 : pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);
963 : 0 : pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE"));
2788 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 : : */
2433 peter_e@gmx.net 971 : 0 : res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, false);
6881 neilc@samurai.com 972 [ # # ]: 0 : if (res->errMsg)
973 : 0 : sprintf(res->errMsg, "%s\n", msgBuf);
974 : : else
990 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);
7603 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
2420 tgl@sss.pgh.pa.us 993 :CBC 3442414 : pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp)
994 : : {
9475 bruce@momjian.us 995 [ + + ]: 3442414 : 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 : : */
2420 tgl@sss.pgh.pa.us 1015 [ + - ]: 124772 : if (res->tupArrSize <= INT_MAX / 2)
1016 [ + + ]: 124772 : newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
2420 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");
2433 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 : :
9268 tgl@sss.pgh.pa.us 1038 [ + + ]:CBC 124772 : if (res->tuples == NULL)
1039 : : newTuples = (PGresAttValue **)
1040 : 118486 : malloc(newSize * sizeof(PGresAttValue *));
1041 : : else
1042 : : newTuples = (PGresAttValue **)
1043 : 6286 : realloc(res->tuples, newSize * sizeof(PGresAttValue *));
9091 bruce@momjian.us 1044 [ - + ]: 124772 : if (!newTuples)
2433 peter_e@gmx.net 1045 :UBC 0 : return false; /* malloc or realloc failed */
2042 tgl@sss.pgh.pa.us 1046 :CBC 124772 : res->memorySize +=
1047 : 124772 : (newSize - res->tupArrSize) * sizeof(PGresAttValue *);
9327 1048 : 124772 : res->tupArrSize = newSize;
1049 : 124772 : res->tuples = newTuples;
1050 : : }
9716 bruce@momjian.us 1051 : 3442414 : res->tuples[res->ntups] = tup;
1052 : 3442414 : res->ntups++;
2433 peter_e@gmx.net 1053 : 3442414 : return true;
1054 : : }
1055 : :
1056 : : /*
1057 : : * pqSaveMessageField - save one field of an error or notice message
1058 : : */
1059 : : void
7603 tgl@sss.pgh.pa.us 1060 : 776204 : pqSaveMessageField(PGresult *res, char code, const char *value)
1061 : : {
1062 : : PGMessageField *pfield;
1063 : :
1064 : : pfield = (PGMessageField *)
1065 : 776204 : pqResultAlloc(res,
1066 : : offsetof(PGMessageField, contents) +
3340 1067 : 776204 : strlen(value) + 1,
1068 : : true);
7603 1069 [ - + ]: 776204 : if (!pfield)
7603 tgl@sss.pgh.pa.us 1070 :UBC 0 : return; /* out of memory? */
7603 tgl@sss.pgh.pa.us 1071 :CBC 776204 : pfield->code = code;
1072 : 776204 : strcpy(pfield->contents, value);
1073 : 776204 : pfield->next = res->errFields;
1074 : 776204 : res->errFields = pfield;
1075 : : }
1076 : :
1077 : : /*
1078 : : * pqSaveParameterStatus - remember parameter status sent by backend
1079 : : */
1080 : : void
7616 1081 : 163796 : pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
1082 : : {
1083 : : pgParameterStatus *pstatus;
1084 : : pgParameterStatus *prev;
1085 : :
1086 : : /*
1087 : : * Forget any old information about the parameter
1088 : : */
1089 : 163796 : for (pstatus = conn->pstatus, prev = NULL;
1090 [ + + ]: 1212183 : pstatus != NULL;
1091 : 1048387 : prev = pstatus, pstatus = pstatus->next)
1092 : : {
1093 [ + + ]: 1052527 : if (strcmp(pstatus->name, name) == 0)
1094 : : {
1095 [ + + ]: 4140 : if (prev)
1096 : 2603 : prev->next = pstatus->next;
1097 : : else
1098 : 1537 : conn->pstatus = pstatus->next;
1099 : 4140 : free(pstatus); /* frees name and value strings too */
1100 : 4140 : break;
1101 : : }
1102 : : }
1103 : :
1104 : : /*
1105 : : * Store new info as a single malloc block
1106 : : */
1107 : 163796 : pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
2489 1108 : 163796 : strlen(name) + strlen(value) + 2);
7616 1109 [ + - ]: 163796 : if (pstatus)
1110 : : {
1111 : : char *ptr;
1112 : :
1113 : 163796 : ptr = ((char *) pstatus) + sizeof(pgParameterStatus);
1114 : 163796 : pstatus->name = ptr;
1115 : 163796 : strcpy(ptr, name);
1116 : 163796 : ptr += strlen(name) + 1;
1117 : 163796 : pstatus->value = ptr;
1118 : 163796 : strcpy(ptr, value);
1119 : 163796 : pstatus->next = conn->pstatus;
1120 : 163796 : conn->pstatus = pstatus;
1121 : : }
1122 : :
1123 : : /*
1124 : : * Save values of settings that are of interest to libpq in fields of the
1125 : : * PGconn object. We keep client_encoding and standard_conforming_strings
1126 : : * in static variables as well, so that PQescapeString and PQescapeBytea
1127 : : * can behave somewhat sanely (at least in single-connection-using
1128 : : * programs).
1129 : : */
1130 [ + + ]: 163796 : if (strcmp(name, "client_encoding") == 0)
1131 : : {
1132 : 11407 : conn->client_encoding = pg_char_to_encoding(value);
1133 : : /* if we don't recognize the encoding name, fall back to SQL_ASCII */
6028 1134 [ - + ]: 11407 : if (conn->client_encoding < 0)
6028 tgl@sss.pgh.pa.us 1135 :UBC 0 : conn->client_encoding = PG_SQL_ASCII;
6538 tgl@sss.pgh.pa.us 1136 :CBC 11407 : static_client_encoding = conn->client_encoding;
1137 : : }
1138 [ + + ]: 152389 : else if (strcmp(name, "standard_conforming_strings") == 0)
1139 : : {
1140 : 11437 : conn->std_strings = (strcmp(value, "on") == 0);
1141 : 11437 : static_std_strings = conn->std_strings;
1142 : : }
7345 1143 [ + + ]: 140952 : else if (strcmp(name, "server_version") == 0)
1144 : : {
1145 : : /* We convert the server version to numeric form. */
1146 : : int cnt;
1147 : : int vmaj,
1148 : : vmin,
1149 : : vrev;
1150 : :
1151 : 11404 : cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
1152 : :
2809 1153 [ - + ]: 11404 : if (cnt == 3)
1154 : : {
1155 : : /* old style, e.g. 9.6.1 */
7345 tgl@sss.pgh.pa.us 1156 :UBC 0 : conn->sversion = (100 * vmaj + vmin) * 100 + vrev;
1157 : : }
2809 tgl@sss.pgh.pa.us 1158 [ - + ]:CBC 11404 : else if (cnt == 2)
1159 : : {
2809 tgl@sss.pgh.pa.us 1160 [ # # ]:LBC (9618) : if (vmaj >= 10)
1161 : : {
1162 : : /* new style, e.g. 10.1 */
1163 : (9618) : conn->sversion = 100 * 100 * vmaj + vmin;
1164 : : }
1165 : : else
1166 : : {
1167 : : /* old style without minor version, e.g. 9.6devel */
2809 tgl@sss.pgh.pa.us 1168 :UBC 0 : conn->sversion = (100 * vmaj + vmin) * 100;
1169 : : }
1170 : : }
2809 tgl@sss.pgh.pa.us 1171 [ + - ]:GBC 11404 : else if (cnt == 1)
1172 : : {
1173 : : /* new style without minor version, e.g. 10devel */
1174 : 11404 : conn->sversion = 100 * 100 * vmaj;
1175 : : }
1176 : : else
2809 tgl@sss.pgh.pa.us 1177 :UBC 0 : conn->sversion = 0; /* unknown */
1178 : : }
1139 tgl@sss.pgh.pa.us 1179 [ + + ]:CBC 129548 : else if (strcmp(name, "default_transaction_read_only") == 0)
1180 : : {
1181 : 11422 : conn->default_transaction_read_only =
1182 [ + + ]: 11422 : (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
1183 : : }
1184 [ + + ]: 118126 : else if (strcmp(name, "in_hot_standby") == 0)
1185 : : {
1186 : 11409 : conn->in_hot_standby =
1187 [ + + ]: 11409 : (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
1188 : : }
384 dgustafsson@postgres 1189 [ + + ]: 106717 : else if (strcmp(name, "scram_iterations") == 0)
1190 : : {
1191 : 11410 : conn->scram_sha_256_iterations = atoi(value);
1192 : : }
7616 tgl@sss.pgh.pa.us 1193 : 163796 : }
1194 : :
1195 : :
1196 : : /*
1197 : : * pqRowProcessor
1198 : : * Add the received row to the current async result (conn->result).
1199 : : * Returns 1 if OK, 0 if error occurred.
1200 : : *
1201 : : * On error, *errmsgp can be set to an error string to be returned.
1202 : : * (Such a string should already be translated via libpq_gettext().)
1203 : : * If it is left NULL, the error is presumed to be "out of memory".
1204 : : */
1205 : : int
4273 1206 : 3442414 : pqRowProcessor(PGconn *conn, const char **errmsgp)
1207 : : {
1208 : 3442414 : PGresult *res = conn->result;
4393 1209 : 3442414 : int nfields = res->numAttributes;
4273 1210 : 3442414 : const PGdataValue *columns = conn->rowBuf;
1211 : : PGresAttValue *tup;
1212 : : int i;
1213 : :
1214 : : /*
1215 : : * In partial-result mode, if we don't already have a partial PGresult
1216 : : * then make one by cloning conn->result (which should hold the correct
1217 : : * result metadata by now). Then the original conn->result is moved over
1218 : : * to saved_result so that we can re-use it as a reference for future
1219 : : * partial results. The saved result will become active again after
1220 : : * pqPrepareAsyncResult() returns the partial result to the application.
1221 : : */
8 tgl@sss.pgh.pa.us 1222 [ + + + + ]:GNC 3442414 : if (conn->partialResMode && conn->saved_result == NULL)
1223 : : {
1224 : : /* Copy everything that should be in the result at this point */
4273 tgl@sss.pgh.pa.us 1225 :CBC 2616 : res = PQcopyResult(res,
1226 : : PG_COPYRES_ATTRS | PG_COPYRES_EVENTS |
1227 : : PG_COPYRES_NOTICEHOOKS);
1228 [ - + ]: 2616 : if (!res)
4273 tgl@sss.pgh.pa.us 1229 :UBC 0 : return 0;
1230 : : /* Change result status to appropriate special value */
8 tgl@sss.pgh.pa.us 1231 [ + + ]:GNC 2616 : res->resultStatus = (conn->singleRowMode ? PGRES_SINGLE_TUPLE : PGRES_TUPLES_CHUNK);
1232 : : /* And stash it as the active result */
1233 : 2616 : conn->saved_result = conn->result;
1234 : 2616 : conn->result = res;
1235 : : }
1236 : :
1237 : : /*
1238 : : * Basically we just allocate space in the PGresult for each field and
1239 : : * copy the data over.
1240 : : *
1241 : : * Note: on malloc failure, we return 0 leaving *errmsgp still NULL, which
1242 : : * caller will take to mean "out of memory". This is preferable to trying
1243 : : * to set up such a message here, because evidently there's not enough
1244 : : * memory for gettext() to do anything.
1245 : : */
1246 : : tup = (PGresAttValue *)
2433 peter_e@gmx.net 1247 :CBC 3442414 : pqResultAlloc(res, nfields * sizeof(PGresAttValue), true);
4393 tgl@sss.pgh.pa.us 1248 [ - + ]: 3442414 : if (tup == NULL)
8 tgl@sss.pgh.pa.us 1249 :UNC 0 : return 0;
1250 : :
4393 tgl@sss.pgh.pa.us 1251 [ + + ]:CBC 19114265 : for (i = 0; i < nfields; i++)
1252 : : {
4326 bruce@momjian.us 1253 : 15671851 : int clen = columns[i].len;
1254 : :
4393 tgl@sss.pgh.pa.us 1255 [ + + ]: 15671851 : if (clen < 0)
1256 : : {
1257 : : /* null field */
1258 : 771199 : tup[i].len = NULL_LEN;
1259 : 771199 : tup[i].value = res->null_field;
1260 : : }
1261 : : else
1262 : : {
1263 : 14900652 : bool isbinary = (res->attDescs[i].format != 0);
1264 : : char *val;
1265 : :
1266 : 14900652 : val = (char *) pqResultAlloc(res, clen + 1, isbinary);
1267 [ - + ]: 14900652 : if (val == NULL)
8 tgl@sss.pgh.pa.us 1268 :UNC 0 : return 0;
1269 : :
1270 : : /* copy and zero-terminate the data (even if it's binary) */
4393 tgl@sss.pgh.pa.us 1271 :CBC 14900652 : memcpy(val, columns[i].value, clen);
1272 : 14900652 : val[clen] = '\0';
1273 : :
1274 : 14900652 : tup[i].len = clen;
1275 : 14900652 : tup[i].value = val;
1276 : : }
1277 : : }
1278 : :
1279 : : /* And add the tuple to the PGresult's tuple array */
2420 1280 [ - + ]: 3442414 : if (!pqAddTuple(res, tup, errmsgp))
8 tgl@sss.pgh.pa.us 1281 :UNC 0 : return 0;
1282 : :
1283 : : /*
1284 : : * Success. In partial-result mode, if we have enough rows then make the
1285 : : * result available to the client immediately.
1286 : : */
8 tgl@sss.pgh.pa.us 1287 [ + + + + ]:GNC 3442414 : if (conn->partialResMode && res->ntups >= conn->maxChunkSize)
1126 alvherre@alvh.no-ip. 1288 :CBC 2609 : conn->asyncStatus = PGASYNC_READY_MORE;
1289 : :
4393 tgl@sss.pgh.pa.us 1290 : 3442414 : return 1;
1291 : : }
1292 : :
1293 : :
1294 : : /*
1295 : : * pqAllocCmdQueueEntry
1296 : : * Get a command queue entry for caller to fill.
1297 : : *
1298 : : * If the recycle queue has a free element, that is returned; if not, a
1299 : : * fresh one is allocated. Caller is responsible for adding it to the
1300 : : * command queue (pqAppendCmdQueueEntry) once the struct is filled in, or
1301 : : * releasing the memory (pqRecycleCmdQueueEntry) if an error occurs.
1302 : : *
1303 : : * If allocation fails, sets the error message and returns NULL.
1304 : : */
1305 : : static PGcmdQueueEntry *
1126 alvherre@alvh.no-ip. 1306 : 279717 : pqAllocCmdQueueEntry(PGconn *conn)
1307 : : {
1308 : : PGcmdQueueEntry *entry;
1309 : :
1310 [ + + ]: 279717 : if (conn->cmd_queue_recycle == NULL)
1311 : : {
1312 : 12092 : entry = (PGcmdQueueEntry *) malloc(sizeof(PGcmdQueueEntry));
1313 [ - + ]: 12092 : if (entry == NULL)
1314 : : {
516 peter@eisentraut.org 1315 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
1126 alvherre@alvh.no-ip. 1316 : 0 : return NULL;
1317 : : }
1318 : : }
1319 : : else
1320 : : {
1126 alvherre@alvh.no-ip. 1321 :CBC 267625 : entry = conn->cmd_queue_recycle;
1322 : 267625 : conn->cmd_queue_recycle = entry->next;
1323 : : }
1324 : 279717 : entry->next = NULL;
1325 : 279717 : entry->query = NULL;
1326 : :
1327 : 279717 : return entry;
1328 : : }
1329 : :
1330 : : /*
1331 : : * pqAppendCmdQueueEntry
1332 : : * Append a caller-allocated entry to the command queue, and update
1333 : : * conn->asyncStatus to account for it.
1334 : : *
1335 : : * The query itself must already have been put in the output buffer by the
1336 : : * caller.
1337 : : */
1338 : : static void
1339 : 279717 : pqAppendCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
1340 : : {
1341 [ - + ]: 279717 : Assert(entry->next == NULL);
1342 : :
1343 [ + + ]: 279717 : if (conn->cmd_queue_head == NULL)
1344 : 277867 : conn->cmd_queue_head = entry;
1345 : : else
1346 : 1850 : conn->cmd_queue_tail->next = entry;
1347 : :
1348 : 279717 : conn->cmd_queue_tail = entry;
1349 : :
1010 1350 [ + + - ]: 279717 : switch (conn->pipelineStatus)
1351 : : {
1352 : 279675 : case PQ_PIPELINE_OFF:
1353 : : case PQ_PIPELINE_ON:
1354 : :
1355 : : /*
1356 : : * When not in pipeline aborted state, if there's a result ready
1357 : : * to be consumed, let it be so (that is, don't change away from
1358 : : * READY or READY_MORE); otherwise set us busy to wait for
1359 : : * something to arrive from the server.
1360 : : */
1361 [ + + ]: 279675 : if (conn->asyncStatus == PGASYNC_IDLE)
1362 : 277866 : conn->asyncStatus = PGASYNC_BUSY;
1363 : 279675 : break;
1364 : :
1365 : 42 : case PQ_PIPELINE_ABORTED:
1366 : :
1367 : : /*
1368 : : * In aborted pipeline state, we don't expect anything from the
1369 : : * server (since we don't send any queries that are queued).
1370 : : * Therefore, if IDLE then do what PQgetResult would do to let
1371 : : * itself consume commands from the queue; if we're in any other
1372 : : * state, we don't have to do anything.
1373 : : */
649 1374 [ + + ]: 42 : if (conn->asyncStatus == PGASYNC_IDLE ||
1375 [ - + ]: 41 : conn->asyncStatus == PGASYNC_PIPELINE_IDLE)
1010 1376 : 1 : pqPipelineProcessQueue(conn);
1377 : 42 : break;
1378 : : }
1126 1379 : 279717 : }
1380 : :
1381 : : /*
1382 : : * pqRecycleCmdQueueEntry
1383 : : * Push a command queue entry onto the freelist.
1384 : : */
1385 : : static void
1386 : 278985 : pqRecycleCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
1387 : : {
1388 [ - + ]: 278985 : if (entry == NULL)
1126 alvherre@alvh.no-ip. 1389 :UBC 0 : return;
1390 : :
1391 : : /* recyclable entries should not have a follow-on command */
1126 alvherre@alvh.no-ip. 1392 [ - + ]:CBC 278985 : Assert(entry->next == NULL);
1393 : :
1394 [ + + ]: 278985 : if (entry->query)
1395 : : {
1396 : 272104 : free(entry->query);
1397 : 272104 : entry->query = NULL;
1398 : : }
1399 : :
1400 : 278985 : entry->next = conn->cmd_queue_recycle;
1401 : 278985 : conn->cmd_queue_recycle = entry;
1402 : : }
1403 : :
1404 : :
1405 : : /*
1406 : : * PQsendQuery
1407 : : * Submit a query, but don't wait for it to finish
1408 : : *
1409 : : * Returns: 1 if successfully submitted
1410 : : * 0 if error (conn->errorMessage is set)
1411 : : *
1412 : : * PQsendQueryContinue is a non-exported version that behaves identically
1413 : : * except that it doesn't reset conn->errorMessage.
1414 : : */
1415 : : int
9475 bruce@momjian.us 1416 : 269096 : PQsendQuery(PGconn *conn, const char *query)
1417 : : {
1189 tgl@sss.pgh.pa.us 1418 : 269096 : return PQsendQueryInternal(conn, query, true);
1419 : : }
1420 : :
1421 : : int
1189 tgl@sss.pgh.pa.us 1422 :UBC 0 : PQsendQueryContinue(PGconn *conn, const char *query)
1423 : : {
1424 : 0 : return PQsendQueryInternal(conn, query, false);
1425 : : }
1426 : :
1427 : : static int
1189 tgl@sss.pgh.pa.us 1428 :CBC 269096 : PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
1429 : : {
1126 alvherre@alvh.no-ip. 1430 : 269096 : PGcmdQueueEntry *entry = NULL;
1431 : :
1189 tgl@sss.pgh.pa.us 1432 [ - + ]: 269096 : if (!PQsendQueryStart(conn, newQuery))
9475 bruce@momjian.us 1433 :UBC 0 : return 0;
1434 : :
1435 : : /* check the argument */
9475 bruce@momjian.us 1436 [ - + ]:CBC 269096 : if (!query)
1437 : : {
516 peter@eisentraut.org 1438 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
9380 bruce@momjian.us 1439 : 0 : return 0;
1440 : : }
1441 : :
649 alvherre@alvh.no-ip. 1442 [ + + ]:CBC 269096 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
1443 : : {
516 peter@eisentraut.org 1444 : 1 : libpq_append_conn_error(conn, "%s not allowed in pipeline mode",
1445 : : "PQsendQuery");
569 alvherre@alvh.no-ip. 1446 : 1 : return 0;
1447 : : }
1448 : :
1449 : 269095 : entry = pqAllocCmdQueueEntry(conn);
1450 [ - + ]: 269095 : if (entry == NULL)
569 alvherre@alvh.no-ip. 1451 :UBC 0 : return 0; /* error msg already set */
1452 : :
1453 : : /* Send the query message(s) */
1454 : : /* construct the outgoing Query message */
236 nathan@postgresql.or 1455 [ + - + - ]:GNC 538190 : if (pqPutMsgStart(PqMsg_Query, conn) < 0 ||
569 alvherre@alvh.no-ip. 1456 [ - + ]:CBC 538190 : pqPuts(query, conn) < 0 ||
1457 : 269095 : pqPutMsgEnd(conn) < 0)
1458 : : {
1459 : : /* error message should be set up already */
569 alvherre@alvh.no-ip. 1460 :UBC 0 : pqRecycleCmdQueueEntry(conn, entry);
1461 : 0 : return 0;
1462 : : }
1463 : :
1464 : : /* remember we are using simple query protocol */
569 alvherre@alvh.no-ip. 1465 :CBC 269095 : entry->queryclass = PGQUERY_SIMPLE;
1466 : : /* and remember the query text too, if possible */
1467 : 269095 : entry->query = strdup(query);
1468 : :
1469 : : /*
1470 : : * Give the data a push. In nonblock mode, don't complain if we're unable
1471 : : * to send it all; PQgetResult() will do any additional flushing needed.
1472 : : */
1473 [ - + ]: 269095 : if (pqFlush(conn) < 0)
1126 alvherre@alvh.no-ip. 1474 :UBC 0 : goto sendFailed;
1475 : :
1476 : : /* OK, it's launched! */
1126 alvherre@alvh.no-ip. 1477 :CBC 269095 : pqAppendCmdQueueEntry(conn, entry);
1478 : :
9475 bruce@momjian.us 1479 : 269095 : return 1;
1480 : :
1126 alvherre@alvh.no-ip. 1481 :UBC 0 : sendFailed:
1482 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1483 : : /* error message should be set up already */
1484 : 0 : return 0;
1485 : : }
1486 : :
1487 : : /*
1488 : : * PQsendQueryParams
1489 : : * Like PQsendQuery, but use extended query protocol so we can pass parameters
1490 : : */
1491 : : int
7603 tgl@sss.pgh.pa.us 1492 :CBC 2436 : PQsendQueryParams(PGconn *conn,
1493 : : const char *command,
1494 : : int nParams,
1495 : : const Oid *paramTypes,
1496 : : const char *const *paramValues,
1497 : : const int *paramLengths,
1498 : : const int *paramFormats,
1499 : : int resultFormat)
1500 : : {
1189 1501 [ - + ]: 2436 : if (!PQsendQueryStart(conn, true))
7550 tgl@sss.pgh.pa.us 1502 :UBC 0 : return 0;
1503 : :
1504 : : /* check the arguments */
7550 tgl@sss.pgh.pa.us 1505 [ - + ]:CBC 2436 : if (!command)
1506 : : {
516 peter@eisentraut.org 1507 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
7550 tgl@sss.pgh.pa.us 1508 : 0 : return 0;
1509 : : }
1041 tomas.vondra@postgre 1510 [ + - - + ]:CBC 2436 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1511 : : {
516 peter@eisentraut.org 1512 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1513 : : PQ_QUERY_PARAM_MAX_LIMIT);
4262 heikki.linnakangas@i 1514 : 0 : return 0;
1515 : : }
1516 : :
7550 tgl@sss.pgh.pa.us 1517 :CBC 2436 : return PQsendQueryGuts(conn,
1518 : : command,
1519 : : "", /* use unnamed statement */
1520 : : nParams,
1521 : : paramTypes,
1522 : : paramValues,
1523 : : paramLengths,
1524 : : paramFormats,
1525 : : resultFormat);
1526 : : }
1527 : :
1528 : : /*
1529 : : * PQsendPrepare
1530 : : * Submit a Parse message, but don't wait for it to finish
1531 : : *
1532 : : * Returns: 1 if successfully submitted
1533 : : * 0 if error (conn->errorMessage is set)
1534 : : */
1535 : : int
7118 1536 : 1304 : PQsendPrepare(PGconn *conn,
1537 : : const char *stmtName, const char *query,
1538 : : int nParams, const Oid *paramTypes)
1539 : : {
1126 alvherre@alvh.no-ip. 1540 : 1304 : PGcmdQueueEntry *entry = NULL;
1541 : :
1189 tgl@sss.pgh.pa.us 1542 [ - + ]: 1304 : if (!PQsendQueryStart(conn, true))
7118 tgl@sss.pgh.pa.us 1543 :UBC 0 : return 0;
1544 : :
1545 : : /* check the arguments */
7118 tgl@sss.pgh.pa.us 1546 [ - + ]:CBC 1304 : if (!stmtName)
1547 : : {
516 peter@eisentraut.org 1548 :UBC 0 : libpq_append_conn_error(conn, "statement name is a null pointer");
7118 tgl@sss.pgh.pa.us 1549 : 0 : return 0;
1550 : : }
7118 tgl@sss.pgh.pa.us 1551 [ - + ]:CBC 1304 : if (!query)
1552 : : {
516 peter@eisentraut.org 1553 :UBC 0 : libpq_append_conn_error(conn, "command string is a null pointer");
7118 tgl@sss.pgh.pa.us 1554 : 0 : return 0;
1555 : : }
1041 tomas.vondra@postgre 1556 [ + - - + ]:CBC 1304 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1557 : : {
516 peter@eisentraut.org 1558 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1559 : : PQ_QUERY_PARAM_MAX_LIMIT);
4262 heikki.linnakangas@i 1560 : 0 : return 0;
1561 : : }
1562 : :
1126 alvherre@alvh.no-ip. 1563 :CBC 1304 : entry = pqAllocCmdQueueEntry(conn);
1564 [ - + ]: 1304 : if (entry == NULL)
1126 alvherre@alvh.no-ip. 1565 :UBC 0 : return 0; /* error msg already set */
1566 : :
1567 : : /* construct the Parse message */
236 nathan@postgresql.or 1568 [ + - + - ]:GNC 2608 : if (pqPutMsgStart(PqMsg_Parse, conn) < 0 ||
7118 tgl@sss.pgh.pa.us 1569 [ - + ]:CBC 2608 : pqPuts(stmtName, conn) < 0 ||
1570 : 1304 : pqPuts(query, conn) < 0)
7118 tgl@sss.pgh.pa.us 1571 :UBC 0 : goto sendFailed;
1572 : :
7118 tgl@sss.pgh.pa.us 1573 [ + + + + ]:CBC 1304 : if (nParams > 0 && paramTypes)
1574 : 3 : {
1575 : : int i;
1576 : :
1577 [ - + ]: 3 : if (pqPutInt(nParams, 2, conn) < 0)
7118 tgl@sss.pgh.pa.us 1578 :UBC 0 : goto sendFailed;
7118 tgl@sss.pgh.pa.us 1579 [ + + ]:CBC 8 : for (i = 0; i < nParams; i++)
1580 : : {
1581 [ - + ]: 5 : if (pqPutInt(paramTypes[i], 4, conn) < 0)
7118 tgl@sss.pgh.pa.us 1582 :UBC 0 : goto sendFailed;
1583 : : }
1584 : : }
1585 : : else
1586 : : {
7118 tgl@sss.pgh.pa.us 1587 [ - + ]:CBC 1301 : if (pqPutInt(0, 2, conn) < 0)
7118 tgl@sss.pgh.pa.us 1588 :UBC 0 : goto sendFailed;
1589 : : }
7118 tgl@sss.pgh.pa.us 1590 [ - + ]:CBC 1304 : if (pqPutMsgEnd(conn) < 0)
7118 tgl@sss.pgh.pa.us 1591 :UBC 0 : goto sendFailed;
1592 : :
1593 : : /* Add a Sync, unless in pipeline mode. */
1126 alvherre@alvh.no-ip. 1594 [ + + ]:CBC 1304 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
1595 : : {
236 nathan@postgresql.or 1596 [ + - - + ]:GNC 2602 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1126 alvherre@alvh.no-ip. 1597 :CBC 1301 : pqPutMsgEnd(conn) < 0)
1126 alvherre@alvh.no-ip. 1598 :UBC 0 : goto sendFailed;
1599 : : }
1600 : :
1601 : : /* remember we are doing just a Parse */
1126 alvherre@alvh.no-ip. 1602 :CBC 1304 : entry->queryclass = PGQUERY_PREPARE;
1603 : :
1604 : : /* and remember the query text too, if possible */
1605 : : /* if insufficient memory, query just winds up NULL */
1606 : 1304 : entry->query = strdup(query);
1607 : :
1608 : : /*
1609 : : * Give the data a push (in pipeline mode, only if we're past the size
1610 : : * threshold). In nonblock mode, don't complain if we're unable to send
1611 : : * it all; PQgetResult() will do any additional flushing needed.
1612 : : */
1613 [ - + ]: 1304 : if (pqPipelineFlush(conn) < 0)
7118 tgl@sss.pgh.pa.us 1614 :UBC 0 : goto sendFailed;
1615 : :
1616 : : /* OK, it's launched! */
1010 alvherre@alvh.no-ip. 1617 :CBC 1304 : pqAppendCmdQueueEntry(conn, entry);
1618 : :
7118 tgl@sss.pgh.pa.us 1619 : 1304 : return 1;
1620 : :
7118 tgl@sss.pgh.pa.us 1621 :UBC 0 : sendFailed:
1126 alvherre@alvh.no-ip. 1622 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1623 : : /* error message should be set up already */
7118 tgl@sss.pgh.pa.us 1624 : 0 : return 0;
1625 : : }
1626 : :
1627 : : /*
1628 : : * PQsendQueryPrepared
1629 : : * Like PQsendQuery, but execute a previously prepared statement,
1630 : : * using extended query protocol so we can pass parameters
1631 : : */
1632 : : int
7550 tgl@sss.pgh.pa.us 1633 :CBC 6760 : PQsendQueryPrepared(PGconn *conn,
1634 : : const char *stmtName,
1635 : : int nParams,
1636 : : const char *const *paramValues,
1637 : : const int *paramLengths,
1638 : : const int *paramFormats,
1639 : : int resultFormat)
1640 : : {
1189 1641 [ - + ]: 6760 : if (!PQsendQueryStart(conn, true))
7603 tgl@sss.pgh.pa.us 1642 :UBC 0 : return 0;
1643 : :
1644 : : /* check the arguments */
7550 tgl@sss.pgh.pa.us 1645 [ - + ]:CBC 6760 : if (!stmtName)
1646 : : {
516 peter@eisentraut.org 1647 :UBC 0 : libpq_append_conn_error(conn, "statement name is a null pointer");
7603 tgl@sss.pgh.pa.us 1648 : 0 : return 0;
1649 : : }
1041 tomas.vondra@postgre 1650 [ + - - + ]:CBC 6760 : if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
1651 : : {
516 peter@eisentraut.org 1652 :UBC 0 : libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
1653 : : PQ_QUERY_PARAM_MAX_LIMIT);
4262 heikki.linnakangas@i 1654 : 0 : return 0;
1655 : : }
1656 : :
7550 tgl@sss.pgh.pa.us 1657 :CBC 6760 : return PQsendQueryGuts(conn,
1658 : : NULL, /* no command to parse */
1659 : : stmtName,
1660 : : nParams,
1661 : : NULL, /* no param types */
1662 : : paramValues,
1663 : : paramLengths,
1664 : : paramFormats,
1665 : : resultFormat);
1666 : : }
1667 : :
1668 : : /*
1669 : : * PQsendQueryStart
1670 : : * Common startup code for PQsendQuery and sibling routines
1671 : : */
1672 : : static bool
1189 1673 : 279652 : PQsendQueryStart(PGconn *conn, bool newQuery)
1674 : : {
7550 1675 [ - + ]: 279652 : if (!conn)
7550 tgl@sss.pgh.pa.us 1676 :UBC 0 : return false;
1677 : :
1678 : : /*
1679 : : * If this is the beginning of a query cycle, reset the error state.
1680 : : * However, in pipeline mode with something already queued, the error
1681 : : * buffer belongs to that command and we shouldn't clear it.
1682 : : */
776 tgl@sss.pgh.pa.us 1683 [ + - + + ]:CBC 279652 : if (newQuery && conn->cmd_queue_head == NULL)
786 1684 : 277866 : pqClearConnErrorState(conn);
1685 : :
1686 : : /* Don't try to send if we know there's no live connection. */
7550 1687 [ - + ]: 279652 : if (conn->status != CONNECTION_OK)
1688 : : {
516 peter@eisentraut.org 1689 :UBC 0 : libpq_append_conn_error(conn, "no connection to the server");
7550 tgl@sss.pgh.pa.us 1690 : 0 : return false;
1691 : : }
1692 : :
1693 : : /* Can't send while already busy, either, unless enqueuing for later */
1126 alvherre@alvh.no-ip. 1694 [ + + ]:CBC 279652 : if (conn->asyncStatus != PGASYNC_IDLE &&
1695 [ - + ]: 1786 : conn->pipelineStatus == PQ_PIPELINE_OFF)
1696 : : {
516 peter@eisentraut.org 1697 :UBC 0 : libpq_append_conn_error(conn, "another command is already in progress");
7550 tgl@sss.pgh.pa.us 1698 : 0 : return false;
1699 : : }
1700 : :
1126 alvherre@alvh.no-ip. 1701 [ + + ]:CBC 279652 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
1702 : : {
1703 : : /*
1704 : : * When enqueuing commands we don't change much of the connection
1705 : : * state since it's already in use for the current command. The
1706 : : * connection state will get updated when pqPipelineProcessQueue()
1707 : : * advances to start processing the queued message.
1708 : : *
1709 : : * Just make sure we can safely enqueue given the current connection
1710 : : * state. We can enqueue behind another queue item, or behind a
1711 : : * non-queue command (one that sends its own sync), but we can't
1712 : : * enqueue if the connection is in a copy state.
1713 : : */
1714 [ + - - ]: 1851 : switch (conn->asyncStatus)
1715 : : {
1716 : 1851 : case PGASYNC_IDLE:
1717 : : case PGASYNC_PIPELINE_IDLE:
1718 : : case PGASYNC_READY:
1719 : : case PGASYNC_READY_MORE:
1720 : : case PGASYNC_BUSY:
1721 : : /* ok to queue */
1722 : 1851 : break;
1723 : :
1126 alvherre@alvh.no-ip. 1724 :UBC 0 : case PGASYNC_COPY_IN:
1725 : : case PGASYNC_COPY_OUT:
1726 : : case PGASYNC_COPY_BOTH:
516 peter@eisentraut.org 1727 : 0 : libpq_append_conn_error(conn, "cannot queue commands during COPY");
1126 alvherre@alvh.no-ip. 1728 : 0 : return false;
1729 : : }
1730 : : }
1731 : : else
1732 : : {
1733 : : /*
1734 : : * This command's results will come in immediately. Initialize async
1735 : : * result-accumulation state
1736 : : */
1126 alvherre@alvh.no-ip. 1737 :CBC 277801 : pqClearAsyncResult(conn);
1738 : :
1739 : : /* reset partial-result mode */
8 tgl@sss.pgh.pa.us 1740 :GNC 277801 : conn->partialResMode = false;
1126 alvherre@alvh.no-ip. 1741 :CBC 277801 : conn->singleRowMode = false;
8 tgl@sss.pgh.pa.us 1742 :GNC 277801 : conn->maxChunkSize = 0;
1743 : : }
1744 : :
1745 : : /* ready to send command message */
7550 tgl@sss.pgh.pa.us 1746 :CBC 279652 : return true;
1747 : : }
1748 : :
1749 : : /*
1750 : : * PQsendQueryGuts
1751 : : * Common code for sending a query with extended query protocol
1752 : : * PQsendQueryStart should be done already
1753 : : *
1754 : : * command may be NULL to indicate we use an already-prepared statement
1755 : : */
1756 : : static int
1757 : 9196 : PQsendQueryGuts(PGconn *conn,
1758 : : const char *command,
1759 : : const char *stmtName,
1760 : : int nParams,
1761 : : const Oid *paramTypes,
1762 : : const char *const *paramValues,
1763 : : const int *paramLengths,
1764 : : const int *paramFormats,
1765 : : int resultFormat)
1766 : : {
1767 : : int i;
1768 : : PGcmdQueueEntry *entry;
1769 : :
1126 alvherre@alvh.no-ip. 1770 : 9196 : entry = pqAllocCmdQueueEntry(conn);
1771 [ - + ]: 9196 : if (entry == NULL)
1126 alvherre@alvh.no-ip. 1772 :UBC 0 : return 0; /* error msg already set */
1773 : :
1774 : : /*
1775 : : * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync
1776 : : * (if not in pipeline mode), using specified statement name and the
1777 : : * unnamed portal.
1778 : : */
1779 : :
7550 tgl@sss.pgh.pa.us 1780 [ + + ]:CBC 9196 : if (command)
1781 : : {
1782 : : /* construct the Parse message */
236 nathan@postgresql.or 1783 [ + - + - ]:GNC 4872 : if (pqPutMsgStart(PqMsg_Parse, conn) < 0 ||
7550 tgl@sss.pgh.pa.us 1784 [ - + ]:CBC 4872 : pqPuts(stmtName, conn) < 0 ||
1785 : 2436 : pqPuts(command, conn) < 0)
7603 tgl@sss.pgh.pa.us 1786 :UBC 0 : goto sendFailed;
7550 tgl@sss.pgh.pa.us 1787 [ + + + + ]:CBC 2436 : if (nParams > 0 && paramTypes)
1788 : : {
1789 [ - + ]: 26 : if (pqPutInt(nParams, 2, conn) < 0)
7603 tgl@sss.pgh.pa.us 1790 :UBC 0 : goto sendFailed;
7550 tgl@sss.pgh.pa.us 1791 [ + + ]:CBC 64 : for (i = 0; i < nParams; i++)
1792 : : {
1793 [ - + ]: 38 : if (pqPutInt(paramTypes[i], 4, conn) < 0)
7550 tgl@sss.pgh.pa.us 1794 :UBC 0 : goto sendFailed;
1795 : : }
1796 : : }
1797 : : else
1798 : : {
7550 tgl@sss.pgh.pa.us 1799 [ - + ]:CBC 2410 : if (pqPutInt(0, 2, conn) < 0)
7550 tgl@sss.pgh.pa.us 1800 :UBC 0 : goto sendFailed;
1801 : : }
7550 tgl@sss.pgh.pa.us 1802 [ - + ]:CBC 2436 : if (pqPutMsgEnd(conn) < 0)
7603 tgl@sss.pgh.pa.us 1803 :UBC 0 : goto sendFailed;
1804 : : }
1805 : :
1806 : : /* Construct the Bind message */
236 nathan@postgresql.or 1807 [ + - + - ]:GNC 18392 : if (pqPutMsgStart(PqMsg_Bind, conn) < 0 ||
7603 tgl@sss.pgh.pa.us 1808 [ - + ]:CBC 18392 : pqPuts("", conn) < 0 ||
7550 1809 : 9196 : pqPuts(stmtName, conn) < 0)
7603 tgl@sss.pgh.pa.us 1810 :UBC 0 : goto sendFailed;
1811 : :
1812 : : /* Send parameter formats */
7603 tgl@sss.pgh.pa.us 1813 [ + + + + ]:CBC 9196 : if (nParams > 0 && paramFormats)
1814 : : {
1815 [ - + ]: 1277 : if (pqPutInt(nParams, 2, conn) < 0)
7603 tgl@sss.pgh.pa.us 1816 :UBC 0 : goto sendFailed;
7603 tgl@sss.pgh.pa.us 1817 [ + + ]:CBC 3009 : for (i = 0; i < nParams; i++)
1818 : : {
1819 [ - + ]: 1732 : if (pqPutInt(paramFormats[i], 2, conn) < 0)
7603 tgl@sss.pgh.pa.us 1820 :UBC 0 : goto sendFailed;
1821 : : }
1822 : : }
1823 : : else
1824 : : {
7603 tgl@sss.pgh.pa.us 1825 [ - + ]:CBC 7919 : if (pqPutInt(0, 2, conn) < 0)
7603 tgl@sss.pgh.pa.us 1826 :UBC 0 : goto sendFailed;
1827 : : }
1828 : :
7603 tgl@sss.pgh.pa.us 1829 [ - + ]:CBC 9196 : if (pqPutInt(nParams, 2, conn) < 0)
7603 tgl@sss.pgh.pa.us 1830 :UBC 0 : goto sendFailed;
1831 : :
1832 : : /* Send parameters */
7603 tgl@sss.pgh.pa.us 1833 [ + + ]:CBC 21876 : for (i = 0; i < nParams; i++)
1834 : : {
1835 [ + - + + ]: 12680 : if (paramValues && paramValues[i])
1836 : 12083 : {
1837 : : int nbytes;
1838 : :
1839 [ + + + + ]: 12083 : if (paramFormats && paramFormats[i] != 0)
1840 : : {
1841 : : /* binary parameter */
6884 1842 [ + - ]: 13 : if (paramLengths)
1843 : 13 : nbytes = paramLengths[i];
1844 : : else
1845 : : {
516 peter@eisentraut.org 1846 :UBC 0 : libpq_append_conn_error(conn, "length must be given for binary parameter");
6884 tgl@sss.pgh.pa.us 1847 : 0 : goto sendFailed;
1848 : : }
1849 : : }
1850 : : else
1851 : : {
1852 : : /* text parameter, do not use paramLengths */
7603 tgl@sss.pgh.pa.us 1853 :CBC 12070 : nbytes = strlen(paramValues[i]);
1854 : : }
1855 [ + - - + ]: 24166 : if (pqPutInt(nbytes, 4, conn) < 0 ||
1856 : 12083 : pqPutnchar(paramValues[i], nbytes, conn) < 0)
7603 tgl@sss.pgh.pa.us 1857 :UBC 0 : goto sendFailed;
1858 : : }
1859 : : else
1860 : : {
1861 : : /* take the param as NULL */
7603 tgl@sss.pgh.pa.us 1862 [ - + ]:CBC 597 : if (pqPutInt(-1, 4, conn) < 0)
7603 tgl@sss.pgh.pa.us 1863 :UBC 0 : goto sendFailed;
1864 : : }
1865 : : }
7603 tgl@sss.pgh.pa.us 1866 [ + - - + ]:CBC 18392 : if (pqPutInt(1, 2, conn) < 0 ||
1867 : 9196 : pqPutInt(resultFormat, 2, conn))
7603 tgl@sss.pgh.pa.us 1868 :UBC 0 : goto sendFailed;
7603 tgl@sss.pgh.pa.us 1869 [ - + ]:CBC 9196 : if (pqPutMsgEnd(conn) < 0)
7603 tgl@sss.pgh.pa.us 1870 :UBC 0 : goto sendFailed;
1871 : :
1872 : : /* construct the Describe Portal message */
236 nathan@postgresql.or 1873 [ + - + - ]:GNC 18392 : if (pqPutMsgStart(PqMsg_Describe, conn) < 0 ||
7603 tgl@sss.pgh.pa.us 1874 [ + - ]:CBC 18392 : pqPutc('P', conn) < 0 ||
1875 [ - + ]: 18392 : pqPuts("", conn) < 0 ||
1876 : 9196 : pqPutMsgEnd(conn) < 0)
7603 tgl@sss.pgh.pa.us 1877 :UBC 0 : goto sendFailed;
1878 : :
1879 : : /* construct the Execute message */
236 nathan@postgresql.or 1880 [ + - + - ]:GNC 18392 : if (pqPutMsgStart(PqMsg_Execute, conn) < 0 ||
7603 tgl@sss.pgh.pa.us 1881 [ + - ]:CBC 18392 : pqPuts("", conn) < 0 ||
1882 [ - + ]: 18392 : pqPutInt(0, 4, conn) < 0 ||
1883 : 9196 : pqPutMsgEnd(conn) < 0)
7603 tgl@sss.pgh.pa.us 1884 :UBC 0 : goto sendFailed;
1885 : :
1886 : : /* construct the Sync message if not in pipeline mode */
1126 alvherre@alvh.no-ip. 1887 [ + + ]:CBC 9196 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
1888 : : {
236 nathan@postgresql.or 1889 [ + - - + ]:GNC 14706 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1126 alvherre@alvh.no-ip. 1890 :CBC 7353 : pqPutMsgEnd(conn) < 0)
1126 alvherre@alvh.no-ip. 1891 :UBC 0 : goto sendFailed;
1892 : : }
1893 : :
1894 : : /* remember we are using extended query protocol */
1126 alvherre@alvh.no-ip. 1895 :CBC 9196 : entry->queryclass = PGQUERY_EXTENDED;
1896 : :
1897 : : /* and remember the query text too, if possible */
1898 : : /* if insufficient memory, query just winds up NULL */
6606 tgl@sss.pgh.pa.us 1899 [ + + ]: 9196 : if (command)
1126 alvherre@alvh.no-ip. 1900 : 2436 : entry->query = strdup(command);
1901 : :
1902 : : /*
1903 : : * Give the data a push (in pipeline mode, only if we're past the size
1904 : : * threshold). In nonblock mode, don't complain if we're unable to send
1905 : : * it all; PQgetResult() will do any additional flushing needed.
1906 : : */
1907 [ - + ]: 9196 : if (pqPipelineFlush(conn) < 0)
7603 tgl@sss.pgh.pa.us 1908 :UBC 0 : goto sendFailed;
1909 : :
1910 : : /* OK, it's launched! */
1126 alvherre@alvh.no-ip. 1911 :CBC 9196 : pqAppendCmdQueueEntry(conn, entry);
1912 : :
7603 tgl@sss.pgh.pa.us 1913 : 9196 : return 1;
1914 : :
7603 tgl@sss.pgh.pa.us 1915 :UBC 0 : sendFailed:
1126 alvherre@alvh.no-ip. 1916 : 0 : pqRecycleCmdQueueEntry(conn, entry);
1917 : : /* error message should be set up already */
7603 tgl@sss.pgh.pa.us 1918 : 0 : return 0;
1919 : : }
1920 : :
1921 : : /*
1922 : : * Is it OK to change partial-result mode now?
1923 : : */
1924 : : static bool
8 tgl@sss.pgh.pa.us 1925 :GNC 74 : canChangeResultMode(PGconn *conn)
1926 : : {
1927 : : /*
1928 : : * Only allow changing the mode when we have launched a query and not yet
1929 : : * received any results.
1930 : : */
4273 1931 [ - + ]: 74 : if (!conn)
8 tgl@sss.pgh.pa.us 1932 :UNC 0 : return false;
4273 tgl@sss.pgh.pa.us 1933 [ - + ]:GNC 74 : if (conn->asyncStatus != PGASYNC_BUSY)
8 tgl@sss.pgh.pa.us 1934 :UNC 0 : return false;
1126 alvherre@alvh.no-ip. 1935 [ + - ]:GNC 74 : if (!conn->cmd_queue_head ||
1936 [ + + ]: 74 : (conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE &&
1937 [ - + ]: 11 : conn->cmd_queue_head->queryclass != PGQUERY_EXTENDED))
8 tgl@sss.pgh.pa.us 1938 :UNC 0 : return false;
724 tgl@sss.pgh.pa.us 1939 [ + - - + ]:GNC 74 : if (pgHavePendingResult(conn))
8 tgl@sss.pgh.pa.us 1940 :UNC 0 : return false;
8 tgl@sss.pgh.pa.us 1941 :GNC 74 : return true;
1942 : : }
1943 : :
1944 : : /*
1945 : : * Select row-by-row processing mode
1946 : : */
1947 : : int
8 tgl@sss.pgh.pa.us 1948 :CBC 33 : PQsetSingleRowMode(PGconn *conn)
1949 : : {
8 tgl@sss.pgh.pa.us 1950 [ + - ]:GNC 33 : if (canChangeResultMode(conn))
1951 : : {
1952 : 33 : conn->partialResMode = true;
1953 : 33 : conn->singleRowMode = true;
1954 : 33 : conn->maxChunkSize = 1;
1955 : 33 : return 1;
1956 : : }
1957 : : else
4273 tgl@sss.pgh.pa.us 1958 :UBC 0 : return 0;
1959 : : }
1960 : :
1961 : : /*
1962 : : * Select chunked results processing mode
1963 : : */
1964 : : int
8 tgl@sss.pgh.pa.us 1965 :GNC 41 : PQsetChunkedRowsMode(PGconn *conn, int chunkSize)
1966 : : {
1967 [ + - + - ]: 41 : if (chunkSize > 0 && canChangeResultMode(conn))
1968 : : {
1969 : 41 : conn->partialResMode = true;
1970 : 41 : conn->singleRowMode = false;
1971 : 41 : conn->maxChunkSize = chunkSize;
1972 : 41 : return 1;
1973 : : }
1974 : : else
8 tgl@sss.pgh.pa.us 1975 :UNC 0 : return 0;
1976 : : }
1977 : :
1978 : : /*
1979 : : * Consume any available input from the backend
1980 : : * 0 return: some kind of trouble
1981 : : * 1 return: no problem
1982 : : */
1983 : : int
9475 bruce@momjian.us 1984 :CBC 449696 : PQconsumeInput(PGconn *conn)
1985 : : {
1986 [ - + ]: 449696 : if (!conn)
9355 bruce@momjian.us 1987 :UBC 0 : return 0;
1988 : :
1989 : : /*
1990 : : * for non-blocking connections try to flush the send-queue, otherwise we
1991 : : * may never get a response for something that may not have already been
1992 : : * sent because it's in our write buffer!
1993 : : */
7666 tgl@sss.pgh.pa.us 1994 [ + + ]:CBC 449696 : if (pqIsnonblocking(conn))
1995 : : {
1996 [ - + ]: 7 : if (pqFlush(conn) < 0)
7666 tgl@sss.pgh.pa.us 1997 :UBC 0 : return 0;
1998 : : }
1999 : :
2000 : : /*
2001 : : * Load more data, if available. We do this no matter what state we are
2002 : : * in, since we are probably getting called because the application wants
2003 : : * to get rid of a read-select condition. Note that we will NOT block
2004 : : * waiting for more input.
2005 : : */
9327 tgl@sss.pgh.pa.us 2006 [ + + ]:CBC 449696 : if (pqReadData(conn) < 0)
9355 bruce@momjian.us 2007 : 82 : return 0;
2008 : :
2009 : : /* Parsing of the data waits till later. */
2010 : 449614 : return 1;
2011 : : }
2012 : :
2013 : :
2014 : : /*
2015 : : * parseInput: if appropriate, parse input data from backend
2016 : : * until input is exhausted or a stopping state is reached.
2017 : : * Note that this function will NOT attempt to read more data from the backend.
2018 : : */
2019 : : static void
9475 2020 : 1631209 : parseInput(PGconn *conn)
2021 : : {
1137 heikki.linnakangas@i 2022 : 1631209 : pqParseInput3(conn);
10141 scrappy@hub.org 2023 : 1631209 : }
2024 : :
2025 : : /*
2026 : : * PQisBusy
2027 : : * Return true if PQgetResult would block waiting for input.
2028 : : */
2029 : :
2030 : : int
9475 bruce@momjian.us 2031 : 138715 : PQisBusy(PGconn *conn)
2032 : : {
9716 2033 [ - + ]: 138715 : if (!conn)
2433 peter_e@gmx.net 2034 :UBC 0 : return false;
2035 : :
2036 : : /* Parse any available data, if our state permits. */
9475 bruce@momjian.us 2037 :CBC 138715 : parseInput(conn);
2038 : :
2039 : : /*
2040 : : * PQgetResult will return immediately in all states except BUSY. Also,
2041 : : * if we've detected read EOF and dropped the connection, we can expect
2042 : : * that PQgetResult will fail immediately. Note that we do *not* check
2043 : : * conn->write_failed here --- once that's become set, we know we have
2044 : : * trouble, but we need to keep trying to read until we have a complete
2045 : : * server message or detect read EOF.
2046 : : */
792 tgl@sss.pgh.pa.us 2047 [ + + + - ]: 138715 : return conn->asyncStatus == PGASYNC_BUSY && conn->status != CONNECTION_BAD;
2048 : : }
2049 : :
2050 : : /*
2051 : : * PQgetResult
2052 : : * Get the next PGresult produced by a query. Returns NULL if no
2053 : : * query work remains or an error has occurred (e.g. out of
2054 : : * memory).
2055 : : *
2056 : : * In pipeline mode, once all the result of a query have been returned,
2057 : : * PQgetResult returns NULL to let the user know that the next
2058 : : * query is being processed. At the end of the pipeline, returns a
2059 : : * result with PQresultStatus(result) == PGRES_PIPELINE_SYNC.
2060 : : */
2061 : : PGresult *
9475 bruce@momjian.us 2062 : 667889 : PQgetResult(PGconn *conn)
2063 : : {
2064 : : PGresult *res;
2065 : :
2066 [ - + ]: 667889 : if (!conn)
9475 bruce@momjian.us 2067 :UBC 0 : return NULL;
2068 : :
2069 : : /* Parse any available data, if our state permits. */
9475 bruce@momjian.us 2070 :CBC 667889 : parseInput(conn);
2071 : :
2072 : : /* If not ready to return something, block until we are. */
2073 [ + + ]: 1015978 : while (conn->asyncStatus == PGASYNC_BUSY)
2074 : : {
2075 : : int flushResult;
2076 : :
2077 : : /*
2078 : : * If data remains unsent, send it. Else we might be waiting for the
2079 : : * result of a command the backend hasn't even got yet.
2080 : : */
7603 tgl@sss.pgh.pa.us 2081 [ - + ]: 348106 : while ((flushResult = pqFlush(conn)) > 0)
2082 : : {
2433 peter_e@gmx.net 2083 [ # # ]:UBC 0 : if (pqWait(false, true, conn))
2084 : : {
7603 tgl@sss.pgh.pa.us 2085 : 0 : flushResult = -1;
2086 : 0 : break;
2087 : : }
2088 : : }
2089 : :
2090 : : /*
2091 : : * Wait for some more data, and load it. (Note: if the connection has
2092 : : * been lost, pqWait should return immediately because the socket
2093 : : * should be read-ready, either with the last server data or with an
2094 : : * EOF indication. We expect therefore that this won't result in any
2095 : : * undue delay in reporting a previous write failure.)
2096 : : */
7603 tgl@sss.pgh.pa.us 2097 [ + - + + ]:CBC 696212 : if (flushResult ||
2433 peter_e@gmx.net 2098 [ + + ]: 696210 : pqWait(true, false, conn) ||
9475 bruce@momjian.us 2099 : 348104 : pqReadData(conn) < 0)
2100 : : {
2101 : : /* Report the error saved by pqWait or pqReadData */
7616 tgl@sss.pgh.pa.us 2102 : 17 : pqSaveErrorResult(conn);
9475 bruce@momjian.us 2103 : 17 : conn->asyncStatus = PGASYNC_IDLE;
7616 tgl@sss.pgh.pa.us 2104 : 17 : return pqPrepareAsyncResult(conn);
2105 : : }
2106 : :
2107 : : /* Parse it. */
9475 bruce@momjian.us 2108 : 348089 : parseInput(conn);
2109 : :
2110 : : /*
2111 : : * If we had a write error, but nothing above obtained a query result
2112 : : * or detected a read error, report the write error.
2113 : : */
1853 tgl@sss.pgh.pa.us 2114 [ - + - - ]: 348089 : if (conn->write_failed && conn->asyncStatus == PGASYNC_BUSY)
2115 : : {
1853 tgl@sss.pgh.pa.us 2116 :UBC 0 : pqSaveWriteError(conn);
2117 : 0 : conn->asyncStatus = PGASYNC_IDLE;
2118 : 0 : return pqPrepareAsyncResult(conn);
2119 : : }
2120 : : }
2121 : :
2122 : : /* Return the appropriate thing. */
9475 bruce@momjian.us 2123 [ + + + + :CBC 667872 : switch (conn->asyncStatus)
+ + + - ]
2124 : : {
2125 : 361418 : case PGASYNC_IDLE:
2126 : 361418 : res = NULL; /* query is complete */
2127 : 361418 : break;
649 alvherre@alvh.no-ip. 2128 : 1849 : case PGASYNC_PIPELINE_IDLE:
2129 [ - + ]: 1849 : Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
2130 : :
2131 : : /*
2132 : : * We're about to return the NULL that terminates the round of
2133 : : * results from the current query; prepare to send the results of
2134 : : * the next query, if any, when we're called next. If there's no
2135 : : * next element in the command queue, this gets us in IDLE state.
2136 : : */
2137 : 1849 : pqPipelineProcessQueue(conn);
2138 : 1849 : res = NULL; /* query is complete */
2139 : 1849 : break;
2140 : :
9475 bruce@momjian.us 2141 : 296714 : case PGASYNC_READY:
1126 alvherre@alvh.no-ip. 2142 : 296714 : res = pqPrepareAsyncResult(conn);
2143 : :
2144 : : /*
2145 : : * Normally pqPrepareAsyncResult will have left conn->result
2146 : : * empty. Otherwise, "res" must be a not-full PGRES_TUPLES_CHUNK
2147 : : * result, which we want to return to the caller while staying in
2148 : : * PGASYNC_READY state. Then the next call here will return the
2149 : : * empty PGRES_TUPLES_OK result that was restored from
2150 : : * saved_result, after which we can proceed.
2151 : : */
8 tgl@sss.pgh.pa.us 2152 [ + + ]:GNC 296714 : if (conn->result)
2153 : : {
2154 [ - + ]: 4 : Assert(res->resultStatus == PGRES_TUPLES_CHUNK);
2155 : 4 : break;
2156 : : }
2157 : :
2158 : : /* Advance the queue as appropriate */
131 alvherre@alvh.no-ip. 2159 :CBC 296710 : pqCommandQueueAdvance(conn, false,
2160 : 296710 : res->resultStatus == PGRES_PIPELINE_SYNC);
2161 : :
1126 2162 [ + + ]: 296710 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
2163 : : {
2164 : : /*
2165 : : * We're about to send the results of the current query. Set
2166 : : * us idle now, and ...
2167 : : */
649 2168 : 1915 : conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
2169 : :
2170 : : /*
2171 : : * ... in cases when we're sending a pipeline-sync result,
2172 : : * move queue processing forwards immediately, so that next
2173 : : * time we're called, we're prepared to return the next result
2174 : : * received from the server. In all other cases, leave the
2175 : : * queue state change for next time, so that a terminating
2176 : : * NULL result is sent.
2177 : : *
2178 : : * (In other words: we don't return a NULL after a pipeline
2179 : : * sync.)
2180 : : */
89 2181 [ + + ]: 1915 : if (res->resultStatus == PGRES_PIPELINE_SYNC)
1126 2182 : 65 : pqPipelineProcessQueue(conn);
2183 : : }
2184 : : else
2185 : : {
2186 : : /* Set the state back to BUSY, allowing parsing to proceed. */
2187 : 294795 : conn->asyncStatus = PGASYNC_BUSY;
2188 : : }
2189 : 296710 : break;
2190 : 2609 : case PGASYNC_READY_MORE:
7616 tgl@sss.pgh.pa.us 2191 : 2609 : res = pqPrepareAsyncResult(conn);
2192 : : /* Set the state back to BUSY, allowing parsing to proceed. */
9475 bruce@momjian.us 2193 : 2609 : conn->asyncStatus = PGASYNC_BUSY;
2194 : 2609 : break;
2195 : 474 : case PGASYNC_COPY_IN:
3714 tgl@sss.pgh.pa.us 2196 : 474 : res = getCopyResult(conn, PGRES_COPY_IN);
9475 bruce@momjian.us 2197 : 474 : break;
2198 : 4213 : case PGASYNC_COPY_OUT:
3714 tgl@sss.pgh.pa.us 2199 : 4213 : res = getCopyResult(conn, PGRES_COPY_OUT);
9475 bruce@momjian.us 2200 : 4213 : break;
4873 rhaas@postgresql.org 2201 : 595 : case PGASYNC_COPY_BOTH:
3714 tgl@sss.pgh.pa.us 2202 : 595 : res = getCopyResult(conn, PGRES_COPY_BOTH);
4873 rhaas@postgresql.org 2203 : 595 : break;
9475 bruce@momjian.us 2204 :UBC 0 : default:
516 peter@eisentraut.org 2205 : 0 : libpq_append_conn_error(conn, "unexpected asyncStatus: %d", (int) conn->asyncStatus);
786 tgl@sss.pgh.pa.us 2206 : 0 : pqSaveErrorResult(conn);
2207 : 0 : conn->asyncStatus = PGASYNC_IDLE; /* try to restore valid state */
2208 : 0 : res = pqPrepareAsyncResult(conn);
9475 bruce@momjian.us 2209 : 0 : break;
2210 : : }
2211 : :
2212 : : /* Time to fire PGEVT_RESULTCREATE events, if there are any */
786 tgl@sss.pgh.pa.us 2213 [ + + - + ]:CBC 667872 : if (res && res->nEvents > 0)
786 tgl@sss.pgh.pa.us 2214 :UBC 0 : (void) PQfireResultCreateEvents(conn, res);
2215 : :
9475 bruce@momjian.us 2216 :CBC 667872 : return res;
2217 : : }
2218 : :
2219 : : /*
2220 : : * getCopyResult
2221 : : * Helper for PQgetResult: generate result for COPY-in-progress cases
2222 : : */
2223 : : static PGresult *
3714 tgl@sss.pgh.pa.us 2224 : 5282 : getCopyResult(PGconn *conn, ExecStatusType copytype)
2225 : : {
2226 : : /*
2227 : : * If the server connection has been lost, don't pretend everything is
2228 : : * hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the
2229 : : * asyncStatus to idle (corresponding to what we'd do if we'd detected I/O
2230 : : * error in the earlier steps in PQgetResult). The text returned in the
2231 : : * result is whatever is in conn->errorMessage; we hope that was filled
2232 : : * with something relevant when the lost connection was detected.
2233 : : */
2234 [ - + ]: 5282 : if (conn->status != CONNECTION_OK)
2235 : : {
3714 tgl@sss.pgh.pa.us 2236 :UBC 0 : pqSaveErrorResult(conn);
2237 : 0 : conn->asyncStatus = PGASYNC_IDLE;
2238 : 0 : return pqPrepareAsyncResult(conn);
2239 : : }
2240 : :
2241 : : /* If we have an async result for the COPY, return that */
3714 tgl@sss.pgh.pa.us 2242 [ + + + - ]:CBC 5282 : if (conn->result && conn->result->resultStatus == copytype)
2243 : 5101 : return pqPrepareAsyncResult(conn);
2244 : :
2245 : : /* Otherwise, invent a suitable PGresult */
2246 : 181 : return PQmakeEmptyPGresult(conn, copytype);
2247 : : }
2248 : :
2249 : :
2250 : : /*
2251 : : * PQexec
2252 : : * send a query to the backend and package up the result in a PGresult
2253 : : *
2254 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2255 : : * a relevant message.
2256 : : * If the query was sent, a new PGresult is returned (which could indicate
2257 : : * either success or failure).
2258 : : * The user is responsible for freeing the PGresult via PQclear()
2259 : : * when done with it.
2260 : : */
2261 : : PGresult *
9475 bruce@momjian.us 2262 : 68034 : PQexec(PGconn *conn, const char *query)
2263 : : {
7603 tgl@sss.pgh.pa.us 2264 [ + + ]: 68034 : if (!PQexecStart(conn))
2265 : 1 : return NULL;
2266 [ - + ]: 68033 : if (!PQsendQuery(conn, query))
7603 tgl@sss.pgh.pa.us 2267 :UBC 0 : return NULL;
7603 tgl@sss.pgh.pa.us 2268 :CBC 68033 : return PQexecFinish(conn);
2269 : : }
2270 : :
2271 : : /*
2272 : : * PQexecParams
2273 : : * Like PQexec, but use extended query protocol so we can pass parameters
2274 : : */
2275 : : PGresult *
2276 : 937 : PQexecParams(PGconn *conn,
2277 : : const char *command,
2278 : : int nParams,
2279 : : const Oid *paramTypes,
2280 : : const char *const *paramValues,
2281 : : const int *paramLengths,
2282 : : const int *paramFormats,
2283 : : int resultFormat)
2284 : : {
2285 [ - + ]: 937 : if (!PQexecStart(conn))
7603 tgl@sss.pgh.pa.us 2286 :UBC 0 : return NULL;
7603 tgl@sss.pgh.pa.us 2287 [ - + ]:CBC 937 : if (!PQsendQueryParams(conn, command,
2288 : : nParams, paramTypes, paramValues, paramLengths,
2289 : : paramFormats, resultFormat))
8853 bruce@momjian.us 2290 :UBC 0 : return NULL;
7603 tgl@sss.pgh.pa.us 2291 :CBC 937 : return PQexecFinish(conn);
2292 : : }
2293 : :
2294 : : /*
2295 : : * PQprepare
2296 : : * Creates a prepared statement by issuing a Parse message.
2297 : : *
2298 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2299 : : * a relevant message.
2300 : : * If the query was sent, a new PGresult is returned (which could indicate
2301 : : * either success or failure).
2302 : : * The user is responsible for freeing the PGresult via PQclear()
2303 : : * when done with it.
2304 : : */
2305 : : PGresult *
7118 2306 : 1127 : PQprepare(PGconn *conn,
2307 : : const char *stmtName, const char *query,
2308 : : int nParams, const Oid *paramTypes)
2309 : : {
2310 [ - + ]: 1127 : if (!PQexecStart(conn))
7118 tgl@sss.pgh.pa.us 2311 :UBC 0 : return NULL;
7118 tgl@sss.pgh.pa.us 2312 [ - + ]:CBC 1127 : if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
7118 tgl@sss.pgh.pa.us 2313 :UBC 0 : return NULL;
7118 tgl@sss.pgh.pa.us 2314 :CBC 1127 : return PQexecFinish(conn);
2315 : : }
2316 : :
2317 : : /*
2318 : : * PQexecPrepared
2319 : : * Like PQexec, but execute a previously prepared statement,
2320 : : * using extended query protocol so we can pass parameters
2321 : : */
2322 : : PGresult *
7550 2323 : 2745 : PQexecPrepared(PGconn *conn,
2324 : : const char *stmtName,
2325 : : int nParams,
2326 : : const char *const *paramValues,
2327 : : const int *paramLengths,
2328 : : const int *paramFormats,
2329 : : int resultFormat)
2330 : : {
2331 [ - + ]: 2745 : if (!PQexecStart(conn))
7550 tgl@sss.pgh.pa.us 2332 :UBC 0 : return NULL;
7550 tgl@sss.pgh.pa.us 2333 [ - + ]:CBC 2745 : if (!PQsendQueryPrepared(conn, stmtName,
2334 : : nParams, paramValues, paramLengths,
2335 : : paramFormats, resultFormat))
7550 tgl@sss.pgh.pa.us 2336 :UBC 0 : return NULL;
7550 tgl@sss.pgh.pa.us 2337 :CBC 2745 : return PQexecFinish(conn);
2338 : : }
2339 : :
2340 : : /*
2341 : : * Common code for PQexec and sibling routines: prepare to send command
2342 : : */
2343 : : static bool
7603 2344 : 72895 : PQexecStart(PGconn *conn)
2345 : : {
2346 : : PGresult *result;
2347 : :
2348 [ - + ]: 72895 : if (!conn)
7603 tgl@sss.pgh.pa.us 2349 :UBC 0 : return false;
2350 : :
2351 : : /*
2352 : : * Since this is the beginning of a query cycle, reset the error state.
2353 : : * However, in pipeline mode with something already queued, the error
2354 : : * buffer belongs to that command and we shouldn't clear it.
2355 : : */
776 tgl@sss.pgh.pa.us 2356 [ + + ]:CBC 72895 : if (conn->cmd_queue_head == NULL)
2357 : 72888 : pqClearConnErrorState(conn);
2358 : :
1126 alvherre@alvh.no-ip. 2359 [ + + ]: 72895 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
2360 : : {
516 peter@eisentraut.org 2361 : 1 : libpq_append_conn_error(conn, "synchronous command execution functions are not allowed in pipeline mode");
1126 alvherre@alvh.no-ip. 2362 : 1 : return false;
2363 : : }
2364 : :
2365 : : /*
2366 : : * Silently discard any prior query result that application didn't eat.
2367 : : * This is probably poor design, but it's here for backward compatibility.
2368 : : */
4273 tgl@sss.pgh.pa.us 2369 [ - + ]: 72894 : while ((result = PQgetResult(conn)) != NULL)
2370 : : {
7550 tgl@sss.pgh.pa.us 2371 :UBC 0 : ExecStatusType resultStatus = result->resultStatus;
2372 : :
2373 : 0 : PQclear(result); /* only need its status */
2374 [ # # ]: 0 : if (resultStatus == PGRES_COPY_IN)
2375 : : {
2376 : : /* get out of a COPY IN state */
1137 heikki.linnakangas@i 2377 [ # # ]: 0 : if (PQputCopyEnd(conn,
2378 : 0 : libpq_gettext("COPY terminated by new PQexec")) < 0)
7596 tgl@sss.pgh.pa.us 2379 : 0 : return false;
2380 : : /* keep waiting to swallow the copy's failure message */
2381 : : }
7550 2382 [ # # ]: 0 : else if (resultStatus == PGRES_COPY_OUT)
2383 : : {
2384 : : /*
2385 : : * Get out of a COPY OUT state: we just switch back to BUSY and
2386 : : * allow the remaining COPY data to be dropped on the floor.
2387 : : */
1137 heikki.linnakangas@i 2388 : 0 : conn->asyncStatus = PGASYNC_BUSY;
2389 : : /* keep waiting to swallow the copy's completion message */
2390 : : }
4873 rhaas@postgresql.org 2391 [ # # ]: 0 : else if (resultStatus == PGRES_COPY_BOTH)
2392 : : {
2393 : : /* We don't allow PQexec during COPY BOTH */
516 peter@eisentraut.org 2394 : 0 : libpq_append_conn_error(conn, "PQexec not allowed during COPY BOTH");
4753 bruce@momjian.us 2395 : 0 : return false;
2396 : : }
2397 : : /* check for loss of connection, too */
7413 tgl@sss.pgh.pa.us 2398 [ # # ]: 0 : if (conn->status == CONNECTION_BAD)
2399 : 0 : return false;
2400 : : }
2401 : :
2402 : : /* OK to send a command */
7603 tgl@sss.pgh.pa.us 2403 :CBC 72894 : return true;
2404 : : }
2405 : :
2406 : : /*
2407 : : * Common code for PQexec and sibling routines: wait for command result
2408 : : */
2409 : : static PGresult *
2410 : 72894 : PQexecFinish(PGconn *conn)
2411 : : {
2412 : : PGresult *result;
2413 : : PGresult *lastResult;
2414 : :
2415 : : /*
2416 : : * For backwards compatibility, return the last result if there are more
2417 : : * than one. (We used to have logic here to concatenate successive error
2418 : : * messages, but now that happens automatically, since conn->errorMessage
2419 : : * will continue to accumulate errors throughout this loop.)
2420 : : *
2421 : : * We have to stop if we see copy in/out/both, however. We will resume
2422 : : * parsing after application performs the data transfer.
2423 : : *
2424 : : * Also stop if the connection is lost (else we'll loop infinitely).
2425 : : */
9475 bruce@momjian.us 2426 : 72894 : lastResult = NULL;
2427 [ + + ]: 157618 : while ((result = PQgetResult(conn)) != NULL)
2428 : : {
651 peter@eisentraut.org 2429 : 88358 : PQclear(lastResult);
9475 bruce@momjian.us 2430 : 88358 : lastResult = result;
2431 [ + + ]: 88358 : if (result->resultStatus == PGRES_COPY_IN ||
7413 tgl@sss.pgh.pa.us 2432 [ + + ]: 88343 : result->resultStatus == PGRES_COPY_OUT ||
4873 rhaas@postgresql.org 2433 [ + + ]: 84864 : result->resultStatus == PGRES_COPY_BOTH ||
7413 tgl@sss.pgh.pa.us 2434 [ + - ]: 84724 : conn->status == CONNECTION_BAD)
2435 : : break;
2436 : : }
2437 : :
9475 bruce@momjian.us 2438 : 72894 : return lastResult;
2439 : : }
2440 : :
2441 : : /*
2442 : : * PQdescribePrepared
2443 : : * Obtain information about a previously prepared statement
2444 : : *
2445 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2446 : : * a relevant message.
2447 : : * If the query was sent, a new PGresult is returned (which could indicate
2448 : : * either success or failure). On success, the PGresult contains status
2449 : : * PGRES_COMMAND_OK, and its parameter and column-heading fields describe
2450 : : * the statement's inputs and outputs respectively.
2451 : : * The user is responsible for freeing the PGresult via PQclear()
2452 : : * when done with it.
2453 : : */
2454 : : PGresult *
6449 tgl@sss.pgh.pa.us 2455 : 49 : PQdescribePrepared(PGconn *conn, const char *stmt)
2456 : : {
2457 [ - + ]: 49 : if (!PQexecStart(conn))
6449 tgl@sss.pgh.pa.us 2458 :UBC 0 : return NULL;
236 nathan@postgresql.or 2459 [ - + ]:GNC 49 : if (!PQsendTypedCommand(conn, PqMsg_Describe, 'S', stmt))
6449 tgl@sss.pgh.pa.us 2460 :UBC 0 : return NULL;
6449 tgl@sss.pgh.pa.us 2461 :CBC 49 : return PQexecFinish(conn);
2462 : : }
2463 : :
2464 : : /*
2465 : : * PQdescribePortal
2466 : : * Obtain information about a previously created portal
2467 : : *
2468 : : * This is much like PQdescribePrepared, except that no parameter info is
2469 : : * returned. Note that at the moment, libpq doesn't really expose portals
2470 : : * to the client; but this can be used with a portal created by a SQL
2471 : : * DECLARE CURSOR command.
2472 : : */
2473 : : PGresult *
6449 tgl@sss.pgh.pa.us 2474 :GBC 1 : PQdescribePortal(PGconn *conn, const char *portal)
2475 : : {
2476 [ - + ]: 1 : if (!PQexecStart(conn))
6449 tgl@sss.pgh.pa.us 2477 :UBC 0 : return NULL;
236 nathan@postgresql.or 2478 [ - + ]:GNC 1 : if (!PQsendTypedCommand(conn, PqMsg_Describe, 'P', portal))
6449 tgl@sss.pgh.pa.us 2479 :UBC 0 : return NULL;
6449 tgl@sss.pgh.pa.us 2480 :GBC 1 : return PQexecFinish(conn);
2481 : : }
2482 : :
2483 : : /*
2484 : : * PQsendDescribePrepared
2485 : : * Submit a Describe Statement command, but don't wait for it to finish
2486 : : *
2487 : : * Returns: 1 if successfully submitted
2488 : : * 0 if error (conn->errorMessage is set)
2489 : : */
2490 : : int
6449 tgl@sss.pgh.pa.us 2491 :CBC 1 : PQsendDescribePrepared(PGconn *conn, const char *stmt)
2492 : : {
236 nathan@postgresql.or 2493 :GNC 1 : return PQsendTypedCommand(conn, PqMsg_Describe, 'S', stmt);
2494 : : }
2495 : :
2496 : : /*
2497 : : * PQsendDescribePortal
2498 : : * Submit a Describe Portal command, but don't wait for it to finish
2499 : : *
2500 : : * Returns: 1 if successfully submitted
2501 : : * 0 if error (conn->errorMessage is set)
2502 : : */
2503 : : int
6449 tgl@sss.pgh.pa.us 2504 :CBC 1 : PQsendDescribePortal(PGconn *conn, const char *portal)
2505 : : {
236 nathan@postgresql.or 2506 :GNC 1 : return PQsendTypedCommand(conn, PqMsg_Describe, 'P', portal);
2507 : : }
2508 : :
2509 : : /*
2510 : : * PQclosePrepared
2511 : : * Close a previously prepared statement
2512 : : *
2513 : : * If the query was not even sent, return NULL; conn->errorMessage is set to
2514 : : * a relevant message.
2515 : : * If the query was sent, a new PGresult is returned (which could indicate
2516 : : * either success or failure). On success, the PGresult contains status
2517 : : * PGRES_COMMAND_OK. The user is responsible for freeing the PGresult via
2518 : : * PQclear() when done with it.
2519 : : */
2520 : : PGresult *
285 michael@paquier.xyz 2521 : 1 : PQclosePrepared(PGconn *conn, const char *stmt)
2522 : : {
2523 [ - + ]: 1 : if (!PQexecStart(conn))
285 michael@paquier.xyz 2524 :UNC 0 : return NULL;
236 nathan@postgresql.or 2525 [ - + ]:GNC 1 : if (!PQsendTypedCommand(conn, PqMsg_Close, 'S', stmt))
285 michael@paquier.xyz 2526 :UNC 0 : return NULL;
285 michael@paquier.xyz 2527 :GNC 1 : return PQexecFinish(conn);
2528 : : }
2529 : :
2530 : : /*
2531 : : * PQclosePortal
2532 : : * Close a previously created portal
2533 : : *
2534 : : * This is exactly like PQclosePrepared, but for portals. Note that at the
2535 : : * moment, libpq doesn't really expose portals to the client; but this can be
2536 : : * used with a portal created by a SQL DECLARE CURSOR command.
2537 : : */
2538 : : PGresult *
2539 : 1 : PQclosePortal(PGconn *conn, const char *portal)
2540 : : {
2541 [ - + ]: 1 : if (!PQexecStart(conn))
285 michael@paquier.xyz 2542 :UNC 0 : return NULL;
236 nathan@postgresql.or 2543 [ - + ]:GNC 1 : if (!PQsendTypedCommand(conn, PqMsg_Close, 'P', portal))
285 michael@paquier.xyz 2544 :UNC 0 : return NULL;
285 michael@paquier.xyz 2545 :GNC 1 : return PQexecFinish(conn);
2546 : : }
2547 : :
2548 : : /*
2549 : : * PQsendClosePrepared
2550 : : * Submit a Close Statement command, but don't wait for it to finish
2551 : : *
2552 : : * Returns: 1 if successfully submitted
2553 : : * 0 if error (conn->errorMessage is set)
2554 : : */
2555 : : int
2556 : 1 : PQsendClosePrepared(PGconn *conn, const char *stmt)
2557 : : {
236 nathan@postgresql.or 2558 : 1 : return PQsendTypedCommand(conn, PqMsg_Close, 'S', stmt);
2559 : : }
2560 : :
2561 : : /*
2562 : : * PQsendClosePortal
2563 : : * Submit a Close Portal command, but don't wait for it to finish
2564 : : *
2565 : : * Returns: 1 if successfully submitted
2566 : : * 0 if error (conn->errorMessage is set)
2567 : : */
2568 : : int
285 michael@paquier.xyz 2569 : 1 : PQsendClosePortal(PGconn *conn, const char *portal)
2570 : : {
236 nathan@postgresql.or 2571 : 1 : return PQsendTypedCommand(conn, PqMsg_Close, 'P', portal);
2572 : : }
2573 : :
2574 : : /*
2575 : : * PQsendTypedCommand
2576 : : * Common code to send a Describe or Close command
2577 : : *
2578 : : * Available options for "command" are
2579 : : * PqMsg_Close for Close; or
2580 : : * PqMsg_Describe for Describe.
2581 : : *
2582 : : * Available options for "type" are
2583 : : * 'S' to run a command on a prepared statement; or
2584 : : * 'P' to run a command on a portal.
2585 : : *
2586 : : * Returns 1 on success and 0 on failure.
2587 : : */
2588 : : static int
285 michael@paquier.xyz 2589 : 56 : PQsendTypedCommand(PGconn *conn, char command, char type, const char *target)
2590 : : {
1126 alvherre@alvh.no-ip. 2591 :CBC 56 : PGcmdQueueEntry *entry = NULL;
2592 : :
2593 : : /* Treat null target as empty string */
285 michael@paquier.xyz 2594 [ - + ]:GNC 56 : if (!target)
285 michael@paquier.xyz 2595 :UNC 0 : target = "";
2596 : :
1189 tgl@sss.pgh.pa.us 2597 [ - + ]:CBC 56 : if (!PQsendQueryStart(conn, true))
6449 tgl@sss.pgh.pa.us 2598 :UBC 0 : return 0;
2599 : :
1126 alvherre@alvh.no-ip. 2600 :CBC 56 : entry = pqAllocCmdQueueEntry(conn);
2601 [ - + ]: 56 : if (entry == NULL)
1126 alvherre@alvh.no-ip. 2602 :UBC 0 : return 0; /* error msg already set */
2603 : :
2604 : : /* construct the Close message */
285 michael@paquier.xyz 2605 [ + - + - ]:GNC 112 : if (pqPutMsgStart(command, conn) < 0 ||
2606 [ + - ]: 112 : pqPutc(type, conn) < 0 ||
2607 [ - + ]: 112 : pqPuts(target, conn) < 0 ||
6449 tgl@sss.pgh.pa.us 2608 :CBC 56 : pqPutMsgEnd(conn) < 0)
6449 tgl@sss.pgh.pa.us 2609 :UBC 0 : goto sendFailed;
2610 : :
2611 : : /* construct the Sync message */
1126 alvherre@alvh.no-ip. 2612 [ + + ]:CBC 56 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
2613 : : {
236 nathan@postgresql.or 2614 [ + - - + ]:GNC 104 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1126 alvherre@alvh.no-ip. 2615 :CBC 52 : pqPutMsgEnd(conn) < 0)
1126 alvherre@alvh.no-ip. 2616 :UBC 0 : goto sendFailed;
2617 : : }
2618 : :
2619 : : /* remember if we are doing a Close or a Describe */
236 nathan@postgresql.or 2620 [ + + ]:GNC 56 : if (command == PqMsg_Close)
2621 : : {
285 michael@paquier.xyz 2622 : 4 : entry->queryclass = PGQUERY_CLOSE;
2623 : : }
236 nathan@postgresql.or 2624 [ + - ]: 52 : else if (command == PqMsg_Describe)
2625 : : {
285 michael@paquier.xyz 2626 : 52 : entry->queryclass = PGQUERY_DESCRIBE;
2627 : : }
2628 : : else
2629 : : {
285 michael@paquier.xyz 2630 :UNC 0 : libpq_append_conn_error(conn, "unknown command type provided");
2631 : 0 : goto sendFailed;
2632 : : }
2633 : :
2634 : : /*
2635 : : * Give the data a push (in pipeline mode, only if we're past the size
2636 : : * threshold). In nonblock mode, don't complain if we're unable to send
2637 : : * it all; PQgetResult() will do any additional flushing needed.
2638 : : */
1126 alvherre@alvh.no-ip. 2639 [ - + ]:CBC 56 : if (pqPipelineFlush(conn) < 0)
6449 tgl@sss.pgh.pa.us 2640 :UBC 0 : goto sendFailed;
2641 : :
2642 : : /* OK, it's launched! */
1126 alvherre@alvh.no-ip. 2643 :CBC 56 : pqAppendCmdQueueEntry(conn, entry);
2644 : :
6449 tgl@sss.pgh.pa.us 2645 : 56 : return 1;
2646 : :
6449 tgl@sss.pgh.pa.us 2647 :UBC 0 : sendFailed:
1126 alvherre@alvh.no-ip. 2648 : 0 : pqRecycleCmdQueueEntry(conn, entry);
2649 : : /* error message should be set up already */
6449 tgl@sss.pgh.pa.us 2650 : 0 : return 0;
2651 : : }
2652 : :
2653 : : /*
2654 : : * PQnotifies
2655 : : * returns a PGnotify* structure of the latest async notification
2656 : : * that has not yet been handled
2657 : : *
2658 : : * returns NULL, if there is currently
2659 : : * no unhandled async notification from the backend
2660 : : *
2661 : : * the CALLER is responsible for FREE'ing the structure returned
2662 : : *
2663 : : * Note that this function does not read any new data from the socket;
2664 : : * so usually, caller should call PQconsumeInput() first.
2665 : : */
2666 : : PGnotify *
9475 bruce@momjian.us 2667 :CBC 174642 : PQnotifies(PGconn *conn)
2668 : : {
2669 : : PGnotify *event;
2670 : :
2671 [ - + ]: 174642 : if (!conn)
9475 bruce@momjian.us 2672 :UBC 0 : return NULL;
2673 : :
2674 : : /* Parse any available data to see if we can extract NOTIFY messages. */
9475 bruce@momjian.us 2675 :CBC 174642 : parseInput(conn);
2676 : :
7120 tgl@sss.pgh.pa.us 2677 : 174642 : event = conn->notifyHead;
2678 [ + + ]: 174642 : if (event)
2679 : : {
2680 : 31 : conn->notifyHead = event->next;
2681 [ + + ]: 31 : if (!conn->notifyHead)
2682 : 19 : conn->notifyTail = NULL;
2683 : 31 : event->next = NULL; /* don't let app see the internal state */
2684 : : }
9475 bruce@momjian.us 2685 : 174642 : return event;
2686 : : }
2687 : :
2688 : : /*
2689 : : * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH
2690 : : *
2691 : : * Returns 1 if successful, 0 if data could not be sent (only possible
2692 : : * in nonblock mode), or -1 if an error occurs.
2693 : : */
2694 : : int
7603 tgl@sss.pgh.pa.us 2695 : 301874 : PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
2696 : : {
2697 [ - + ]: 301874 : if (!conn)
7603 tgl@sss.pgh.pa.us 2698 :UBC 0 : return -1;
4873 rhaas@postgresql.org 2699 [ + + ]:CBC 301874 : if (conn->asyncStatus != PGASYNC_COPY_IN &&
2700 [ - + ]: 101209 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2701 : : {
516 peter@eisentraut.org 2702 :UBC 0 : libpq_append_conn_error(conn, "no COPY in progress");
7603 tgl@sss.pgh.pa.us 2703 : 0 : return -1;
2704 : : }
2705 : :
2706 : : /*
2707 : : * Process any NOTICE or NOTIFY messages that might be pending in the
2708 : : * input buffer. Since the server might generate many notices during the
2709 : : * COPY, we want to clean those out reasonably promptly to prevent
2710 : : * indefinite expansion of the input buffer. (Note: the actual read of
2711 : : * input data into the input buffer happens down inside pqSendSome, but
2712 : : * it's not authorized to get rid of the data again.)
2713 : : */
7483 tgl@sss.pgh.pa.us 2714 :CBC 301874 : parseInput(conn);
2715 : :
7603 2716 [ + - ]: 301874 : if (nbytes > 0)
2717 : : {
2718 : : /*
2719 : : * Try to flush any previously sent data in preference to growing the
2720 : : * output buffer. If we can't enlarge the buffer enough to hold the
2721 : : * data, return 0 in the nonblock case, else hard error. (For
2722 : : * simplicity, always assume 5 bytes of overhead.)
2723 : : */
2724 [ + + ]: 301874 : if ((conn->outBufSize - conn->outCount - 5) < nbytes)
2725 : : {
7603 tgl@sss.pgh.pa.us 2726 [ - + ]:GBC 15 : if (pqFlush(conn) < 0)
7603 tgl@sss.pgh.pa.us 2727 :UBC 0 : return -1;
5799 tgl@sss.pgh.pa.us 2728 [ - + ]:GBC 15 : if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes,
2729 : : conn))
7603 tgl@sss.pgh.pa.us 2730 [ # # ]:UBC 0 : return pqIsnonblocking(conn) ? 0 : -1;
2731 : : }
2732 : : /* Send the data (too simple to delegate to fe-protocol files) */
236 nathan@postgresql.or 2733 [ + - + - ]:GNC 603748 : if (pqPutMsgStart(PqMsg_CopyData, conn) < 0 ||
1137 heikki.linnakangas@i 2734 [ - + ]:CBC 603748 : pqPutnchar(buffer, nbytes, conn) < 0 ||
2735 : 301874 : pqPutMsgEnd(conn) < 0)
1137 heikki.linnakangas@i 2736 :UBC 0 : return -1;
2737 : : }
7603 tgl@sss.pgh.pa.us 2738 :CBC 301874 : return 1;
2739 : : }
2740 : :
2741 : : /*
2742 : : * PQputCopyEnd - send EOF indication to the backend during COPY IN
2743 : : *
2744 : : * After calling this, use PQgetResult() to check command completion status.
2745 : : *
2746 : : * Returns 1 if successful, or -1 if an error occurs.
2747 : : */
2748 : : int
2749 : 787 : PQputCopyEnd(PGconn *conn, const char *errormsg)
2750 : : {
2751 [ - + ]: 787 : if (!conn)
7603 tgl@sss.pgh.pa.us 2752 :UBC 0 : return -1;
4140 heikki.linnakangas@i 2753 [ + + ]:CBC 787 : if (conn->asyncStatus != PGASYNC_COPY_IN &&
2754 [ + + ]: 319 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2755 : : {
516 peter@eisentraut.org 2756 : 30 : libpq_append_conn_error(conn, "no COPY in progress");
7603 tgl@sss.pgh.pa.us 2757 : 30 : return -1;
2758 : : }
2759 : :
2760 : : /*
2761 : : * Send the COPY END indicator. This is simple enough that we don't
2762 : : * bother delegating it to the fe-protocol files.
2763 : : */
1137 heikki.linnakangas@i 2764 [ - + ]: 757 : if (errormsg)
2765 : : {
2766 : : /* Send COPY FAIL */
236 nathan@postgresql.or 2767 [ # # # # ]:UNC 0 : if (pqPutMsgStart(PqMsg_CopyFail, conn) < 0 ||
1137 heikki.linnakangas@i 2768 [ # # ]:UBC 0 : pqPuts(errormsg, conn) < 0 ||
2769 : 0 : pqPutMsgEnd(conn) < 0)
2770 : 0 : return -1;
2771 : : }
2772 : : else
2773 : : {
2774 : : /* Send COPY DONE */
236 nathan@postgresql.or 2775 [ + - - + ]:GNC 1514 : if (pqPutMsgStart(PqMsg_CopyDone, conn) < 0 ||
1137 heikki.linnakangas@i 2776 :CBC 757 : pqPutMsgEnd(conn) < 0)
1137 heikki.linnakangas@i 2777 :UBC 0 : return -1;
2778 : : }
2779 : :
2780 : : /*
2781 : : * If we sent the COPY command in extended-query mode, we must issue a
2782 : : * Sync as well.
2783 : : */
1126 alvherre@alvh.no-ip. 2784 [ + - ]:CBC 757 : if (conn->cmd_queue_head &&
2785 [ - + ]: 757 : conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
2786 : : {
236 nathan@postgresql.or 2787 [ # # # # ]:UNC 0 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1137 heikki.linnakangas@i 2788 :UBC 0 : pqPutMsgEnd(conn) < 0)
7603 tgl@sss.pgh.pa.us 2789 : 0 : return -1;
2790 : : }
2791 : :
2792 : : /* Return to active duty */
4140 heikki.linnakangas@i 2793 [ + + ]:CBC 757 : if (conn->asyncStatus == PGASYNC_COPY_BOTH)
2794 : 289 : conn->asyncStatus = PGASYNC_COPY_OUT;
2795 : : else
2796 : 468 : conn->asyncStatus = PGASYNC_BUSY;
2797 : :
2798 : : /* Try to flush data */
7603 tgl@sss.pgh.pa.us 2799 [ - + ]: 757 : if (pqFlush(conn) < 0)
7603 tgl@sss.pgh.pa.us 2800 :UBC 0 : return -1;
2801 : :
7603 tgl@sss.pgh.pa.us 2802 :CBC 757 : return 1;
2803 : : }
2804 : :
2805 : : /*
2806 : : * PQgetCopyData - read a row of data from the backend during COPY OUT
2807 : : * or COPY BOTH
2808 : : *
2809 : : * If successful, sets *buffer to point to a malloc'd row of data, and
2810 : : * returns row length (always > 0) as result.
2811 : : * Returns 0 if no row available yet (only possible if async is true),
2812 : : * -1 if end of copy (consult PQgetResult), or -2 if error (consult
2813 : : * PQerrorMessage).
2814 : : */
2815 : : int
2816 : 2581951 : PQgetCopyData(PGconn *conn, char **buffer, int async)
2817 : : {
2818 : 2581951 : *buffer = NULL; /* for all failure cases */
2819 [ - + ]: 2581951 : if (!conn)
7603 tgl@sss.pgh.pa.us 2820 :UBC 0 : return -2;
4873 rhaas@postgresql.org 2821 [ + + ]:CBC 2581951 : if (conn->asyncStatus != PGASYNC_COPY_OUT &&
2822 [ - + ]: 544256 : conn->asyncStatus != PGASYNC_COPY_BOTH)
2823 : : {
516 peter@eisentraut.org 2824 :UBC 0 : libpq_append_conn_error(conn, "no COPY in progress");
7603 tgl@sss.pgh.pa.us 2825 : 0 : return -2;
2826 : : }
1137 heikki.linnakangas@i 2827 :CBC 2581951 : return pqGetCopyData3(conn, buffer, async);
2828 : : }
2829 : :
2830 : : /*
2831 : : * PQgetline - gets a newline-terminated string from the backend.
2832 : : *
2833 : : * Chiefly here so that applications can use "COPY <rel> to stdout"
2834 : : * and read the output string. Returns a null-terminated string in `buffer`.
2835 : : *
2836 : : * XXX this routine is now deprecated, because it can't handle binary data.
2837 : : * If called during a COPY BINARY we return EOF.
2838 : : *
2839 : : * PQgetline reads up to `length`-1 characters (like fgets(3)) but strips
2840 : : * the terminating \n (like gets(3)).
2841 : : *
2842 : : * CAUTION: the caller is responsible for detecting the end-of-copy signal
2843 : : * (a line containing just "\.") when using this routine.
2844 : : *
2845 : : * RETURNS:
2846 : : * EOF if error (eg, invalid arguments are given)
2847 : : * 0 if EOL is reached (i.e., \n has been read)
2848 : : * (this is required for backward-compatibility -- this
2849 : : * routine used to always return EOF or 0, assuming that
2850 : : * the line ended within `length` bytes.)
2851 : : * 1 in other cases (i.e., the buffer was filled before \n is reached)
2852 : : */
2853 : : int
572 pg@bowt.ie 2854 :UBC 0 : PQgetline(PGconn *conn, char *buffer, int length)
2855 : : {
2856 [ # # # # ]: 0 : if (!buffer || length <= 0)
7616 tgl@sss.pgh.pa.us 2857 : 0 : return EOF;
572 pg@bowt.ie 2858 : 0 : *buffer = '\0';
2859 : : /* length must be at least 3 to hold the \. terminator! */
2860 [ # # ]: 0 : if (length < 3)
9475 bruce@momjian.us 2861 : 0 : return EOF;
2862 : :
7616 tgl@sss.pgh.pa.us 2863 [ # # ]: 0 : if (!conn)
9475 bruce@momjian.us 2864 : 0 : return EOF;
2865 : :
572 pg@bowt.ie 2866 : 0 : return pqGetline3(conn, buffer, length);
2867 : : }
2868 : :
2869 : : /*
2870 : : * PQgetlineAsync - gets a COPY data row without blocking.
2871 : : *
2872 : : * This routine is for applications that want to do "COPY <rel> to stdout"
2873 : : * asynchronously, that is without blocking. Having issued the COPY command
2874 : : * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput
2875 : : * and this routine until the end-of-data signal is detected. Unlike
2876 : : * PQgetline, this routine takes responsibility for detecting end-of-data.
2877 : : *
2878 : : * On each call, PQgetlineAsync will return data if a complete data row
2879 : : * is available in libpq's input buffer. Otherwise, no data is returned
2880 : : * until the rest of the row arrives.
2881 : : *
2882 : : * If -1 is returned, the end-of-data signal has been recognized (and removed
2883 : : * from libpq's input buffer). The caller *must* next call PQendcopy and
2884 : : * then return to normal processing.
2885 : : *
2886 : : * RETURNS:
2887 : : * -1 if the end-of-copy-data marker has been recognized
2888 : : * 0 if no data is available
2889 : : * >0 the number of bytes returned.
2890 : : *
2891 : : * The data returned will not extend beyond a data-row boundary. If possible
2892 : : * a whole row will be returned at one time. But if the buffer offered by
2893 : : * the caller is too small to hold a row sent by the backend, then a partial
2894 : : * data row will be returned. In text mode this can be detected by testing
2895 : : * whether the last returned byte is '\n' or not.
2896 : : *
2897 : : * The returned data is *not* null-terminated.
2898 : : */
2899 : :
2900 : : int
9355 bruce@momjian.us 2901 : 0 : PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
2902 : : {
7616 tgl@sss.pgh.pa.us 2903 [ # # ]: 0 : if (!conn)
7663 2904 : 0 : return -1;
2905 : :
1137 heikki.linnakangas@i 2906 : 0 : return pqGetlineAsync3(conn, buffer, bufsize);
2907 : : }
2908 : :
2909 : : /*
2910 : : * PQputline -- sends a string to the backend during COPY IN.
2911 : : * Returns 0 if OK, EOF if not.
2912 : : *
2913 : : * This is deprecated primarily because the return convention doesn't allow
2914 : : * caller to tell the difference between a hard error and a nonblock-mode
2915 : : * send failure.
2916 : : */
2917 : : int
572 pg@bowt.ie 2918 :CBC 200028 : PQputline(PGconn *conn, const char *string)
2919 : : {
2920 : 200028 : return PQputnbytes(conn, string, strlen(string));
2921 : : }
2922 : :
2923 : : /*
2924 : : * PQputnbytes -- like PQputline, but buffer need not be null-terminated.
2925 : : * Returns 0 if OK, EOF if not.
2926 : : */
2927 : : int
9372 scrappy@hub.org 2928 : 200028 : PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
2929 : : {
7603 tgl@sss.pgh.pa.us 2930 [ + - ]: 200028 : if (PQputCopyData(conn, buffer, nbytes) > 0)
2931 : 200028 : return 0;
2932 : : else
9355 bruce@momjian.us 2933 :UBC 0 : return EOF;
2934 : : }
2935 : :
2936 : : /*
2937 : : * PQendcopy
2938 : : * After completing the data transfer portion of a copy in/out,
2939 : : * the application must call this routine to finish the command protocol.
2940 : : *
2941 : : * This is deprecated; it's cleaner to use PQgetResult to get the transfer
2942 : : * status.
2943 : : *
2944 : : * RETURNS:
2945 : : * 0 on success
2946 : : * 1 on failure
2947 : : */
2948 : : int
9475 bruce@momjian.us 2949 :CBC 166 : PQendcopy(PGconn *conn)
2950 : : {
2951 [ - + ]: 166 : if (!conn)
9475 bruce@momjian.us 2952 :UBC 0 : return 0;
2953 : :
1137 heikki.linnakangas@i 2954 :CBC 166 : return pqEndcopy3(conn);
2955 : : }
2956 : :
2957 : :
2958 : : /* ----------------
2959 : : * PQfn - Send a function call to the POSTGRES backend.
2960 : : *
2961 : : * conn : backend connection
2962 : : * fnid : OID of function to be called
2963 : : * result_buf : pointer to result buffer
2964 : : * result_len : actual length of result is returned here
2965 : : * result_is_int : If the result is an integer, this must be 1,
2966 : : * otherwise this should be 0
2967 : : * args : pointer to an array of function arguments
2968 : : * (each has length, if integer, and value/pointer)
2969 : : * nargs : # of arguments in args array.
2970 : : *
2971 : : * RETURNS
2972 : : * PGresult with status = PGRES_COMMAND_OK if successful.
2973 : : * *result_len is > 0 if there is a return value, 0 if not.
2974 : : * PGresult with status = PGRES_FATAL_ERROR if backend returns an error.
2975 : : * NULL on communications failure. conn->errorMessage will be set.
2976 : : * ----------------
2977 : : */
2978 : :
2979 : : PGresult *
9715 bruce@momjian.us 2980 : 1063 : PQfn(PGconn *conn,
2981 : : int fnid,
2982 : : int *result_buf,
2983 : : int *result_len,
2984 : : int result_is_int,
2985 : : const PQArgBlock *args,
2986 : : int nargs)
2987 : : {
3325 tgl@sss.pgh.pa.us 2988 : 1063 : *result_len = 0;
2989 : :
9716 bruce@momjian.us 2990 [ - + ]: 1063 : if (!conn)
9716 bruce@momjian.us 2991 :UBC 0 : return NULL;
2992 : :
2993 : : /*
2994 : : * Since this is the beginning of a query cycle, reset the error state.
2995 : : * However, in pipeline mode with something already queued, the error
2996 : : * buffer belongs to that command and we shouldn't clear it.
2997 : : */
776 tgl@sss.pgh.pa.us 2998 [ + - ]:CBC 1063 : if (conn->cmd_queue_head == NULL)
2999 : 1063 : pqClearConnErrorState(conn);
3000 : :
1126 alvherre@alvh.no-ip. 3001 [ - + ]: 1063 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
3002 : : {
516 peter@eisentraut.org 3003 :UBC 0 : libpq_append_conn_error(conn, "%s not allowed in pipeline mode", "PQfn");
1126 alvherre@alvh.no-ip. 3004 : 0 : return NULL;
3005 : : }
3006 : :
3651 bruce@momjian.us 3007 [ + - + - ]:CBC 1063 : if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
724 tgl@sss.pgh.pa.us 3008 [ + - - + ]: 1063 : pgHavePendingResult(conn))
3009 : : {
516 peter@eisentraut.org 3010 :UBC 0 : libpq_append_conn_error(conn, "connection in wrong state");
9475 bruce@momjian.us 3011 : 0 : return NULL;
3012 : : }
3013 : :
1137 heikki.linnakangas@i 3014 :CBC 1063 : return pqFunctionCall3(conn, fnid,
3015 : : result_buf, result_len,
3016 : : result_is_int,
3017 : : args, nargs);
3018 : : }
3019 : :
3020 : : /* ====== Pipeline mode support ======== */
3021 : :
3022 : : /*
3023 : : * PQenterPipelineMode
3024 : : * Put an idle connection in pipeline mode.
3025 : : *
3026 : : * Returns 1 on success. On failure, errorMessage is set and 0 is returned.
3027 : : *
3028 : : * Commands submitted after this can be pipelined on the connection;
3029 : : * there's no requirement to wait for one to finish before the next is
3030 : : * dispatched.
3031 : : *
3032 : : * Queuing of a new query or syncing during COPY is not allowed.
3033 : : *
3034 : : * A set of commands is terminated by a PQpipelineSync. Multiple sync
3035 : : * points can be established while in pipeline mode. Pipeline mode can
3036 : : * be exited by calling PQexitPipelineMode() once all results are processed.
3037 : : *
3038 : : * This doesn't actually send anything on the wire, it just puts libpq
3039 : : * into a state where it can pipeline work.
3040 : : */
3041 : : int
1126 alvherre@alvh.no-ip. 3042 : 63 : PQenterPipelineMode(PGconn *conn)
3043 : : {
3044 [ - + ]: 63 : if (!conn)
1126 alvherre@alvh.no-ip. 3045 :UBC 0 : return 0;
3046 : :
3047 : : /* succeed with no action if already in pipeline mode */
1126 alvherre@alvh.no-ip. 3048 [ + + ]:CBC 63 : if (conn->pipelineStatus != PQ_PIPELINE_OFF)
3049 : 1 : return 1;
3050 : :
3051 [ - + ]: 62 : if (conn->asyncStatus != PGASYNC_IDLE)
3052 : : {
516 peter@eisentraut.org 3053 :UBC 0 : libpq_append_conn_error(conn, "cannot enter pipeline mode, connection not idle");
1126 alvherre@alvh.no-ip. 3054 : 0 : return 0;
3055 : : }
3056 : :
1126 alvherre@alvh.no-ip. 3057 :CBC 62 : conn->pipelineStatus = PQ_PIPELINE_ON;
3058 : :
3059 : 62 : return 1;
3060 : : }
3061 : :
3062 : : /*
3063 : : * PQexitPipelineMode
3064 : : * End pipeline mode and return to normal command mode.
3065 : : *
3066 : : * Returns 1 in success (pipeline mode successfully ended, or not in pipeline
3067 : : * mode).
3068 : : *
3069 : : * Returns 0 if in pipeline mode and cannot be ended yet. Error message will
3070 : : * be set.
3071 : : */
3072 : : int
3073 : 61 : PQexitPipelineMode(PGconn *conn)
3074 : : {
3075 [ - + ]: 61 : if (!conn)
1126 alvherre@alvh.no-ip. 3076 :UBC 0 : return 0;
3077 : :
649 alvherre@alvh.no-ip. 3078 [ + + ]:CBC 61 : if (conn->pipelineStatus == PQ_PIPELINE_OFF &&
3079 [ - + ]: 1 : (conn->asyncStatus == PGASYNC_IDLE ||
649 alvherre@alvh.no-ip. 3080 [ # # ]:UBC 0 : conn->asyncStatus == PGASYNC_PIPELINE_IDLE) &&
649 alvherre@alvh.no-ip. 3081 [ + - ]:CBC 1 : conn->cmd_queue_head == NULL)
1126 3082 : 1 : return 1;
3083 : :
3084 [ - + + - : 60 : switch (conn->asyncStatus)
- ]
3085 : : {
1126 alvherre@alvh.no-ip. 3086 :UBC 0 : case PGASYNC_READY:
3087 : : case PGASYNC_READY_MORE:
3088 : : /* there are some uncollected results */
516 peter@eisentraut.org 3089 : 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode with uncollected results");
1126 alvherre@alvh.no-ip. 3090 : 0 : return 0;
3091 : :
1126 alvherre@alvh.no-ip. 3092 :CBC 5 : case PGASYNC_BUSY:
516 peter@eisentraut.org 3093 : 5 : libpq_append_conn_error(conn, "cannot exit pipeline mode while busy");
1126 alvherre@alvh.no-ip. 3094 : 5 : return 0;
3095 : :
649 3096 : 55 : case PGASYNC_IDLE:
3097 : : case PGASYNC_PIPELINE_IDLE:
3098 : : /* OK */
1126 3099 : 55 : break;
3100 : :
649 alvherre@alvh.no-ip. 3101 :UBC 0 : case PGASYNC_COPY_IN:
3102 : : case PGASYNC_COPY_OUT:
3103 : : case PGASYNC_COPY_BOTH:
516 peter@eisentraut.org 3104 : 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode while in COPY");
3105 : : }
3106 : :
3107 : : /* still work to process */
1126 alvherre@alvh.no-ip. 3108 [ - + ]:CBC 55 : if (conn->cmd_queue_head != NULL)
3109 : : {
516 peter@eisentraut.org 3110 :UBC 0 : libpq_append_conn_error(conn, "cannot exit pipeline mode with uncollected results");
1126 alvherre@alvh.no-ip. 3111 : 0 : return 0;
3112 : : }
3113 : :
1126 alvherre@alvh.no-ip. 3114 :CBC 55 : conn->pipelineStatus = PQ_PIPELINE_OFF;
3115 : 55 : conn->asyncStatus = PGASYNC_IDLE;
3116 : :
3117 : : /* Flush any pending data in out buffer */
3118 [ - + ]: 55 : if (pqFlush(conn) < 0)
1126 alvherre@alvh.no-ip. 3119 :UBC 0 : return 0; /* error message is setup already */
1126 alvherre@alvh.no-ip. 3120 :CBC 55 : return 1;
3121 : : }
3122 : :
3123 : : /*
3124 : : * pqCommandQueueAdvance
3125 : : * Remove one query from the command queue, if appropriate.
3126 : : *
3127 : : * If we have received all results corresponding to the head element
3128 : : * in the command queue, remove it.
3129 : : *
3130 : : * In simple query protocol we must not advance the command queue until the
3131 : : * ReadyForQuery message has been received. This is because in simple mode a
3132 : : * command can have multiple queries, and we must process result for all of
3133 : : * them before moving on to the next command.
3134 : : *
3135 : : * Another consideration is synchronization during error processing in
3136 : : * extended query protocol: we refuse to advance the queue past a SYNC queue
3137 : : * element, unless the result we've received is also a SYNC. In particular
3138 : : * this protects us from advancing when an error is received at an
3139 : : * inappropriate moment.
3140 : : */
3141 : : void
131 3142 : 585182 : pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery, bool gotSync)
3143 : : {
3144 : : PGcmdQueueEntry *prevquery;
3145 : :
1126 3146 [ + + ]: 585182 : if (conn->cmd_queue_head == NULL)
3147 : 20126 : return;
3148 : :
3149 : : /*
3150 : : * If processing a query of simple query protocol, we only advance the
3151 : : * queue when we receive the ReadyForQuery message for it.
3152 : : */
131 3153 [ + + + + ]: 565056 : if (conn->cmd_queue_head->queryclass == PGQUERY_SIMPLE && !isReadyForQuery)
3154 : 286072 : return;
3155 : :
3156 : : /*
3157 : : * If we're waiting for a SYNC, don't advance the queue until we get one.
3158 : : */
3159 [ + + - + ]: 278984 : if (conn->cmd_queue_head->queryclass == PGQUERY_SYNC && !gotSync)
131 alvherre@alvh.no-ip. 3160 :UBC 0 : return;
3161 : :
3162 : : /* delink element from queue */
1126 alvherre@alvh.no-ip. 3163 :CBC 278984 : prevquery = conn->cmd_queue_head;
3164 : 278984 : conn->cmd_queue_head = conn->cmd_queue_head->next;
3165 : :
3166 : : /* If the queue is now empty, reset the tail too */
649 3167 [ + + ]: 278984 : if (conn->cmd_queue_head == NULL)
3168 : 277134 : conn->cmd_queue_tail = NULL;
3169 : :
3170 : : /* and make the queue element recyclable */
1126 3171 : 278984 : prevquery->next = NULL;
3172 : 278984 : pqRecycleCmdQueueEntry(conn, prevquery);
3173 : : }
3174 : :
3175 : : /*
3176 : : * pqPipelineProcessQueue: subroutine for PQgetResult
3177 : : * In pipeline mode, start processing the results of the next query in the queue.
3178 : : */
3179 : : static void
3180 : 1915 : pqPipelineProcessQueue(PGconn *conn)
3181 : : {
3182 [ - + + - ]: 1915 : switch (conn->asyncStatus)
3183 : : {
1126 alvherre@alvh.no-ip. 3184 :UBC 0 : case PGASYNC_COPY_IN:
3185 : : case PGASYNC_COPY_OUT:
3186 : : case PGASYNC_COPY_BOTH:
3187 : : case PGASYNC_READY:
3188 : : case PGASYNC_READY_MORE:
3189 : : case PGASYNC_BUSY:
3190 : : /* client still has to process current query or results */
3191 : 0 : return;
3192 : :
1126 alvherre@alvh.no-ip. 3193 :CBC 1 : case PGASYNC_IDLE:
3194 : :
3195 : : /*
3196 : : * If we're in IDLE mode and there's some command in the queue,
3197 : : * get us into PIPELINE_IDLE mode and process normally. Otherwise
3198 : : * there's nothing for us to do.
3199 : : */
649 3200 [ + - ]: 1 : if (conn->cmd_queue_head != NULL)
3201 : : {
3202 : 1 : conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
3203 : 1 : break;
3204 : : }
649 alvherre@alvh.no-ip. 3205 :UBC 0 : return;
3206 : :
649 alvherre@alvh.no-ip. 3207 :CBC 1914 : case PGASYNC_PIPELINE_IDLE:
3208 [ - + ]: 1914 : Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
3209 : : /* next query please */
1126 3210 : 1914 : break;
3211 : : }
3212 : :
3213 : : /*
3214 : : * Reset partial-result mode. (Client has to set it up for each query, if
3215 : : * desired.)
3216 : : */
8 tgl@sss.pgh.pa.us 3217 :GNC 1915 : conn->partialResMode = false;
548 alvherre@alvh.no-ip. 3218 :CBC 1915 : conn->singleRowMode = false;
8 tgl@sss.pgh.pa.us 3219 :GNC 1915 : conn->maxChunkSize = 0;
3220 : :
3221 : : /*
3222 : : * If there are no further commands to process in the queue, get us in
3223 : : * "real idle" mode now.
3224 : : */
649 alvherre@alvh.no-ip. 3225 [ + + ]:CBC 1915 : if (conn->cmd_queue_head == NULL)
3226 : : {
3227 : 64 : conn->asyncStatus = PGASYNC_IDLE;
1126 3228 : 64 : return;
3229 : : }
3230 : :
3231 : : /*
3232 : : * Reset the error state. This and the next couple of steps correspond to
3233 : : * what PQsendQueryStart didn't do for this query.
3234 : : */
776 tgl@sss.pgh.pa.us 3235 : 1851 : pqClearConnErrorState(conn);
3236 : :
3237 : : /* Initialize async result-accumulation state */
1126 alvherre@alvh.no-ip. 3238 : 1851 : pqClearAsyncResult(conn);
3239 : :
3240 [ + + ]: 1851 : if (conn->pipelineStatus == PQ_PIPELINE_ABORTED &&
3241 [ + + ]: 189 : conn->cmd_queue_head->queryclass != PGQUERY_SYNC)
3242 : : {
3243 : : /*
3244 : : * In an aborted pipeline we don't get anything from the server for
3245 : : * each result; we're just discarding commands from the queue until we
3246 : : * get to the next sync from the server.
3247 : : *
3248 : : * The PGRES_PIPELINE_ABORTED results tell the client that its queries
3249 : : * got aborted.
3250 : : */
3251 : 184 : conn->result = PQmakeEmptyPGresult(conn, PGRES_PIPELINE_ABORTED);
3252 [ - + ]: 184 : if (!conn->result)
3253 : : {
516 peter@eisentraut.org 3254 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
1126 alvherre@alvh.no-ip. 3255 : 0 : pqSaveErrorResult(conn);
3256 : 0 : return;
3257 : : }
1126 alvherre@alvh.no-ip. 3258 :CBC 184 : conn->asyncStatus = PGASYNC_READY;
3259 : : }
3260 : : else
3261 : : {
3262 : : /* allow parsing to continue */
3263 : 1667 : conn->asyncStatus = PGASYNC_BUSY;
3264 : : }
3265 : : }
3266 : :
3267 : : /*
3268 : : * PQpipelineSync
3269 : : * Send a Sync message as part of a pipeline, and flush to server
3270 : : */
3271 : : int
89 michael@paquier.xyz 3272 : 61 : PQpipelineSync(PGconn *conn)
3273 : : {
89 michael@paquier.xyz 3274 :GNC 61 : return pqPipelineSyncInternal(conn, true);
3275 : : }
3276 : :
3277 : : /*
3278 : : * PQsendPipelineSync
3279 : : * Send a Sync message as part of a pipeline, without flushing to server
3280 : : */
3281 : : int
3282 : 5 : PQsendPipelineSync(PGconn *conn)
3283 : : {
3284 : 5 : return pqPipelineSyncInternal(conn, false);
3285 : : }
3286 : :
3287 : : /*
3288 : : * Workhorse function for PQpipelineSync and PQsendPipelineSync.
3289 : : *
3290 : : * immediate_flush controls if the flush happens immediately after sending the
3291 : : * Sync message or not.
3292 : : */
3293 : : static int
3294 : 66 : pqPipelineSyncInternal(PGconn *conn, bool immediate_flush)
3295 : : {
3296 : : PGcmdQueueEntry *entry;
3297 : :
1126 alvherre@alvh.no-ip. 3298 [ - + ]:CBC 66 : if (!conn)
1126 alvherre@alvh.no-ip. 3299 :UBC 0 : return 0;
3300 : :
1126 alvherre@alvh.no-ip. 3301 [ - + ]:CBC 66 : if (conn->pipelineStatus == PQ_PIPELINE_OFF)
3302 : : {
516 peter@eisentraut.org 3303 :UBC 0 : libpq_append_conn_error(conn, "cannot send pipeline when not in pipeline mode");
1126 alvherre@alvh.no-ip. 3304 : 0 : return 0;
3305 : : }
3306 : :
1126 alvherre@alvh.no-ip. 3307 [ - + - ]:CBC 66 : switch (conn->asyncStatus)
3308 : : {
1126 alvherre@alvh.no-ip. 3309 :UBC 0 : case PGASYNC_COPY_IN:
3310 : : case PGASYNC_COPY_OUT:
3311 : : case PGASYNC_COPY_BOTH:
3312 : : /* should be unreachable */
3313 : 0 : appendPQExpBufferStr(&conn->errorMessage,
3314 : : "internal error: cannot send pipeline while in COPY\n");
3315 : 0 : return 0;
1126 alvherre@alvh.no-ip. 3316 :CBC 66 : case PGASYNC_READY:
3317 : : case PGASYNC_READY_MORE:
3318 : : case PGASYNC_BUSY:
3319 : : case PGASYNC_IDLE:
3320 : : case PGASYNC_PIPELINE_IDLE:
3321 : : /* OK to send sync */
3322 : 66 : break;
3323 : : }
3324 : :
3325 : 66 : entry = pqAllocCmdQueueEntry(conn);
3326 [ - + ]: 66 : if (entry == NULL)
1126 alvherre@alvh.no-ip. 3327 :UBC 0 : return 0; /* error msg already set */
3328 : :
1126 alvherre@alvh.no-ip. 3329 :CBC 66 : entry->queryclass = PGQUERY_SYNC;
3330 : 66 : entry->query = NULL;
3331 : :
3332 : : /* construct the Sync message */
236 nathan@postgresql.or 3333 [ + - - + ]:GNC 132 : if (pqPutMsgStart(PqMsg_Sync, conn) < 0 ||
1126 alvherre@alvh.no-ip. 3334 :CBC 66 : pqPutMsgEnd(conn) < 0)
1126 alvherre@alvh.no-ip. 3335 :UBC 0 : goto sendFailed;
3336 : :
3337 : : /*
3338 : : * Give the data a push. In nonblock mode, don't complain if we're unable
3339 : : * to send it all; PQgetResult() will do any additional flushing needed.
3340 : : * If immediate_flush is disabled, the data is pushed if we are past the
3341 : : * size threshold.
3342 : : */
89 michael@paquier.xyz 3343 [ + + ]:GNC 66 : if (immediate_flush)
3344 : : {
3345 [ - + ]: 61 : if (pqFlush(conn) < 0)
89 michael@paquier.xyz 3346 :UNC 0 : goto sendFailed;
3347 : : }
3348 : : else
3349 : : {
89 michael@paquier.xyz 3350 [ - + ]:GNC 5 : if (pqPipelineFlush(conn) < 0)
89 michael@paquier.xyz 3351 :UNC 0 : goto sendFailed;
3352 : : }
3353 : :
3354 : : /* OK, it's launched! */
1010 alvherre@alvh.no-ip. 3355 :CBC 66 : pqAppendCmdQueueEntry(conn, entry);
3356 : :
1126 3357 : 66 : return 1;
3358 : :
1126 alvherre@alvh.no-ip. 3359 :UBC 0 : sendFailed:
3360 : 0 : pqRecycleCmdQueueEntry(conn, entry);
3361 : : /* error message should be set up already */
3362 : 0 : return 0;
3363 : : }
3364 : :
3365 : : /*
3366 : : * PQsendFlushRequest
3367 : : * Send request for server to flush its buffer. Useful in pipeline
3368 : : * mode when a sync point is not desired.
3369 : : */
3370 : : int
1020 alvherre@alvh.no-ip. 3371 :CBC 8 : PQsendFlushRequest(PGconn *conn)
3372 : : {
3373 [ - + ]: 8 : if (!conn)
1020 alvherre@alvh.no-ip. 3374 :UBC 0 : return 0;
3375 : :
3376 : : /* Don't try to send if we know there's no live connection. */
1020 alvherre@alvh.no-ip. 3377 [ - + ]:CBC 8 : if (conn->status != CONNECTION_OK)
3378 : : {
516 peter@eisentraut.org 3379 :UBC 0 : libpq_append_conn_error(conn, "no connection to the server");
1020 alvherre@alvh.no-ip. 3380 : 0 : return 0;
3381 : : }
3382 : :
3383 : : /* Can't send while already busy, either, unless enqueuing for later */
1020 alvherre@alvh.no-ip. 3384 [ + - ]:CBC 8 : if (conn->asyncStatus != PGASYNC_IDLE &&
3385 [ - + ]: 8 : conn->pipelineStatus == PQ_PIPELINE_OFF)
3386 : : {
516 peter@eisentraut.org 3387 :UBC 0 : libpq_append_conn_error(conn, "another command is already in progress");
1010 alvherre@alvh.no-ip. 3388 : 0 : return 0;
3389 : : }
3390 : :
236 nathan@postgresql.or 3391 [ + - - + ]:GNC 16 : if (pqPutMsgStart(PqMsg_Flush, conn) < 0 ||
1020 alvherre@alvh.no-ip. 3392 :CBC 8 : pqPutMsgEnd(conn) < 0)
3393 : : {
1020 alvherre@alvh.no-ip. 3394 :UBC 0 : return 0;
3395 : : }
3396 : :
3397 : : /*
3398 : : * Give the data a push (in pipeline mode, only if we're past the size
3399 : : * threshold). In nonblock mode, don't complain if we're unable to send
3400 : : * it all; PQgetResult() will do any additional flushing needed.
3401 : : */
158 alvherre@alvh.no-ip. 3402 [ - + ]:CBC 8 : if (pqPipelineFlush(conn) < 0)
158 alvherre@alvh.no-ip. 3403 :UBC 0 : return 0;
3404 : :
1020 alvherre@alvh.no-ip. 3405 :CBC 8 : return 1;
3406 : : }
3407 : :
3408 : : /* ====== accessor funcs for PGresult ======== */
3409 : :
3410 : : ExecStatusType
8921 bruce@momjian.us 3411 : 1086989 : PQresultStatus(const PGresult *res)
3412 : : {
9716 3413 [ + + ]: 1086989 : if (!res)
7603 tgl@sss.pgh.pa.us 3414 : 1 : return PGRES_FATAL_ERROR;
9716 bruce@momjian.us 3415 : 1086988 : return res->resultStatus;
3416 : : }
3417 : :
3418 : : char *
9198 tgl@sss.pgh.pa.us 3419 : 22 : PQresStatus(ExecStatusType status)
3420 : : {
1126 alvherre@alvh.no-ip. 3421 [ - + ]: 22 : if ((unsigned int) status >= lengthof(pgresStatus))
8309 peter_e@gmx.net 3422 :UBC 0 : return libpq_gettext("invalid ExecStatusType code");
9198 tgl@sss.pgh.pa.us 3423 :CBC 22 : return pgresStatus[status];
3424 : : }
3425 : :
3426 : : char *
8921 bruce@momjian.us 3427 : 96854 : PQresultErrorMessage(const PGresult *res)
3428 : : {
9327 tgl@sss.pgh.pa.us 3429 [ + - + + ]: 96854 : if (!res || !res->errMsg)
3430 : 1 : return "";
3431 : 96853 : return res->errMsg;
3432 : : }
3433 : :
3434 : : char *
2933 3435 : 3 : PQresultVerboseErrorMessage(const PGresult *res,
3436 : : PGVerbosity verbosity,
3437 : : PGContextVisibility show_context)
3438 : : {
3439 : : PQExpBufferData workBuf;
3440 : :
3441 : : /*
3442 : : * Because the caller is expected to free the result string, we must
3443 : : * strdup any constant result. We use plain strdup and document that
3444 : : * callers should expect NULL if out-of-memory.
3445 : : */
3446 [ + - ]: 3 : if (!res ||
3447 [ - + ]: 3 : (res->resultStatus != PGRES_FATAL_ERROR &&
2933 tgl@sss.pgh.pa.us 3448 [ # # ]:UBC 0 : res->resultStatus != PGRES_NONFATAL_ERROR))
3449 : 0 : return strdup(libpq_gettext("PGresult is not an error result\n"));
3450 : :
2933 tgl@sss.pgh.pa.us 3451 :CBC 3 : initPQExpBuffer(&workBuf);
3452 : :
3453 : 3 : pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
3454 : :
3455 : : /* If insufficient memory to format the message, fail cleanly */
3456 [ - + ]: 3 : if (PQExpBufferDataBroken(workBuf))
3457 : : {
2933 tgl@sss.pgh.pa.us 3458 :UBC 0 : termPQExpBuffer(&workBuf);
3459 : 0 : return strdup(libpq_gettext("out of memory\n"));
3460 : : }
3461 : :
2933 tgl@sss.pgh.pa.us 3462 :CBC 3 : return workBuf.data;
3463 : : }
3464 : :
3465 : : char *
7603 3466 : 739449 : PQresultErrorField(const PGresult *res, int fieldcode)
3467 : : {
3468 : : PGMessageField *pfield;
3469 : :
3470 [ + + ]: 739449 : if (!res)
3471 : 2 : return NULL;
3472 [ + + ]: 5578524 : for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
3473 : : {
3474 [ + + ]: 5152696 : if (pfield->code == fieldcode)
3475 : 313619 : return pfield->contents;
3476 : : }
3477 : 425828 : return NULL;
3478 : : }
3479 : :
3480 : : int
8921 bruce@momjian.us 3481 : 248607 : PQntuples(const PGresult *res)
3482 : : {
9716 3483 [ - + ]: 248607 : if (!res)
9475 bruce@momjian.us 3484 :UBC 0 : return 0;
9716 bruce@momjian.us 3485 :CBC 248607 : return res->ntups;
3486 : : }
3487 : :
3488 : : int
8921 3489 : 160089 : PQnfields(const PGresult *res)
3490 : : {
9716 3491 [ - + ]: 160089 : if (!res)
9475 bruce@momjian.us 3492 :UBC 0 : return 0;
9716 bruce@momjian.us 3493 :CBC 160089 : return res->numAttributes;
3494 : : }
3495 : :
3496 : : int
8921 3497 : 431 : PQbinaryTuples(const PGresult *res)
3498 : : {
9355 3499 [ - + ]: 431 : if (!res)
9355 bruce@momjian.us 3500 :UBC 0 : return 0;
9355 bruce@momjian.us 3501 :CBC 431 : return res->binary;
3502 : : }
3503 : :
3504 : : /*
3505 : : * Helper routines to range-check field numbers and tuple numbers.
3506 : : * Return true if OK, false if not
3507 : : */
3508 : :
3509 : : static int
8309 peter_e@gmx.net 3510 : 254038 : check_field_number(const PGresult *res, int field_num)
3511 : : {
9716 bruce@momjian.us 3512 [ - + ]: 254038 : if (!res)
2433 peter_e@gmx.net 3513 :UBC 0 : return false; /* no way to display error message... */
9380 bruce@momjian.us 3514 [ + - - + ]:CBC 254038 : if (field_num < 0 || field_num >= res->numAttributes)
3515 : : {
7601 tgl@sss.pgh.pa.us 3516 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3517 : : "column number %d is out of range 0..%d",
3518 : 0 : field_num, res->numAttributes - 1);
2433 peter_e@gmx.net 3519 : 0 : return false;
3520 : : }
2433 peter_e@gmx.net 3521 :CBC 254038 : return true;
3522 : : }
3523 : :
3524 : : static int
8309 3525 : 17913129 : check_tuple_field_number(const PGresult *res,
3526 : : int tup_num, int field_num)
3527 : : {
9380 bruce@momjian.us 3528 [ - + ]: 17913129 : if (!res)
2433 peter_e@gmx.net 3529 :UBC 0 : return false; /* no way to display error message... */
9380 bruce@momjian.us 3530 [ + - - + ]:CBC 17913129 : if (tup_num < 0 || tup_num >= res->ntups)
3531 : : {
7601 tgl@sss.pgh.pa.us 3532 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3533 : : "row number %d is out of range 0..%d",
3534 : 0 : tup_num, res->ntups - 1);
2433 peter_e@gmx.net 3535 : 0 : return false;
3536 : : }
9475 bruce@momjian.us 3537 [ + - - + ]:CBC 17913129 : if (field_num < 0 || field_num >= res->numAttributes)
3538 : : {
7601 tgl@sss.pgh.pa.us 3539 :UBC 0 : pqInternalNotice(&res->noticeHooks,
3540 : : "column number %d is out of range 0..%d",
3541 : 0 : field_num, res->numAttributes - 1);
2433 peter_e@gmx.net 3542 : 0 : return false;
3543 : : }
2433 peter_e@gmx.net 3544 :CBC 17913129 : return true;
3545 : : }
3546 : :
3547 : : static int
6449 tgl@sss.pgh.pa.us 3548 :UBC 0 : check_param_number(const PGresult *res, int param_num)
3549 : : {
3550 [ # # ]: 0 : if (!res)
2433 peter_e@gmx.net 3551 : 0 : return false; /* no way to display error message... */
6449 tgl@sss.pgh.pa.us 3552 [ # # # # ]: 0 : if (param_num < 0 || param_num >= res->numParameters)
3553 : : {
3554 : 0 : pqInternalNotice(&res->noticeHooks,
3555 : : "parameter number %d is out of range 0..%d",
3556 : 0 : param_num, res->numParameters - 1);
2433 peter_e@gmx.net 3557 : 0 : return false;
3558 : : }
3559 : :
3560 : 0 : return true;
3561 : : }
3562 : :
3563 : : /*
3564 : : * returns NULL if the field_num is invalid
3565 : : */
3566 : : char *
8921 bruce@momjian.us 3567 :CBC 124693 : PQfname(const PGresult *res, int field_num)
3568 : : {
8309 peter_e@gmx.net 3569 [ - + ]: 124693 : if (!check_field_number(res, field_num))
9380 bruce@momjian.us 3570 :UBC 0 : return NULL;
9716 bruce@momjian.us 3571 [ + - ]:CBC 124693 : if (res->attDescs)
3572 : 124693 : return res->attDescs[field_num].name;
3573 : : else
9716 bruce@momjian.us 3574 :UBC 0 : return NULL;
3575 : : }
3576 : :
3577 : : /*
3578 : : * PQfnumber: find column number given column name
3579 : : *
3580 : : * The column name is parsed as if it were in a SQL statement, including
3581 : : * case-folding and double-quote processing. But note a possible gotcha:
3582 : : * downcasing in the frontend might follow different locale rules than
3583 : : * downcasing in the backend...
3584 : : *
3585 : : * Returns -1 if no match. In the present backend it is also possible
3586 : : * to have multiple matches, in which case the first one is found.
3587 : : */
3588 : : int
8921 bruce@momjian.us 3589 :CBC 144240 : PQfnumber(const PGresult *res, const char *field_name)
3590 : : {
3591 : : char *field_case;
3592 : : bool in_quotes;
3697 sfrost@snowman.net 3593 : 144240 : bool all_lower = true;
3594 : : const char *iptr;
3595 : : char *optr;
3596 : : int i;
3597 : :
9716 bruce@momjian.us 3598 [ - + ]: 144240 : if (!res)
9716 bruce@momjian.us 3599 :UBC 0 : return -1;
3600 : :
3601 : : /*
3602 : : * Note: it is correct to reject a zero-length input string; the proper
3603 : : * input to match a zero-length field name would be "".
3604 : : */
9716 bruce@momjian.us 3605 [ + - ]:CBC 144240 : if (field_name == NULL ||
3606 [ + - ]: 144240 : field_name[0] == '\0' ||
3607 [ - + ]: 144240 : res->attDescs == NULL)
9716 bruce@momjian.us 3608 :UBC 0 : return -1;
3609 : :
3610 : : /*
3611 : : * Check if we can avoid the strdup() and related work because the
3612 : : * passed-in string wouldn't be changed before we do the check anyway.
3613 : : */
3697 sfrost@snowman.net 3614 [ + + ]:CBC 1500630 : for (iptr = field_name; *iptr; iptr++)
3615 : : {
3616 : 1356390 : char c = *iptr;
3617 : :
3618 [ + - - + ]: 1356390 : if (c == '"' || c != pg_tolower((unsigned char) c))
3619 : : {
3697 sfrost@snowman.net 3620 :UBC 0 : all_lower = false;
3621 : 0 : break;
3622 : : }
3623 : : }
3624 : :
3697 sfrost@snowman.net 3625 [ + - ]:CBC 144240 : if (all_lower)
3626 [ + - ]: 1075544 : for (i = 0; i < res->numAttributes; i++)
3627 [ + + ]: 1075544 : if (strcmp(field_name, res->attDescs[i].name) == 0)
3628 : 144240 : return i;
3629 : :
3630 : : /* Fall through to the normal check if that didn't work out. */
3631 : :
3632 : : /*
3633 : : * Note: this code will not reject partially quoted strings, eg
3634 : : * foo"BAR"foo will become fooBARfoo when it probably ought to be an error
3635 : : * condition.
3636 : : */
9652 bruce@momjian.us 3637 :LBC (1242) : field_case = strdup(field_name);
7498 tgl@sss.pgh.pa.us 3638 [ # # ]: (1242) : if (field_case == NULL)
7498 tgl@sss.pgh.pa.us 3639 :UBC 0 : return -1; /* grotty */
3640 : :
7498 tgl@sss.pgh.pa.us 3641 :LBC (1242) : in_quotes = false;
3642 : (1242) : optr = field_case;
3643 [ # # ]: (14904) : for (iptr = field_case; *iptr; iptr++)
3644 : : {
7168 bruce@momjian.us 3645 : (13662) : char c = *iptr;
3646 : :
7498 tgl@sss.pgh.pa.us 3647 [ # # ]: (13662) : if (in_quotes)
3648 : : {
7498 tgl@sss.pgh.pa.us 3649 [ # # ]:UBC 0 : if (c == '"')
3650 : : {
3651 [ # # ]: 0 : if (iptr[1] == '"')
3652 : : {
3653 : : /* doubled quotes become a single quote */
3654 : 0 : *optr++ = '"';
3655 : 0 : iptr++;
3656 : : }
3657 : : else
3658 : 0 : in_quotes = false;
3659 : : }
3660 : : else
3661 : 0 : *optr++ = c;
3662 : : }
7498 tgl@sss.pgh.pa.us 3663 [ # # ]:LBC (13662) : else if (c == '"')
7498 tgl@sss.pgh.pa.us 3664 :UBC 0 : in_quotes = true;
3665 : : else
3666 : : {
7282 tgl@sss.pgh.pa.us 3667 :LBC (13662) : c = pg_tolower((unsigned char) c);
7498 3668 : (13662) : *optr++ = c;
3669 : : }
3670 : : }
3671 : (1242) : *optr = '\0';
3672 : :
9716 bruce@momjian.us 3673 [ # # ]: (11178) : for (i = 0; i < res->numAttributes; i++)
3674 : : {
9202 3675 [ # # ]: (9936) : if (strcmp(field_case, res->attDescs[i].name) == 0)
3676 : : {
9652 bruce@momjian.us 3677 :UBC 0 : free(field_case);
9716 3678 : 0 : return i;
3679 : : }
3680 : : }
9652 bruce@momjian.us 3681 :LBC (1242) : free(field_case);
9716 3682 : (1242) : return -1;
3683 : : }
3684 : :
3685 : : Oid
7603 tgl@sss.pgh.pa.us 3686 :UBC 0 : PQftable(const PGresult *res, int field_num)
3687 : : {
3688 [ # # ]: 0 : if (!check_field_number(res, field_num))
3689 : 0 : return InvalidOid;
3690 [ # # ]: 0 : if (res->attDescs)
3691 : 0 : return res->attDescs[field_num].tableid;
3692 : : else
3693 : 0 : return InvalidOid;
3694 : : }
3695 : :
3696 : : int
3697 : 0 : PQftablecol(const PGresult *res, int field_num)
3698 : : {
3699 [ # # ]: 0 : if (!check_field_number(res, field_num))
3700 : 0 : return 0;
3701 [ # # ]: 0 : if (res->attDescs)
3702 : 0 : return res->attDescs[field_num].columnid;
3703 : : else
3704 : 0 : return 0;
3705 : : }
3706 : :
3707 : : int
7603 tgl@sss.pgh.pa.us 3708 :CBC 4683 : PQfformat(const PGresult *res, int field_num)
3709 : : {
3710 [ - + ]: 4683 : if (!check_field_number(res, field_num))
7603 tgl@sss.pgh.pa.us 3711 :UBC 0 : return 0;
7603 tgl@sss.pgh.pa.us 3712 [ + - ]:CBC 4683 : if (res->attDescs)
3713 : 4683 : return res->attDescs[field_num].format;
3714 : : else
7603 tgl@sss.pgh.pa.us 3715 :UBC 0 : return 0;
3716 : : }
3717 : :
3718 : : Oid
8921 bruce@momjian.us 3719 :CBC 124433 : PQftype(const PGresult *res, int field_num)
3720 : : {
8309 peter_e@gmx.net 3721 [ - + ]: 124433 : if (!check_field_number(res, field_num))
9716 bruce@momjian.us 3722 :UBC 0 : return InvalidOid;
9716 bruce@momjian.us 3723 [ + - ]:CBC 124433 : if (res->attDescs)
9407 3724 : 124433 : return res->attDescs[field_num].typid;
3725 : : else
9716 bruce@momjian.us 3726 :UBC 0 : return InvalidOid;
3727 : : }
3728 : :
3729 : : int
8921 bruce@momjian.us 3730 :CBC 80 : PQfsize(const PGresult *res, int field_num)
3731 : : {
8309 peter_e@gmx.net 3732 [ - + ]: 80 : if (!check_field_number(res, field_num))
9475 bruce@momjian.us 3733 :UBC 0 : return 0;
9716 bruce@momjian.us 3734 [ + - ]:CBC 80 : if (res->attDescs)
9407 3735 : 80 : return res->attDescs[field_num].typlen;
3736 : : else
9716 bruce@momjian.us 3737 :UBC 0 : return 0;
3738 : : }
3739 : :
3740 : : int
8921 bruce@momjian.us 3741 :CBC 149 : PQfmod(const PGresult *res, int field_num)
3742 : : {
8309 peter_e@gmx.net 3743 [ - + ]: 149 : if (!check_field_number(res, field_num))
9475 bruce@momjian.us 3744 :UBC 0 : return 0;
9475 bruce@momjian.us 3745 [ + - ]:CBC 149 : if (res->attDescs)
9407 3746 : 149 : return res->attDescs[field_num].atttypmod;
3747 : : else
9475 bruce@momjian.us 3748 :UBC 0 : return 0;
3749 : : }
3750 : :
3751 : : char *
8833 peter_e@gmx.net 3752 :CBC 284949 : PQcmdStatus(PGresult *res)
3753 : : {
9716 bruce@momjian.us 3754 [ - + ]: 284949 : if (!res)
9716 bruce@momjian.us 3755 :UBC 0 : return NULL;
9716 bruce@momjian.us 3756 :CBC 284949 : return res->cmdStatus;
3757 : : }
3758 : :
3759 : : /*
3760 : : * PQoidStatus -
3761 : : * if the last command was an INSERT, return the oid string
3762 : : * if not, return ""
3763 : : */
3764 : : char *
8921 bruce@momjian.us 3765 :UBC 0 : PQoidStatus(const PGresult *res)
3766 : : {
3767 : : /*
3768 : : * This must be enough to hold the result. Don't laugh, this is better
3769 : : * than what this function used to do.
3770 : : */
3771 : : static char buf[24];
3772 : :
3773 : : size_t len;
3774 : :
3697 sfrost@snowman.net 3775 [ # # # # ]: 0 : if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0)
9372 scrappy@hub.org 3776 : 0 : return "";
3777 : :
8921 bruce@momjian.us 3778 : 0 : len = strspn(res->cmdStatus + 7, "0123456789");
3368 tgl@sss.pgh.pa.us 3779 [ # # ]: 0 : if (len > sizeof(buf) - 1)
3780 : 0 : len = sizeof(buf) - 1;
3781 : 0 : memcpy(buf, res->cmdStatus + 7, len);
8468 3782 : 0 : buf[len] = '\0';
3783 : :
8921 bruce@momjian.us 3784 : 0 : return buf;
3785 : : }
3786 : :
3787 : : /*
3788 : : * PQoidValue -
3789 : : * a perhaps preferable form of the above which just returns
3790 : : * an Oid type
3791 : : */
3792 : : Oid
8921 bruce@momjian.us 3793 :CBC 81443 : PQoidValue(const PGresult *res)
3794 : : {
8768 3795 : 81443 : char *endptr = NULL;
3796 : : unsigned long result;
3797 : :
6756 3798 [ + - ]: 81443 : if (!res ||
3799 [ + + ]: 81443 : strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||
3800 [ + - ]: 15858 : res->cmdStatus[7] < '0' ||
3801 [ - + ]: 15858 : res->cmdStatus[7] > '9')
8768 3802 : 65585 : return InvalidOid;
3803 : :
3804 : 15858 : result = strtoul(res->cmdStatus + 7, &endptr, 10);
3805 : :
6809 3806 [ + - - + : 15858 : if (!endptr || (*endptr != ' ' && *endptr != '\0'))
- - ]
8768 bruce@momjian.us 3807 :UBC 0 : return InvalidOid;
3808 : : else
8768 bruce@momjian.us 3809 :CBC 15858 : return (Oid) result;
3810 : : }
3811 : :
3812 : :
3813 : : /*
3814 : : * PQcmdTuples -
3815 : : * If the last command was INSERT/UPDATE/DELETE/MERGE/MOVE/FETCH/COPY,
3816 : : * return a string containing the number of inserted/affected tuples.
3817 : : * If not, return "".
3818 : : *
3819 : : * XXX: this should probably return an int
3820 : : */
3821 : : char *
8833 peter_e@gmx.net 3822 : 143186 : PQcmdTuples(PGresult *res)
3823 : : {
3824 : : char *p,
3825 : : *c;
3826 : :
9716 bruce@momjian.us 3827 [ + + ]: 143186 : if (!res)
9380 3828 : 220 : return "";
3829 : :
7725 3830 [ + + ]: 142966 : if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
3831 : : {
6617 tgl@sss.pgh.pa.us 3832 : 16669 : p = res->cmdStatus + 7;
3833 : : /* INSERT: skip oid and space */
3834 [ + - + + ]: 33338 : while (*p && *p != ' ')
7725 bruce@momjian.us 3835 : 16669 : p++;
6617 tgl@sss.pgh.pa.us 3836 [ - + ]: 16669 : if (*p == 0)
2489 tgl@sss.pgh.pa.us 3837 :UBC 0 : goto interpret_error; /* no space? */
6617 tgl@sss.pgh.pa.us 3838 :CBC 16669 : p++;
3839 : : }
5171 bruce@momjian.us 3840 [ + + ]: 126297 : else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 ||
3841 [ + + ]: 71417 : strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
7725 3842 [ + + ]: 70044 : strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
6617 tgl@sss.pgh.pa.us 3843 : 58425 : p = res->cmdStatus + 7;
748 alvherre@alvh.no-ip. 3844 [ + + ]: 67872 : else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0 ||
3845 [ + + ]: 67187 : strncmp(res->cmdStatus, "MERGE ", 6) == 0)
6617 tgl@sss.pgh.pa.us 3846 : 1107 : p = res->cmdStatus + 6;
3847 [ + + ]: 66765 : else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
3848 [ + + ]: 66736 : strncmp(res->cmdStatus, "COPY ", 5) == 0)
7725 bruce@momjian.us 3849 : 519 : p = res->cmdStatus + 5;
3850 : : else
3851 : 66246 : return "";
3852 : :
3853 : : /* check that we have an integer (at least one digit, nothing else) */
6617 tgl@sss.pgh.pa.us 3854 [ + + ]: 162326 : for (c = p; *c; c++)
3855 : : {
3856 [ - + ]: 85606 : if (!isdigit((unsigned char) *c))
6617 tgl@sss.pgh.pa.us 3857 :UBC 0 : goto interpret_error;
3858 : : }
6617 tgl@sss.pgh.pa.us 3859 [ - + ]:CBC 76720 : if (c == p)
6617 tgl@sss.pgh.pa.us 3860 :UBC 0 : goto interpret_error;
3861 : :
7725 bruce@momjian.us 3862 :CBC 76720 : return p;
3863 : :
6617 tgl@sss.pgh.pa.us 3864 :UBC 0 : interpret_error:
3865 : 0 : pqInternalNotice(&res->noticeHooks,
3866 : : "could not interpret result from server: %s",
3867 : 0 : res->cmdStatus);
3868 : 0 : return "";
3869 : : }
3870 : :
3871 : : /*
3872 : : * PQgetvalue:
3873 : : * return the value of field 'field_num' of row 'tup_num'
3874 : : */
3875 : : char *
8921 bruce@momjian.us 3876 :CBC 14765091 : PQgetvalue(const PGresult *res, int tup_num, int field_num)
3877 : : {
8309 peter_e@gmx.net 3878 [ - + ]: 14765091 : if (!check_tuple_field_number(res, tup_num, field_num))
9716 bruce@momjian.us 3879 :UBC 0 : return NULL;
9716 bruce@momjian.us 3880 :CBC 14765091 : return res->tuples[tup_num][field_num].value;
3881 : : }
3882 : :
3883 : : /* PQgetlength:
3884 : : * returns the actual length of a field value in bytes.
3885 : : */
3886 : : int
8921 3887 : 18854 : PQgetlength(const PGresult *res, int tup_num, int field_num)
3888 : : {
8309 peter_e@gmx.net 3889 [ - + ]: 18854 : if (!check_tuple_field_number(res, tup_num, field_num))
9475 bruce@momjian.us 3890 :UBC 0 : return 0;
9716 bruce@momjian.us 3891 [ + + ]:CBC 18854 : if (res->tuples[tup_num][field_num].len != NULL_LEN)
3892 : 18683 : return res->tuples[tup_num][field_num].len;
3893 : : else
3894 : 171 : return 0;
3895 : : }
3896 : :
3897 : : /* PQgetisnull:
3898 : : * returns the null status of a field value.
3899 : : */
3900 : : int
8921 3901 : 3129184 : PQgetisnull(const PGresult *res, int tup_num, int field_num)
3902 : : {
8309 peter_e@gmx.net 3903 [ - + ]: 3129184 : if (!check_tuple_field_number(res, tup_num, field_num))
9475 bruce@momjian.us 3904 :UBC 0 : return 1; /* pretend it is null */
9716 bruce@momjian.us 3905 [ + + ]:CBC 3129184 : if (res->tuples[tup_num][field_num].len == NULL_LEN)
3906 : 372712 : return 1;
3907 : : else
3908 : 2756472 : return 0;
3909 : : }
3910 : :
3911 : : /* PQnparams:
3912 : : * returns the number of input parameters of a prepared statement.
3913 : : */
3914 : : int
6449 tgl@sss.pgh.pa.us 3915 :UBC 0 : PQnparams(const PGresult *res)
3916 : : {
3917 [ # # ]: 0 : if (!res)
3918 : 0 : return 0;
3919 : 0 : return res->numParameters;
3920 : : }
3921 : :
3922 : : /* PQparamtype:
3923 : : * returns type Oid of the specified statement parameter.
3924 : : */
3925 : : Oid
3926 : 0 : PQparamtype(const PGresult *res, int param_num)
3927 : : {
3928 [ # # ]: 0 : if (!check_param_number(res, param_num))
3929 : 0 : return InvalidOid;
3930 [ # # ]: 0 : if (res->paramDescs)
3931 : 0 : return res->paramDescs[param_num].typid;
3932 : : else
3933 : 0 : return InvalidOid;
3934 : : }
3935 : :
3936 : :
3937 : : /* PQsetnonblocking:
3938 : : * sets the PGconn's database connection non-blocking if the arg is true
3939 : : * or makes it blocking if the arg is false, this will not protect
3940 : : * you from PQexec(), you'll only be safe when using the non-blocking API.
3941 : : * Needs to be called only on a connected database connection.
3942 : : */
3943 : : int
8853 bruce@momjian.us 3944 :CBC 4 : PQsetnonblocking(PGconn *conn, int arg)
3945 : : {
3946 : : bool barg;
3947 : :
7603 tgl@sss.pgh.pa.us 3948 [ + - - + ]: 4 : if (!conn || conn->status == CONNECTION_BAD)
7603 tgl@sss.pgh.pa.us 3949 :UBC 0 : return -1;
3950 : :
2433 peter_e@gmx.net 3951 :CBC 4 : barg = (arg ? true : false);
3952 : :
3953 : : /* early out if the socket is already in the state requested */
7550 tgl@sss.pgh.pa.us 3954 [ - + ]: 4 : if (barg == conn->nonblocking)
6668 neilc@samurai.com 3955 :UBC 0 : return 0;
3956 : :
3957 : : /*
3958 : : * to guarantee constancy for flushing/query/result-polling behavior we
3959 : : * need to flush the send queue at this point in order to guarantee proper
3960 : : * behavior. this is ok because either they are making a transition _from_
3961 : : * or _to_ blocking mode, either way we can block them.
3962 : : *
3963 : : * Clear error state in case pqFlush adds to it, unless we're actively
3964 : : * pipelining, in which case it seems best not to.
3965 : : */
776 tgl@sss.pgh.pa.us 3966 [ + + ]:CBC 4 : if (conn->cmd_queue_head == NULL)
3967 : 3 : pqClearConnErrorState(conn);
3968 : :
3969 : : /* if we are going from blocking to non-blocking flush here */
8847 bruce@momjian.us 3970 [ - + ]: 4 : if (pqFlush(conn))
6668 neilc@samurai.com 3971 :UBC 0 : return -1;
3972 : :
7550 tgl@sss.pgh.pa.us 3973 :CBC 4 : conn->nonblocking = barg;
3974 : :
6668 neilc@samurai.com 3975 : 4 : return 0;
3976 : : }
3977 : :
3978 : : /*
3979 : : * return the blocking status of the database connection
3980 : : * true == nonblocking, false == blocking
3981 : : */
3982 : : int
8853 bruce@momjian.us 3983 : 2 : PQisnonblocking(const PGconn *conn)
3984 : : {
608 tgl@sss.pgh.pa.us 3985 [ + - - + ]: 2 : if (!conn || conn->status == CONNECTION_BAD)
608 tgl@sss.pgh.pa.us 3986 :UBC 0 : return false;
6668 neilc@samurai.com 3987 :CBC 2 : return pqIsnonblocking(conn);
3988 : : }
3989 : :
3990 : : /* libpq is thread-safe? */
3991 : : int
6536 bruce@momjian.us 3992 :UBC 0 : PQisthreadsafe(void)
3993 : : {
3994 : 0 : return true;
3995 : : }
3996 : :
3997 : :
3998 : : /* try to force data out, really only useful for non-blocking users */
3999 : : int
8853 bruce@momjian.us 4000 :CBC 129807 : PQflush(PGconn *conn)
4001 : : {
608 tgl@sss.pgh.pa.us 4002 [ + - - + ]: 129807 : if (!conn || conn->status == CONNECTION_BAD)
608 tgl@sss.pgh.pa.us 4003 :UBC 0 : return -1;
6925 bruce@momjian.us 4004 :CBC 129807 : return pqFlush(conn);
4005 : : }
4006 : :
4007 : : /*
4008 : : * pqPipelineFlush
4009 : : *
4010 : : * In pipeline mode, data will be flushed only when the out buffer reaches the
4011 : : * threshold value. In non-pipeline mode, it behaves as stock pqFlush.
4012 : : *
4013 : : * Returns 0 on success.
4014 : : */
4015 : : static int
1126 alvherre@alvh.no-ip. 4016 : 10569 : pqPipelineFlush(PGconn *conn)
4017 : : {
4018 [ + + ]: 10569 : if ((conn->pipelineStatus != PQ_PIPELINE_ON) ||
4019 [ - + ]: 1820 : (conn->outCount >= OUTBUFFER_THRESHOLD))
4020 : 8749 : return pqFlush(conn);
4021 : 1820 : return 0;
4022 : : }
4023 : :
4024 : :
4025 : : /*
4026 : : * PQfreemem - safely frees memory allocated
4027 : : *
4028 : : * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
4029 : : * Used for freeing memory from PQescapeBytea()/PQunescapeBytea()
4030 : : */
4031 : : void
7559 bruce@momjian.us 4032 : 2361493 : PQfreemem(void *ptr)
4033 : : {
7647 tgl@sss.pgh.pa.us 4034 : 2361493 : free(ptr);
4035 : 2361493 : }
4036 : :
4037 : : /*
4038 : : * PQfreeNotify - free's the memory associated with a PGnotify
4039 : : *
4040 : : * This function is here only for binary backward compatibility.
4041 : : * New code should use PQfreemem(). A macro will automatically map
4042 : : * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24
4043 : : */
4044 : :
4045 : : #undef PQfreeNotify
4046 : : void PQfreeNotify(PGnotify *notify);
4047 : :
4048 : : void
7691 bruce@momjian.us 4049 :UBC 0 : PQfreeNotify(PGnotify *notify)
4050 : : {
4051 : 0 : PQfreemem(notify);
4052 : 0 : }
4053 : :
4054 : :
4055 : : /*
4056 : : * Escaping arbitrary strings to get valid SQL literal strings.
4057 : : *
4058 : : * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\".
4059 : : *
4060 : : * length is the length of the source string. (Note: if a terminating NUL
4061 : : * is encountered sooner, PQescapeString stops short of "length"; the behavior
4062 : : * is thus rather like strncpy.)
4063 : : *
4064 : : * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
4065 : : * A terminating NUL character is added to the output string, whether the
4066 : : * input is NUL-terminated or not.
4067 : : *
4068 : : * Returns the actual length of the output (not counting the terminating NUL).
4069 : : */
4070 : : static size_t
6538 tgl@sss.pgh.pa.us 4071 :CBC 4936 : PQescapeStringInternal(PGconn *conn,
4072 : : char *to, const char *from, size_t length,
4073 : : int *error,
4074 : : int encoding, bool std_strings)
4075 : : {
7647 4076 : 4936 : const char *source = from;
4077 : 4936 : char *target = to;
7499 4078 : 4936 : size_t remaining = length;
4079 : :
6538 4080 [ - + ]: 4936 : if (error)
6538 tgl@sss.pgh.pa.us 4081 :UBC 0 : *error = 0;
4082 : :
7499 tgl@sss.pgh.pa.us 4083 [ + + + - ]:CBC 71964 : while (remaining > 0 && *source != '\0')
4084 : : {
6402 bruce@momjian.us 4085 : 67028 : char c = *source;
4086 : : int len;
4087 : : int i;
4088 : :
4089 : : /* Fast path for plain ASCII */
6538 tgl@sss.pgh.pa.us 4090 [ + + ]: 67028 : if (!IS_HIGHBIT_SET(c))
4091 : : {
4092 : : /* Apply quoting if needed */
6531 4093 [ + + + + : 66286 : if (SQL_STR_DOUBLE(c, !std_strings))
- + ]
6538 4094 : 11 : *target++ = c;
4095 : : /* Copy the character */
4096 : 66286 : *target++ = c;
4097 : 66286 : source++;
4098 : 66286 : remaining--;
4099 : 66286 : continue;
4100 : : }
4101 : :
4102 : : /* Slow path for possible multibyte characters */
4103 : 742 : len = pg_encoding_mblen(encoding, source);
4104 : :
4105 : : /* Copy the character */
4106 [ + + ]: 1484 : for (i = 0; i < len; i++)
4107 : : {
4108 [ + - + - ]: 742 : if (remaining == 0 || *source == '\0')
4109 : : break;
4110 : 742 : *target++ = *source++;
4111 : 742 : remaining--;
4112 : : }
4113 : :
4114 : : /*
4115 : : * If we hit premature end of string (ie, incomplete multibyte
4116 : : * character), try to pad out to the correct length with spaces. We
4117 : : * may not be able to pad completely, but we will always be able to
4118 : : * insert at least one pad space (since we'd not have quoted a
4119 : : * multibyte character). This should be enough to make a string that
4120 : : * the server will error out on.
4121 : : */
4122 [ - + ]: 742 : if (i < len)
4123 : : {
6538 tgl@sss.pgh.pa.us 4124 [ # # ]:UBC 0 : if (error)
4125 : 0 : *error = 1;
4126 [ # # ]: 0 : if (conn)
516 peter@eisentraut.org 4127 : 0 : libpq_append_conn_error(conn, "incomplete multibyte character");
6538 tgl@sss.pgh.pa.us 4128 [ # # ]: 0 : for (; i < len; i++)
4129 : : {
4130 [ # # ]: 0 : if (((size_t) (target - to)) / 2 >= length)
4131 : 0 : break;
4132 : 0 : *target++ = ' ';
4133 : : }
4134 : 0 : break;
4135 : : }
4136 : : }
4137 : :
4138 : : /* Write the terminating NUL character. */
7647 tgl@sss.pgh.pa.us 4139 :CBC 4936 : *target = '\0';
4140 : :
4141 : 4936 : return target - to;
4142 : : }
4143 : :
4144 : : size_t
6538 4145 : 4936 : PQescapeStringConn(PGconn *conn,
4146 : : char *to, const char *from, size_t length,
4147 : : int *error)
4148 : : {
4149 [ - + ]: 4936 : if (!conn)
4150 : : {
4151 : : /* force empty-string result */
6538 tgl@sss.pgh.pa.us 4152 :UBC 0 : *to = '\0';
4153 [ # # ]: 0 : if (error)
4154 : 0 : *error = 1;
4155 : 0 : return 0;
4156 : : }
4157 : :
776 tgl@sss.pgh.pa.us 4158 [ + - ]:CBC 4936 : if (conn->cmd_queue_head == NULL)
4159 : 4936 : pqClearConnErrorState(conn);
4160 : :
6538 4161 : 4936 : return PQescapeStringInternal(conn, to, from, length, error,
4162 : : conn->client_encoding,
4163 : 4936 : conn->std_strings);
4164 : : }
4165 : :
4166 : : size_t
6538 tgl@sss.pgh.pa.us 4167 :UBC 0 : PQescapeString(char *to, const char *from, size_t length)
4168 : : {
4169 : 0 : return PQescapeStringInternal(NULL, to, from, length, NULL,
4170 : : static_client_encoding,
4171 : : static_std_strings);
4172 : : }
4173 : :
4174 : :
4175 : : /*
4176 : : * Escape arbitrary strings. If as_ident is true, we escape the result
4177 : : * as an identifier; if false, as a literal. The result is returned in
4178 : : * a newly allocated buffer. If we fail due to an encoding violation or out
4179 : : * of memory condition, we return NULL, storing an error message into conn.
4180 : : */
4181 : : static char *
5197 rhaas@postgresql.org 4182 :CBC 1449 : PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
4183 : : {
4184 : : const char *s;
4185 : : char *result;
4186 : : char *rp;
5161 bruce@momjian.us 4187 : 1449 : int num_quotes = 0; /* single or double, depending on as_ident */
4188 : 1449 : int num_backslashes = 0;
4189 : : int input_len;
4190 : : int result_size;
4191 [ + + ]: 1449 : char quote_char = as_ident ? '"' : '\'';
4192 : :
4193 : : /* We must have a connection, else fail immediately. */
5197 rhaas@postgresql.org 4194 [ - + ]: 1449 : if (!conn)
5197 rhaas@postgresql.org 4195 :UBC 0 : return NULL;
4196 : :
776 tgl@sss.pgh.pa.us 4197 [ + - ]:CBC 1449 : if (conn->cmd_queue_head == NULL)
4198 : 1449 : pqClearConnErrorState(conn);
4199 : :
4200 : : /* Scan the string for characters that must be escaped. */
5197 rhaas@postgresql.org 4201 [ + + + - ]: 38985 : for (s = str; (s - str) < len && *s != '\0'; ++s)
4202 : : {
4203 [ + + ]: 37536 : if (*s == quote_char)
4204 : 38 : ++num_quotes;
4205 [ + + ]: 37498 : else if (*s == '\\')
4206 : 6 : ++num_backslashes;
4207 [ - + ]: 37492 : else if (IS_HIGHBIT_SET(*s))
4208 : : {
4209 : : int charlen;
4210 : :
4211 : : /* Slow path for possible multibyte characters */
5197 rhaas@postgresql.org 4212 :UBC 0 : charlen = pg_encoding_mblen(conn->client_encoding, s);
4213 : :
4214 : : /* Multibyte character overruns allowable length. */
4215 [ # # # # ]: 0 : if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
4216 : : {
516 peter@eisentraut.org 4217 : 0 : libpq_append_conn_error(conn, "incomplete multibyte character");
5197 rhaas@postgresql.org 4218 : 0 : return NULL;
4219 : : }
4220 : :
4221 : : /* Adjust s, bearing in mind that for loop will increment it. */
4222 : 0 : s += charlen - 1;
4223 : : }
4224 : : }
4225 : :
4226 : : /* Allocate output buffer. */
5197 rhaas@postgresql.org 4227 :CBC 1449 : input_len = s - str;
5161 bruce@momjian.us 4228 : 1449 : result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */
5197 rhaas@postgresql.org 4229 [ + + + + ]: 1449 : if (!as_ident && num_backslashes > 0)
4230 : 6 : result_size += num_backslashes + 2;
4231 : 1449 : result = rp = (char *) malloc(result_size);
4232 [ - + ]: 1449 : if (rp == NULL)
4233 : : {
516 peter@eisentraut.org 4234 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
5197 rhaas@postgresql.org 4235 : 0 : return NULL;
4236 : : }
4237 : :
4238 : : /*
4239 : : * If we are escaping a literal that contains backslashes, we use the
4240 : : * escape string syntax so that the result is correct under either value
4241 : : * of standard_conforming_strings. We also emit a leading space in this
4242 : : * case, to guard against the possibility that the result might be
4243 : : * interpolated immediately following an identifier.
4244 : : */
5197 rhaas@postgresql.org 4245 [ + + + + ]:CBC 1449 : if (!as_ident && num_backslashes > 0)
4246 : : {
4247 : 6 : *rp++ = ' ';
4248 : 6 : *rp++ = 'E';
4249 : : }
4250 : :
4251 : : /* Opening quote. */
4252 : 1449 : *rp++ = quote_char;
4253 : :
4254 : : /*
4255 : : * Use fast path if possible.
4256 : : *
4257 : : * We've already verified that the input string is well-formed in the
4258 : : * current encoding. If it contains no quotes and, in the case of
4259 : : * literal-escaping, no backslashes, then we can just copy it directly to
4260 : : * the output buffer, adding the necessary quotes.
4261 : : *
4262 : : * If not, we must rescan the input and process each character
4263 : : * individually.
4264 : : */
4265 [ + + + + : 1449 : if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
- + ]
4266 : : {
4267 : 1432 : memcpy(rp, str, input_len);
4268 : 1432 : rp += input_len;
4269 : : }
4270 : : else
4271 : : {
4272 [ + + ]: 1791 : for (s = str; s - str < input_len; ++s)
4273 : : {
4274 [ + + + - : 1774 : if (*s == quote_char || (!as_ident && *s == '\\'))
+ + ]
4275 : : {
4276 : 44 : *rp++ = *s;
4277 : 44 : *rp++ = *s;
4278 : : }
4279 [ + - ]: 1730 : else if (!IS_HIGHBIT_SET(*s))
4280 : 1730 : *rp++ = *s;
4281 : : else
4282 : : {
5161 bruce@momjian.us 4283 :UBC 0 : int i = pg_encoding_mblen(conn->client_encoding, s);
4284 : :
4285 : : while (1)
4286 : : {
5197 rhaas@postgresql.org 4287 : 0 : *rp++ = *s;
4288 [ # # ]: 0 : if (--i == 0)
4289 : 0 : break;
5161 bruce@momjian.us 4290 : 0 : ++s; /* for loop will provide the final increment */
4291 : : }
4292 : : }
4293 : : }
4294 : : }
4295 : :
4296 : : /* Closing quote and terminating NUL. */
5197 rhaas@postgresql.org 4297 :CBC 1449 : *rp++ = quote_char;
4298 : 1449 : *rp = '\0';
4299 : :
4300 : 1449 : return result;
4301 : : }
4302 : :
4303 : : char *
4304 : 812 : PQescapeLiteral(PGconn *conn, const char *str, size_t len)
4305 : : {
4306 : 812 : return PQescapeInternal(conn, str, len, false);
4307 : : }
4308 : :
4309 : : char *
4310 : 637 : PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
4311 : : {
4312 : 637 : return PQescapeInternal(conn, str, len, true);
4313 : : }
4314 : :
4315 : : /* HEX encoding support for bytea */
4316 : : static const char hextbl[] = "0123456789abcdef";
4317 : :
4318 : : static const int8 hexlookup[128] = {
4319 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4320 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4321 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4322 : : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4323 : : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4324 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4325 : : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4326 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4327 : : };
4328 : :
4329 : : static inline char
5367 tgl@sss.pgh.pa.us 4330 :UBC 0 : get_hex(char c)
4331 : : {
4332 : 0 : int res = -1;
4333 : :
4334 [ # # # # ]: 0 : if (c > 0 && c < 127)
4335 : 0 : res = hexlookup[(unsigned char) c];
4336 : :
4337 : 0 : return (char) res;
4338 : : }
4339 : :
4340 : :
4341 : : /*
4342 : : * PQescapeBytea - converts from binary string to the
4343 : : * minimal encoding necessary to include the string in an SQL
4344 : : * INSERT statement with a bytea type column as the target.
4345 : : *
4346 : : * We can use either hex or escape (traditional) encoding.
4347 : : * In escape mode, the following transformations are applied:
4348 : : * '\0' == ASCII 0 == \000
4349 : : * '\'' == ASCII 39 == ''
4350 : : * '\\' == ASCII 92 == \\
4351 : : * anything < 0x20, or > 0x7e ---> \ooo
4352 : : * (where ooo is an octal expression)
4353 : : *
4354 : : * If not std_strings, all backslashes sent to the output are doubled.
4355 : : */
4356 : : static unsigned char *
6538 4357 : 0 : PQescapeByteaInternal(PGconn *conn,
4358 : : const unsigned char *from, size_t from_length,
4359 : : size_t *to_length, bool std_strings, bool use_hex)
4360 : : {
4361 : : const unsigned char *vp;
4362 : : unsigned char *rp;
4363 : : unsigned char *result;
4364 : : size_t i;
4365 : : size_t len;
4366 [ # # ]: 0 : size_t bslash_len = (std_strings ? 1 : 2);
4367 : :
4368 : : /*
4369 : : * empty string has 1 char ('\0')
4370 : : */
7647 4371 : 0 : len = 1;
4372 : :
5367 4373 [ # # ]: 0 : if (use_hex)
4374 : : {
4375 : 0 : len += bslash_len + 1 + 2 * from_length;
4376 : : }
4377 : : else
4378 : : {
4379 : 0 : vp = from;
4380 [ # # ]: 0 : for (i = from_length; i > 0; i--, vp++)
4381 : : {
4382 [ # # # # ]: 0 : if (*vp < 0x20 || *vp > 0x7e)
4383 : 0 : len += bslash_len + 3;
4384 [ # # ]: 0 : else if (*vp == '\'')
4385 : 0 : len += 2;
4386 [ # # ]: 0 : else if (*vp == '\\')
4387 : 0 : len += bslash_len + bslash_len;
4388 : : else
4389 : 0 : len++;
4390 : : }
4391 : : }
4392 : :
6538 4393 : 0 : *to_length = len;
7647 4394 : 0 : rp = result = (unsigned char *) malloc(len);
4395 [ # # ]: 0 : if (rp == NULL)
4396 : : {
6538 4397 [ # # ]: 0 : if (conn)
516 peter@eisentraut.org 4398 : 0 : libpq_append_conn_error(conn, "out of memory");
7647 tgl@sss.pgh.pa.us 4399 : 0 : return NULL;
4400 : : }
4401 : :
5367 4402 [ # # ]: 0 : if (use_hex)
4403 : : {
4404 [ # # ]: 0 : if (!std_strings)
4405 : 0 : *rp++ = '\\';
4406 : 0 : *rp++ = '\\';
4407 : 0 : *rp++ = 'x';
4408 : : }
4409 : :
6538 4410 : 0 : vp = from;
4411 [ # # ]: 0 : for (i = from_length; i > 0; i--, vp++)
4412 : : {
5367 4413 : 0 : unsigned char c = *vp;
4414 : :
4415 [ # # ]: 0 : if (use_hex)
4416 : : {
4417 : 0 : *rp++ = hextbl[(c >> 4) & 0xF];
4418 : 0 : *rp++ = hextbl[c & 0xF];
4419 : : }
4420 [ # # # # ]: 0 : else if (c < 0x20 || c > 0x7e)
4421 : : {
6538 4422 [ # # ]: 0 : if (!std_strings)
4423 : 0 : *rp++ = '\\';
5695 4424 : 0 : *rp++ = '\\';
5367 4425 : 0 : *rp++ = (c >> 6) + '0';
4426 : 0 : *rp++ = ((c >> 3) & 07) + '0';
4427 : 0 : *rp++ = (c & 07) + '0';
4428 : : }
4429 [ # # ]: 0 : else if (c == '\'')
4430 : : {
6538 4431 : 0 : *rp++ = '\'';
4432 : 0 : *rp++ = '\'';
4433 : : }
5367 4434 [ # # ]: 0 : else if (c == '\\')
4435 : : {
6538 4436 [ # # ]: 0 : if (!std_strings)
4437 : : {
4438 : 0 : *rp++ = '\\';
4439 : 0 : *rp++ = '\\';
4440 : : }
4441 : 0 : *rp++ = '\\';
4442 : 0 : *rp++ = '\\';
4443 : : }
4444 : : else
5367 4445 : 0 : *rp++ = c;
4446 : : }
7647 4447 : 0 : *rp = '\0';
4448 : :
4449 : 0 : return result;
4450 : : }
4451 : :
4452 : : unsigned char *
6538 4453 : 0 : PQescapeByteaConn(PGconn *conn,
4454 : : const unsigned char *from, size_t from_length,
4455 : : size_t *to_length)
4456 : : {
4457 [ # # ]: 0 : if (!conn)
4458 : 0 : return NULL;
4459 : :
776 4460 [ # # ]: 0 : if (conn->cmd_queue_head == NULL)
4461 : 0 : pqClearConnErrorState(conn);
4462 : :
6538 4463 : 0 : return PQescapeByteaInternal(conn, from, from_length, to_length,
5367 4464 : 0 : conn->std_strings,
5170 4465 : 0 : (conn->sversion >= 90000));
4466 : : }
4467 : :
4468 : : unsigned char *
6538 4469 : 0 : PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
4470 : : {
4471 : 0 : return PQescapeByteaInternal(NULL, from, from_length, to_length,
4472 : : static_std_strings,
4473 : : false /* can't use hex */ );
4474 : : }
4475 : :
4476 : :
4477 : : #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
4478 : : #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
4479 : : #define OCTVAL(CH) ((CH) - '0')
4480 : :
4481 : : /*
4482 : : * PQunescapeBytea - converts the null terminated string representation
4483 : : * of a bytea, strtext, into binary, filling a buffer. It returns a
4484 : : * pointer to the buffer (or NULL on error), and the size of the
4485 : : * buffer in retbuflen. The pointer may subsequently be used as an
4486 : : * argument to the function PQfreemem.
4487 : : *
4488 : : * The following transformations are made:
4489 : : * \\ == ASCII 92 == \
4490 : : * \ooo == a byte whose value = ooo (ooo is an octal number)
4491 : : * \x == x (x is any character not matched by the above transformations)
4492 : : */
4493 : : unsigned char *
7647 4494 : 0 : PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
4495 : : {
4496 : : size_t strtextlen,
4497 : : buflen;
4498 : : unsigned char *buffer,
4499 : : *tmpbuf;
4500 : : size_t i,
4501 : : j;
4502 : :
7559 bruce@momjian.us 4503 [ # # ]: 0 : if (strtext == NULL)
7647 tgl@sss.pgh.pa.us 4504 : 0 : return NULL;
4505 : :
6777 4506 : 0 : strtextlen = strlen((const char *) strtext);
4507 : :
5367 4508 [ # # # # ]: 0 : if (strtext[0] == '\\' && strtext[1] == 'x')
4509 : 0 : {
4510 : : const unsigned char *s;
4511 : : unsigned char *p;
4512 : :
5161 bruce@momjian.us 4513 : 0 : buflen = (strtextlen - 2) / 2;
4514 : : /* Avoid unportable malloc(0) */
5367 tgl@sss.pgh.pa.us 4515 [ # # ]: 0 : buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1);
4516 [ # # ]: 0 : if (buffer == NULL)
4517 : 0 : return NULL;
4518 : :
4519 : 0 : s = strtext + 2;
4520 : 0 : p = buffer;
4521 [ # # ]: 0 : while (*s)
4522 : : {
4523 : : char v1,
4524 : : v2;
4525 : :
4526 : : /*
4527 : : * Bad input is silently ignored. Note that this includes
4528 : : * whitespace between hex pairs, which is allowed by byteain.
4529 : : */
4530 : 0 : v1 = get_hex(*s++);
4531 [ # # # # ]: 0 : if (!*s || v1 == (char) -1)
4532 : 0 : continue;
4533 : 0 : v2 = get_hex(*s++);
4534 [ # # ]: 0 : if (v2 != (char) -1)
4535 : 0 : *p++ = (v1 << 4) | v2;
4536 : : }
4537 : :
4538 : 0 : buflen = p - buffer;
4539 : : }
4540 : : else
4541 : : {
4542 : : /*
4543 : : * Length of input is max length of output, but add one to avoid
4544 : : * unportable malloc(0) if input is zero-length.
4545 : : */
5161 bruce@momjian.us 4546 : 0 : buffer = (unsigned char *) malloc(strtextlen + 1);
4547 [ # # ]: 0 : if (buffer == NULL)
4548 : 0 : return NULL;
4549 : :
4550 [ # # ]: 0 : for (i = j = 0; i < strtextlen;)
4551 : : {
4552 [ # # ]: 0 : switch (strtext[i])
4553 : : {
4554 : 0 : case '\\':
4555 : 0 : i++;
4556 [ # # ]: 0 : if (strtext[i] == '\\')
4557 : 0 : buffer[j++] = strtext[i++];
4558 : : else
4559 : : {
4560 [ # # # # ]: 0 : if ((ISFIRSTOCTDIGIT(strtext[i])) &&
4561 [ # # # # ]: 0 : (ISOCTDIGIT(strtext[i + 1])) &&
4562 [ # # # # ]: 0 : (ISOCTDIGIT(strtext[i + 2])))
4563 : : {
4564 : : int byte;
4565 : :
4566 : 0 : byte = OCTVAL(strtext[i++]);
4753 4567 : 0 : byte = (byte << 3) + OCTVAL(strtext[i++]);
4568 : 0 : byte = (byte << 3) + OCTVAL(strtext[i++]);
5161 4569 : 0 : buffer[j++] = byte;
4570 : : }
4571 : : }
4572 : :
4573 : : /*
4574 : : * Note: if we see '\' followed by something that isn't a
4575 : : * recognized escape sequence, we loop around having done
4576 : : * nothing except advance i. Therefore the something will
4577 : : * be emitted as ordinary data on the next cycle. Corner
4578 : : * case: '\' at end of string will just be discarded.
4579 : : */
4580 : 0 : break;
4581 : :
4582 : 0 : default:
4583 : 0 : buffer[j++] = strtext[i++];
4584 : 0 : break;
4585 : : }
4586 : : }
4587 : 0 : buflen = j; /* buflen is the length of the dequoted data */
4588 : : }
4589 : :
4590 : : /* Shrink the buffer to be no larger than necessary */
4591 : : /* +1 avoids unportable behavior when buflen==0 */
7471 tgl@sss.pgh.pa.us 4592 : 0 : tmpbuf = realloc(buffer, buflen + 1);
4593 : :
4594 : : /* It would only be a very brain-dead realloc that could fail, but... */
7612 bruce@momjian.us 4595 [ # # ]: 0 : if (!tmpbuf)
4596 : : {
4597 : 0 : free(buffer);
7500 tgl@sss.pgh.pa.us 4598 : 0 : return NULL;
4599 : : }
4600 : :
7647 4601 : 0 : *retbuflen = buflen;
7612 bruce@momjian.us 4602 : 0 : return tmpbuf;
4603 : : }
|