/Users/deen/code/yugabyte-db/src/postgres/src/interfaces/libpq/fe-protocol3.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * fe-protocol3.c |
4 | | * functions that are specific to frontend/backend protocol version 3 |
5 | | * |
6 | | * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/interfaces/libpq/fe-protocol3.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres_fe.h" |
16 | | |
17 | | #include <ctype.h> |
18 | | #include <fcntl.h> |
19 | | |
20 | | #include "libpq-fe.h" |
21 | | #include "libpq-int.h" |
22 | | |
23 | | #include "mb/pg_wchar.h" |
24 | | #include "port/pg_bswap.h" |
25 | | |
26 | | #ifdef WIN32 |
27 | | #include "win32.h" |
28 | | #else |
29 | | #include <unistd.h> |
30 | | #ifdef HAVE_NETINET_TCP_H |
31 | | #include <netinet/tcp.h> |
32 | | #endif |
33 | | #endif |
34 | | |
35 | | |
36 | | /* |
37 | | * This macro lists the backend message types that could be "long" (more |
38 | | * than a couple of kilobytes). |
39 | | */ |
40 | | #define VALID_LONG_MESSAGE_TYPE(id) \ |
41 | 0 | ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \ |
42 | 0 | (id) == 'E' || (id) == 'N' || (id) == 'A') |
43 | | |
44 | 13.0k | #define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) |
45 | | |
46 | | |
47 | | static void handleSyncLoss(PGconn *conn, char id, int msgLength); |
48 | | static int getRowDescriptions(PGconn *conn, int msgLength); |
49 | | static int getParamDescriptions(PGconn *conn, int msgLength); |
50 | | static int getAnotherTuple(PGconn *conn, int msgLength); |
51 | | static int getParameterStatus(PGconn *conn); |
52 | | static int getNotify(PGconn *conn); |
53 | | static int getCopyStart(PGconn *conn, ExecStatusType copytype); |
54 | | static int getReadyForQuery(PGconn *conn); |
55 | | static void reportErrorPosition(PQExpBuffer msg, const char *query, |
56 | | int loc, int encoding); |
57 | | static int build_startup_packet(const PGconn *conn, char *packet, |
58 | | const PQEnvironmentOption *options); |
59 | | |
60 | | |
61 | | /* |
62 | | * parseInput: if appropriate, parse input data from backend |
63 | | * until input is exhausted or a stopping state is reached. |
64 | | * Note that this function will NOT attempt to read more data from the backend. |
65 | | */ |
66 | | void |
67 | | pqParseInput3(PGconn *conn) |
68 | 379k | { |
69 | 379k | char id; |
70 | 379k | int msgLength; |
71 | 379k | int avail; |
72 | | |
73 | | /* |
74 | | * Loop to parse successive complete messages available in the buffer. |
75 | | */ |
76 | 379k | for (;;) |
77 | 1.14M | { |
78 | | /* |
79 | | * Try to read a message. First get the type code and length. Return |
80 | | * if not enough data. |
81 | | */ |
82 | 1.14M | conn->inCursor = conn->inStart; |
83 | 1.14M | if (pqGetc(&id, conn)) |
84 | 290k | return; |
85 | 851k | if (pqGetInt(&msgLength, 4, conn)) |
86 | 194 | return; |
87 | | |
88 | | /* |
89 | | * Try to validate message type/length here. A length less than 4 is |
90 | | * definitely broken. Large lengths should only be believed for a few |
91 | | * message types. |
92 | | */ |
93 | 851k | if (msgLength < 4) |
94 | 0 | { |
95 | 0 | handleSyncLoss(conn, id, msgLength); |
96 | 0 | return; |
97 | 0 | } |
98 | 851k | if (msgLength > 30000 && !0 VALID_LONG_MESSAGE_TYPE0 (id)) |
99 | 0 | { |
100 | 0 | handleSyncLoss(conn, id, msgLength); |
101 | 0 | return; |
102 | 0 | } |
103 | | |
104 | | /* |
105 | | * Can't process if message body isn't all here yet. |
106 | | */ |
107 | 851k | msgLength -= 4; |
108 | 851k | avail = conn->inEnd - conn->inCursor; |
109 | 851k | if (avail < msgLength) |
110 | 72 | { |
111 | | /* |
112 | | * Before returning, enlarge the input buffer if needed to hold |
113 | | * the whole message. This is better than leaving it to |
114 | | * pqReadData because we can avoid multiple cycles of realloc() |
115 | | * when the message is large; also, we can implement a reasonable |
116 | | * recovery strategy if we are unable to make the buffer big |
117 | | * enough. |
118 | | */ |
119 | 72 | if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, |
120 | 72 | conn)) |
121 | 0 | { |
122 | | /* |
123 | | * XXX add some better recovery code... plan is to skip over |
124 | | * the message using its length, then report an error. For the |
125 | | * moment, just treat this like loss of sync (which indeed it |
126 | | * might be!) |
127 | | */ |
128 | 0 | handleSyncLoss(conn, id, msgLength); |
129 | 0 | } |
130 | 72 | return; |
131 | 72 | } |
132 | | |
133 | | /* |
134 | | * NOTIFY and NOTICE messages can happen in any state; always process |
135 | | * them right away. |
136 | | * |
137 | | * Most other messages should only be processed while in BUSY state. |
138 | | * (In particular, in READY state we hold off further parsing until |
139 | | * the application collects the current PGresult.) |
140 | | * |
141 | | * However, if the state is IDLE then we got trouble; we need to deal |
142 | | * with the unexpected message somehow. |
143 | | * |
144 | | * ParameterStatus ('S') messages are a special case: in IDLE state we |
145 | | * must process 'em (this case could happen if a new value was adopted |
146 | | * from config file due to SIGHUP), but otherwise we hold off until |
147 | | * BUSY state. |
148 | | */ |
149 | 851k | if (id == 'A') |
150 | 0 | { |
151 | 0 | if (getNotify(conn)) |
152 | 0 | return; |
153 | 0 | } |
154 | 851k | else if (id == 'N') |
155 | 210 | { |
156 | 210 | if (pqGetErrorNotice3(conn, false)) |
157 | 0 | return; |
158 | 210 | } |
159 | 851k | else if (conn->asyncStatus != PGASYNC_BUSY) |
160 | 88.3k | { |
161 | | /* If not IDLE state, just wait ... */ |
162 | 88.3k | if (conn->asyncStatus != PGASYNC_IDLE) |
163 | 88.3k | return; |
164 | | |
165 | | /* |
166 | | * Unexpected message in IDLE state; need to recover somehow. |
167 | | * ERROR messages are handled using the notice processor; |
168 | | * ParameterStatus is handled normally; anything else is just |
169 | | * dropped on the floor after displaying a suitable warning |
170 | | * notice. (An ERROR is very possibly the backend telling us why |
171 | | * it is about to close the connection, so we don't want to just |
172 | | * discard it...) |
173 | | */ |
174 | 12 | if (id == 'E') |
175 | 0 | { |
176 | 0 | if (pqGetErrorNotice3(conn, false /* treat as notice */ )) |
177 | 0 | return; |
178 | 0 | } |
179 | 12 | else if (id == 'S') |
180 | 0 | { |
181 | 0 | if (getParameterStatus(conn)) |
182 | 0 | return; |
183 | 0 | } |
184 | 12 | else |
185 | 12 | { |
186 | 12 | pqInternalNotice(&conn->noticeHooks, |
187 | 12 | "message type 0x%02x arrived from server while idle", |
188 | 12 | id); |
189 | | /* Discard the unexpected message */ |
190 | 12 | conn->inCursor += msgLength; |
191 | 12 | } |
192 | 12 | } |
193 | 762k | else |
194 | 762k | { |
195 | | /* |
196 | | * In BUSY state, we can process everything. |
197 | | */ |
198 | 762k | switch (id) |
199 | 762k | { |
200 | 79.6k | case 'C': /* command complete */ |
201 | 79.6k | if (pqGets(&conn->workBuffer, conn)) |
202 | 0 | return; |
203 | 79.6k | if (conn->result == NULL) |
204 | 63.1k | { |
205 | 63.1k | conn->result = PQmakeEmptyPGresult(conn, |
206 | 63.1k | PGRES_COMMAND_OK); |
207 | 63.1k | if (!conn->result) |
208 | 0 | { |
209 | 0 | printfPQExpBuffer(&conn->errorMessage, |
210 | 0 | libpq_gettext("out of memory")); |
211 | 0 | pqSaveErrorResult(conn); |
212 | 0 | } |
213 | 63.1k | } |
214 | 79.6k | if (conn->result) |
215 | 79.6k | strlcpy(conn->result->cmdStatus, conn->workBuffer.data, |
216 | 79.6k | CMDSTATUS_LEN); |
217 | 79.6k | conn->asyncStatus = PGASYNC_READY; |
218 | 79.6k | break; |
219 | 5.42k | case 'E': /* error return */ |
220 | 5.42k | if (pqGetErrorNotice3(conn, true)) |
221 | 0 | return; |
222 | 5.42k | conn->asyncStatus = PGASYNC_READY; |
223 | 5.42k | break; |
224 | 86.6k | case 'Z': /* backend is ready for new query */ |
225 | 86.6k | if (getReadyForQuery(conn)) |
226 | 0 | return; |
227 | 86.6k | conn->asyncStatus = PGASYNC_IDLE; |
228 | 86.6k | break; |
229 | 3 | case 'I': /* empty query */ |
230 | 3 | if (conn->result == NULL) |
231 | 3 | { |
232 | 3 | conn->result = PQmakeEmptyPGresult(conn, |
233 | 3 | PGRES_EMPTY_QUERY); |
234 | 3 | if (!conn->result) |
235 | 0 | { |
236 | 0 | printfPQExpBuffer(&conn->errorMessage, |
237 | 0 | libpq_gettext("out of memory")); |
238 | 0 | pqSaveErrorResult(conn); |
239 | 0 | } |
240 | 3 | } |
241 | 3 | conn->asyncStatus = PGASYNC_READY; |
242 | 3 | break; |
243 | 4.81k | case '1': /* Parse Complete */ |
244 | | /* If we're doing PQprepare, we're done; else ignore */ |
245 | 4.81k | if (conn->queryclass == PGQUERY_PREPARE) |
246 | 55 | { |
247 | 55 | if (conn->result == NULL) |
248 | 55 | { |
249 | 55 | conn->result = PQmakeEmptyPGresult(conn, |
250 | 55 | PGRES_COMMAND_OK); |
251 | 55 | if (!conn->result) |
252 | 0 | { |
253 | 0 | printfPQExpBuffer(&conn->errorMessage, |
254 | 0 | libpq_gettext("out of memory")); |
255 | 0 | pqSaveErrorResult(conn); |
256 | 0 | } |
257 | 55 | } |
258 | 55 | conn->asyncStatus = PGASYNC_READY; |
259 | 55 | } |
260 | 4.81k | break; |
261 | 8.06k | case '2': /* Bind Complete */ |
262 | 8.06k | case '3': /* Close Complete */ |
263 | | /* Nothing to do for these message types */ |
264 | 8.06k | break; |
265 | 37.3k | case 'S': /* parameter status */ |
266 | 37.3k | if (getParameterStatus(conn)) |
267 | 0 | return; |
268 | 37.3k | break; |
269 | 37.3k | case 'K': /* secret key data from the backend */ |
270 | | |
271 | | /* |
272 | | * This is expected only during backend startup, but it's |
273 | | * just as easy to handle it as part of the main loop. |
274 | | * Save the data and continue processing. |
275 | | */ |
276 | 2.45k | if (pqGetInt(&(conn->be_pid), 4, conn)) |
277 | 0 | return; |
278 | 2.45k | if (pqGetInt(&(conn->be_key), 4, conn)) |
279 | 0 | return; |
280 | 2.45k | break; |
281 | 16.9k | case 'T': /* Row Description */ |
282 | 16.9k | if (conn->result != NULL && |
283 | 16.9k | conn->result->resultStatus == PGRES_FATAL_ERROR6 ) |
284 | 0 | { |
285 | | /* |
286 | | * We've already choked for some reason. Just discard |
287 | | * the data till we get to the end of the query. |
288 | | */ |
289 | 0 | conn->inCursor += msgLength; |
290 | 0 | } |
291 | 16.9k | else if (conn->result == NULL || |
292 | 16.9k | conn->queryclass == PGQUERY_DESCRIBE6 ) |
293 | 16.9k | { |
294 | | /* First 'T' in a query sequence */ |
295 | 16.9k | if (getRowDescriptions(conn, msgLength)) |
296 | 0 | return; |
297 | | /* getRowDescriptions() moves inStart itself */ |
298 | 16.9k | continue; |
299 | 16.9k | } |
300 | 6 | else |
301 | 6 | { |
302 | | /* |
303 | | * A new 'T' message is treated as the start of |
304 | | * another PGresult. (It is not clear that this is |
305 | | * really possible with the current backend.) We stop |
306 | | * parsing until the application accepts the current |
307 | | * result. |
308 | | */ |
309 | 6 | conn->asyncStatus = PGASYNC_READY; |
310 | 6 | return; |
311 | 6 | } |
312 | 0 | break; |
313 | 1.64k | case 'n': /* No Data */ |
314 | | |
315 | | /* |
316 | | * NoData indicates that we will not be seeing a |
317 | | * RowDescription message because the statement or portal |
318 | | * inquired about doesn't return rows. |
319 | | * |
320 | | * If we're doing a Describe, we have to pass something |
321 | | * back to the client, so set up a COMMAND_OK result, |
322 | | * instead of TUPLES_OK. Otherwise we can just ignore |
323 | | * this message. |
324 | | */ |
325 | 1.64k | if (conn->queryclass == PGQUERY_DESCRIBE) |
326 | 0 | { |
327 | 0 | if (conn->result == NULL) |
328 | 0 | { |
329 | 0 | conn->result = PQmakeEmptyPGresult(conn, |
330 | 0 | PGRES_COMMAND_OK); |
331 | 0 | if (!conn->result) |
332 | 0 | { |
333 | 0 | printfPQExpBuffer(&conn->errorMessage, |
334 | 0 | libpq_gettext("out of memory")); |
335 | 0 | pqSaveErrorResult(conn); |
336 | 0 | } |
337 | 0 | } |
338 | 0 | conn->asyncStatus = PGASYNC_READY; |
339 | 0 | } |
340 | 1.64k | break; |
341 | 0 | case 't': /* Parameter Description */ |
342 | 0 | if (getParamDescriptions(conn, msgLength)) |
343 | 0 | return; |
344 | | /* getParamDescriptions() moves inStart itself */ |
345 | 0 | continue; |
346 | 519k | case 'D': /* Data Row */ |
347 | 519k | if (conn->result != NULL && |
348 | 519k | conn->result->resultStatus == PGRES_TUPLES_OK519k ) |
349 | 519k | { |
350 | | /* Read another tuple of a normal query response */ |
351 | 519k | if (getAnotherTuple(conn, msgLength)) |
352 | 0 | return; |
353 | | /* getAnotherTuple() moves inStart itself */ |
354 | 519k | continue; |
355 | 519k | } |
356 | 18 | else if (conn->result != NULL && |
357 | 18 | conn->result->resultStatus == PGRES_FATAL_ERROR0 ) |
358 | 0 | { |
359 | | /* |
360 | | * We've already choked for some reason. Just discard |
361 | | * tuples till we get to the end of the query. |
362 | | */ |
363 | 0 | conn->inCursor += msgLength; |
364 | 0 | } |
365 | 18 | else |
366 | 18 | { |
367 | | /* Set up to report error at end of query */ |
368 | 18 | printfPQExpBuffer(&conn->errorMessage, |
369 | 18 | libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); |
370 | 18 | pqSaveErrorResult(conn); |
371 | | /* Discard the unexpected message */ |
372 | 18 | conn->inCursor += msgLength; |
373 | 18 | } |
374 | 18 | break; |
375 | 36 | case 'G': /* Start Copy In */ |
376 | 36 | if (getCopyStart(conn, PGRES_COPY_IN)) |
377 | 0 | return; |
378 | 36 | conn->asyncStatus = PGASYNC_COPY_IN; |
379 | 36 | break; |
380 | 0 | case 'H': /* Start Copy Out */ |
381 | 0 | if (getCopyStart(conn, PGRES_COPY_OUT)) |
382 | 0 | return; |
383 | 0 | conn->asyncStatus = PGASYNC_COPY_OUT; |
384 | 0 | conn->copy_already_done = 0; |
385 | 0 | break; |
386 | 0 | case 'W': /* Start Copy Both */ |
387 | 0 | if (getCopyStart(conn, PGRES_COPY_BOTH)) |
388 | 0 | return; |
389 | 0 | conn->asyncStatus = PGASYNC_COPY_BOTH; |
390 | 0 | conn->copy_already_done = 0; |
391 | 0 | break; |
392 | 0 | case 'd': /* Copy Data */ |
393 | | |
394 | | /* |
395 | | * If we see Copy Data, just silently drop it. This would |
396 | | * only occur if application exits COPY OUT mode too |
397 | | * early. |
398 | | */ |
399 | 0 | conn->inCursor += msgLength; |
400 | 0 | break; |
401 | 0 | case 'c': /* Copy Done */ |
402 | | |
403 | | /* |
404 | | * If we see Copy Done, just silently drop it. This is |
405 | | * the normal case during PQendcopy. We will keep |
406 | | * swallowing data, expecting to see command-complete for |
407 | | * the COPY command. |
408 | | */ |
409 | 0 | break; |
410 | 0 | default: |
411 | 0 | printfPQExpBuffer(&conn->errorMessage, |
412 | 0 | libpq_gettext( |
413 | 0 | "unexpected response from server; first received character was \"%c\"\n"), |
414 | 0 | id); |
415 | | /* build an error result holding the error message */ |
416 | 0 | pqSaveErrorResult(conn); |
417 | | /* not sure if we will see more, so go to ready state */ |
418 | 0 | conn->asyncStatus = PGASYNC_READY; |
419 | | /* Discard the unexpected message */ |
420 | 0 | conn->inCursor += msgLength; |
421 | 0 | break; |
422 | 762k | } /* switch on protocol character */ |
423 | 762k | } |
424 | | /* Successfully consumed this message */ |
425 | 226k | if (conn->inCursor == conn->inStart + 5 + msgLength) |
426 | 226k | { |
427 | | /* Normal case: parsing agrees with specified length */ |
428 | 226k | conn->inStart = conn->inCursor; |
429 | 226k | } |
430 | 18.4E | else |
431 | 18.4E | { |
432 | | /* Trouble --- report it */ |
433 | 18.4E | printfPQExpBuffer(&conn->errorMessage, |
434 | 18.4E | libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), |
435 | 18.4E | id); |
436 | | /* build an error result holding the error message */ |
437 | 18.4E | pqSaveErrorResult(conn); |
438 | 18.4E | conn->asyncStatus = PGASYNC_READY; |
439 | | /* trust the specified message length as what to skip */ |
440 | 18.4E | conn->inStart += 5 + msgLength; |
441 | 18.4E | } |
442 | 226k | } |
443 | 379k | } |
444 | | |
445 | | /* |
446 | | * handleSyncLoss: clean up after loss of message-boundary sync |
447 | | * |
448 | | * There isn't really a lot we can do here except abandon the connection. |
449 | | */ |
450 | | static void |
451 | | handleSyncLoss(PGconn *conn, char id, int msgLength) |
452 | 0 | { |
453 | 0 | printfPQExpBuffer(&conn->errorMessage, |
454 | 0 | libpq_gettext( |
455 | 0 | "lost synchronization with server: got message type \"%c\", length %d\n"), |
456 | 0 | id, msgLength); |
457 | | /* build an error result holding the error message */ |
458 | 0 | pqSaveErrorResult(conn); |
459 | 0 | conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ |
460 | | /* flush input data since we're giving up on processing it */ |
461 | 0 | pqDropConnection(conn, true); |
462 | 0 | conn->status = CONNECTION_BAD; /* No more connection to backend */ |
463 | 0 | } |
464 | | |
465 | | /* |
466 | | * parseInput subroutine to read a 'T' (row descriptions) message. |
467 | | * We'll build a new PGresult structure (unless called for a Describe |
468 | | * command for a prepared statement) containing the attribute data. |
469 | | * Returns: 0 if processed message successfully, EOF to suspend parsing |
470 | | * (the latter case is not actually used currently). |
471 | | * In the former case, conn->inStart has been advanced past the message. |
472 | | */ |
473 | | static int |
474 | | getRowDescriptions(PGconn *conn, int msgLength) |
475 | 16.9k | { |
476 | 16.9k | PGresult *result; |
477 | 16.9k | int nfields; |
478 | 16.9k | const char *errmsg; |
479 | 16.9k | int i; |
480 | | |
481 | | /* |
482 | | * When doing Describe for a prepared statement, there'll already be a |
483 | | * PGresult created by getParamDescriptions, and we should fill data into |
484 | | * that. Otherwise, create a new, empty PGresult. |
485 | | */ |
486 | 16.9k | if (conn->queryclass == PGQUERY_DESCRIBE) |
487 | 0 | { |
488 | 0 | if (conn->result) |
489 | 0 | result = conn->result; |
490 | 0 | else |
491 | 0 | result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); |
492 | 0 | } |
493 | 16.9k | else |
494 | 16.9k | result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); |
495 | 16.9k | if (!result) |
496 | 0 | { |
497 | 0 | errmsg = NULL; /* means "out of memory", see below */ |
498 | 0 | goto advance_and_error; |
499 | 0 | } |
500 | | |
501 | | /* parseInput already read the 'T' label and message length. */ |
502 | | /* the next two bytes are the number of fields */ |
503 | 16.9k | if (pqGetInt(&(result->numAttributes), 2, conn)) |
504 | 0 | { |
505 | | /* We should not run out of data here, so complain */ |
506 | 0 | errmsg = libpq_gettext("insufficient data in \"T\" message"); |
507 | 0 | goto advance_and_error; |
508 | 0 | } |
509 | 16.9k | nfields = result->numAttributes; |
510 | | |
511 | | /* allocate space for the attribute descriptors */ |
512 | 16.9k | if (nfields > 0) |
513 | 16.9k | { |
514 | 16.9k | result->attDescs = (PGresAttDesc *) |
515 | 16.9k | pqResultAlloc(result, nfields * sizeof(PGresAttDesc), true); |
516 | 16.9k | if (!result->attDescs) |
517 | 0 | { |
518 | 0 | errmsg = NULL; /* means "out of memory", see below */ |
519 | 0 | goto advance_and_error; |
520 | 0 | } |
521 | 16.9k | MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); |
522 | 16.9k | } |
523 | | |
524 | | /* result->binary is true only if ALL columns are binary */ |
525 | 16.9k | result->binary = (nfields > 0) ? 1 : 00 ; |
526 | | |
527 | | /* get type info */ |
528 | 41.5k | for (i = 0; i < nfields; i++24.6k ) |
529 | 24.6k | { |
530 | 24.6k | int tableid; |
531 | 24.6k | int columnid; |
532 | 24.6k | int typid; |
533 | 24.6k | int typlen; |
534 | 24.6k | int atttypmod; |
535 | 24.6k | int format; |
536 | | |
537 | 24.6k | if (pqGets(&conn->workBuffer, conn) || |
538 | 24.6k | pqGetInt(&tableid, 4, conn) || |
539 | 24.6k | pqGetInt(&columnid, 2, conn) || |
540 | 24.6k | pqGetInt(&typid, 4, conn) || |
541 | 24.6k | pqGetInt(&typlen, 2, conn) || |
542 | 24.6k | pqGetInt(&atttypmod, 4, conn) || |
543 | 24.6k | pqGetInt(&format, 2, conn)) |
544 | 0 | { |
545 | | /* We should not run out of data here, so complain */ |
546 | 0 | errmsg = libpq_gettext("insufficient data in \"T\" message"); |
547 | 0 | goto advance_and_error; |
548 | 0 | } |
549 | | |
550 | | /* |
551 | | * Since pqGetInt treats 2-byte integers as unsigned, we need to |
552 | | * coerce these results to signed form. |
553 | | */ |
554 | 24.6k | columnid = (int) ((int16) columnid); |
555 | 24.6k | typlen = (int) ((int16) typlen); |
556 | 24.6k | format = (int) ((int16) format); |
557 | | |
558 | 24.6k | result->attDescs[i].name = pqResultStrdup(result, |
559 | 24.6k | conn->workBuffer.data); |
560 | 24.6k | if (!result->attDescs[i].name) |
561 | 0 | { |
562 | 0 | errmsg = NULL; /* means "out of memory", see below */ |
563 | 0 | goto advance_and_error; |
564 | 0 | } |
565 | 24.6k | result->attDescs[i].tableid = tableid; |
566 | 24.6k | result->attDescs[i].columnid = columnid; |
567 | 24.6k | result->attDescs[i].format = format; |
568 | 24.6k | result->attDescs[i].typid = typid; |
569 | 24.6k | result->attDescs[i].typlen = typlen; |
570 | 24.6k | result->attDescs[i].atttypmod = atttypmod; |
571 | | |
572 | 24.6k | if (format != 1) |
573 | 19.9k | result->binary = 0; |
574 | 24.6k | } |
575 | | |
576 | | /* Sanity check that we absorbed all the data */ |
577 | 16.9k | if (conn->inCursor != conn->inStart + 5 + msgLength) |
578 | 0 | { |
579 | 0 | errmsg = libpq_gettext("extraneous data in \"T\" message"); |
580 | 0 | goto advance_and_error; |
581 | 0 | } |
582 | | |
583 | | /* Success! */ |
584 | 16.9k | conn->result = result; |
585 | | |
586 | | /* Advance inStart to show that the "T" message has been processed. */ |
587 | 16.9k | conn->inStart = conn->inCursor; |
588 | | |
589 | | /* |
590 | | * If we're doing a Describe, we're done, and ready to pass the result |
591 | | * back to the client. |
592 | | */ |
593 | 16.9k | if (conn->queryclass == PGQUERY_DESCRIBE) |
594 | 0 | { |
595 | 0 | conn->asyncStatus = PGASYNC_READY; |
596 | 0 | return 0; |
597 | 0 | } |
598 | | |
599 | | /* |
600 | | * We could perform additional setup for the new result set here, but for |
601 | | * now there's nothing else to do. |
602 | | */ |
603 | | |
604 | | /* And we're done. */ |
605 | 16.9k | return 0; |
606 | | |
607 | 0 | advance_and_error: |
608 | | /* Discard unsaved result, if any */ |
609 | 0 | if (result && result != conn->result) |
610 | 0 | PQclear(result); |
611 | | |
612 | | /* Discard the failed message by pretending we read it */ |
613 | 0 | conn->inStart += 5 + msgLength; |
614 | | |
615 | | /* |
616 | | * Replace partially constructed result with an error result. First |
617 | | * discard the old result to try to win back some memory. |
618 | | */ |
619 | 0 | pqClearAsyncResult(conn); |
620 | | |
621 | | /* |
622 | | * If preceding code didn't provide an error message, assume "out of |
623 | | * memory" was meant. The advantage of having this special case is that |
624 | | * freeing the old result first greatly improves the odds that gettext() |
625 | | * will succeed in providing a translation. |
626 | | */ |
627 | 0 | if (!errmsg) |
628 | 0 | errmsg = libpq_gettext("out of memory for query result"); |
629 | |
|
630 | 0 | printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); |
631 | 0 | pqSaveErrorResult(conn); |
632 | | |
633 | | /* |
634 | | * Return zero to allow input parsing to continue. Subsequent "D" |
635 | | * messages will be ignored until we get to end of data, since an error |
636 | | * result is already set up. |
637 | | */ |
638 | 0 | return 0; |
639 | 16.9k | } |
640 | | |
641 | | /* |
642 | | * parseInput subroutine to read a 't' (ParameterDescription) message. |
643 | | * We'll build a new PGresult structure containing the parameter data. |
644 | | * Returns: 0 if completed message, EOF if not enough data yet. |
645 | | * In the former case, conn->inStart has been advanced past the message. |
646 | | * |
647 | | * Note that if we run out of data, we have to release the partially |
648 | | * constructed PGresult, and rebuild it again next time. Fortunately, |
649 | | * that shouldn't happen often, since 't' messages usually fit in a packet. |
650 | | */ |
651 | | static int |
652 | | getParamDescriptions(PGconn *conn, int msgLength) |
653 | 0 | { |
654 | 0 | PGresult *result; |
655 | 0 | const char *errmsg = NULL; /* means "out of memory", see below */ |
656 | 0 | int nparams; |
657 | 0 | int i; |
658 | |
|
659 | 0 | result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); |
660 | 0 | if (!result) |
661 | 0 | goto advance_and_error; |
662 | | |
663 | | /* parseInput already read the 't' label and message length. */ |
664 | | /* the next two bytes are the number of parameters */ |
665 | 0 | if (pqGetInt(&(result->numParameters), 2, conn)) |
666 | 0 | goto not_enough_data; |
667 | 0 | nparams = result->numParameters; |
668 | | |
669 | | /* allocate space for the parameter descriptors */ |
670 | 0 | if (nparams > 0) |
671 | 0 | { |
672 | 0 | result->paramDescs = (PGresParamDesc *) |
673 | 0 | pqResultAlloc(result, nparams * sizeof(PGresParamDesc), true); |
674 | 0 | if (!result->paramDescs) |
675 | 0 | goto advance_and_error; |
676 | 0 | MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc)); |
677 | 0 | } |
678 | | |
679 | | /* get parameter info */ |
680 | 0 | for (i = 0; i < nparams; i++) |
681 | 0 | { |
682 | 0 | int typid; |
683 | |
|
684 | 0 | if (pqGetInt(&typid, 4, conn)) |
685 | 0 | goto not_enough_data; |
686 | 0 | result->paramDescs[i].typid = typid; |
687 | 0 | } |
688 | | |
689 | | /* Sanity check that we absorbed all the data */ |
690 | 0 | if (conn->inCursor != conn->inStart + 5 + msgLength) |
691 | 0 | { |
692 | 0 | errmsg = libpq_gettext("extraneous data in \"t\" message"); |
693 | 0 | goto advance_and_error; |
694 | 0 | } |
695 | | |
696 | | /* Success! */ |
697 | 0 | conn->result = result; |
698 | | |
699 | | /* Advance inStart to show that the "t" message has been processed. */ |
700 | 0 | conn->inStart = conn->inCursor; |
701 | |
|
702 | 0 | return 0; |
703 | | |
704 | 0 | not_enough_data: |
705 | 0 | PQclear(result); |
706 | 0 | return EOF; |
707 | | |
708 | 0 | advance_and_error: |
709 | | /* Discard unsaved result, if any */ |
710 | 0 | if (result && result != conn->result) |
711 | 0 | PQclear(result); |
712 | | |
713 | | /* Discard the failed message by pretending we read it */ |
714 | 0 | conn->inStart += 5 + msgLength; |
715 | | |
716 | | /* |
717 | | * Replace partially constructed result with an error result. First |
718 | | * discard the old result to try to win back some memory. |
719 | | */ |
720 | 0 | pqClearAsyncResult(conn); |
721 | | |
722 | | /* |
723 | | * If preceding code didn't provide an error message, assume "out of |
724 | | * memory" was meant. The advantage of having this special case is that |
725 | | * freeing the old result first greatly improves the odds that gettext() |
726 | | * will succeed in providing a translation. |
727 | | */ |
728 | 0 | if (!errmsg) |
729 | 0 | errmsg = libpq_gettext("out of memory"); |
730 | 0 | printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); |
731 | 0 | pqSaveErrorResult(conn); |
732 | | |
733 | | /* |
734 | | * Return zero to allow input parsing to continue. Essentially, we've |
735 | | * replaced the COMMAND_OK result with an error result, but since this |
736 | | * doesn't affect the protocol state, it's fine. |
737 | | */ |
738 | 0 | return 0; |
739 | 0 | } |
740 | | |
741 | | /* |
742 | | * parseInput subroutine to read a 'D' (row data) message. |
743 | | * We fill rowbuf with column pointers and then call the row processor. |
744 | | * Returns: 0 if processed message successfully, EOF to suspend parsing |
745 | | * (the latter case is not actually used currently). |
746 | | * In the former case, conn->inStart has been advanced past the message. |
747 | | */ |
748 | | static int |
749 | | getAnotherTuple(PGconn *conn, int msgLength) |
750 | 519k | { |
751 | 519k | PGresult *result = conn->result; |
752 | 519k | int nfields = result->numAttributes; |
753 | 519k | const char *errmsg; |
754 | 519k | PGdataValue *rowbuf; |
755 | 519k | int tupnfields; /* # fields from tuple */ |
756 | 519k | int vlen; /* length of the current field value */ |
757 | 519k | int i; |
758 | | |
759 | | /* Get the field count and make sure it's what we expect */ |
760 | 519k | if (pqGetInt(&tupnfields, 2, conn)) |
761 | 0 | { |
762 | | /* We should not run out of data here, so complain */ |
763 | 0 | errmsg = libpq_gettext("insufficient data in \"D\" message"); |
764 | 0 | goto advance_and_error; |
765 | 0 | } |
766 | | |
767 | 519k | if (tupnfields != nfields) |
768 | 0 | { |
769 | 0 | errmsg = libpq_gettext("unexpected field count in \"D\" message"); |
770 | 0 | goto advance_and_error; |
771 | 0 | } |
772 | | |
773 | | /* Resize row buffer if needed */ |
774 | 519k | rowbuf = conn->rowBuf; |
775 | 519k | if (nfields > conn->rowBufLen) |
776 | 0 | { |
777 | 0 | rowbuf = (PGdataValue *) realloc(rowbuf, |
778 | 0 | nfields * sizeof(PGdataValue)); |
779 | 0 | if (!rowbuf) |
780 | 0 | { |
781 | 0 | errmsg = NULL; /* means "out of memory", see below */ |
782 | 0 | goto advance_and_error; |
783 | 0 | } |
784 | 0 | conn->rowBuf = rowbuf; |
785 | 0 | conn->rowBufLen = nfields; |
786 | 0 | } |
787 | | |
788 | | /* Scan the fields */ |
789 | 1.29M | for (i = 0; 519k i < nfields; i++777k ) |
790 | 777k | { |
791 | | /* get the value length */ |
792 | 777k | if (pqGetInt(&vlen, 4, conn)) |
793 | 0 | { |
794 | | /* We should not run out of data here, so complain */ |
795 | 0 | errmsg = libpq_gettext("insufficient data in \"D\" message"); |
796 | 0 | goto advance_and_error; |
797 | 0 | } |
798 | 777k | rowbuf[i].len = vlen; |
799 | | |
800 | | /* |
801 | | * rowbuf[i].value always points to the next address in the data |
802 | | * buffer even if the value is NULL. This allows row processors to |
803 | | * estimate data sizes more easily. |
804 | | */ |
805 | 777k | rowbuf[i].value = conn->inBuffer + conn->inCursor; |
806 | | |
807 | | /* Skip over the data value */ |
808 | 777k | if (vlen > 0) |
809 | 769k | { |
810 | 769k | if (pqSkipnchar(vlen, conn)) |
811 | 0 | { |
812 | | /* We should not run out of data here, so complain */ |
813 | 0 | errmsg = libpq_gettext("insufficient data in \"D\" message"); |
814 | 0 | goto advance_and_error; |
815 | 0 | } |
816 | 769k | } |
817 | 777k | } |
818 | | |
819 | | /* Sanity check that we absorbed all the data */ |
820 | 519k | if (conn->inCursor != conn->inStart + 5 + msgLength) |
821 | 0 | { |
822 | 0 | errmsg = libpq_gettext("extraneous data in \"D\" message"); |
823 | 0 | goto advance_and_error; |
824 | 0 | } |
825 | | |
826 | | /* Advance inStart to show that the "D" message has been processed. */ |
827 | 519k | conn->inStart = conn->inCursor; |
828 | | |
829 | | /* Process the collected row */ |
830 | 519k | errmsg = NULL; |
831 | 519k | if (pqRowProcessor(conn, &errmsg)) |
832 | 519k | return 0; /* normal, successful exit */ |
833 | | |
834 | 658 | goto set_error_result; /* pqRowProcessor failed, report it */ |
835 | | |
836 | 658 | advance_and_error: |
837 | | /* Discard the failed message by pretending we read it */ |
838 | 0 | conn->inStart += 5 + msgLength; |
839 | |
|
840 | 0 | set_error_result: |
841 | | |
842 | | /* |
843 | | * Replace partially constructed result with an error result. First |
844 | | * discard the old result to try to win back some memory. |
845 | | */ |
846 | 0 | pqClearAsyncResult(conn); |
847 | | |
848 | | /* |
849 | | * If preceding code didn't provide an error message, assume "out of |
850 | | * memory" was meant. The advantage of having this special case is that |
851 | | * freeing the old result first greatly improves the odds that gettext() |
852 | | * will succeed in providing a translation. |
853 | | */ |
854 | 0 | if (!errmsg) |
855 | 0 | errmsg = libpq_gettext("out of memory for query result"); |
856 | |
|
857 | 0 | printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); |
858 | 0 | pqSaveErrorResult(conn); |
859 | | |
860 | | /* |
861 | | * Return zero to allow input parsing to continue. Subsequent "D" |
862 | | * messages will be ignored until we get to end of data, since an error |
863 | | * result is already set up. |
864 | | */ |
865 | 0 | return 0; |
866 | 0 | } |
867 | | |
868 | | |
869 | | /* |
870 | | * Attempt to read an Error or Notice response message. |
871 | | * This is possible in several places, so we break it out as a subroutine. |
872 | | * Entry: 'E' or 'N' message type and length have already been consumed. |
873 | | * Exit: returns 0 if successfully consumed message. |
874 | | * returns EOF if not enough data. |
875 | | */ |
876 | | int |
877 | | pqGetErrorNotice3(PGconn *conn, bool isError) |
878 | 5.81k | { |
879 | 5.81k | PGresult *res = NULL; |
880 | 5.81k | bool have_position = false; |
881 | 5.81k | PQExpBufferData workBuf; |
882 | 5.81k | char id; |
883 | | |
884 | | /* |
885 | | * If this is an error message, pre-emptively clear any incomplete query |
886 | | * result we may have. We'd just throw it away below anyway, and |
887 | | * releasing it before collecting the error might avoid out-of-memory. |
888 | | */ |
889 | 5.81k | if (isError) |
890 | 5.60k | pqClearAsyncResult(conn); |
891 | | |
892 | | /* |
893 | | * Since the fields might be pretty long, we create a temporary |
894 | | * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended |
895 | | * for stuff that is expected to be short. We shouldn't use |
896 | | * conn->errorMessage either, since this might be only a notice. |
897 | | */ |
898 | 5.81k | initPQExpBuffer(&workBuf); |
899 | | |
900 | | /* |
901 | | * Make a PGresult to hold the accumulated fields. We temporarily lie |
902 | | * about the result status, so that PQmakeEmptyPGresult doesn't uselessly |
903 | | * copy conn->errorMessage. |
904 | | * |
905 | | * NB: This allocation can fail, if you run out of memory. The rest of the |
906 | | * function handles that gracefully, and we still try to set the error |
907 | | * message as the connection's error message. |
908 | | */ |
909 | 5.81k | res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); |
910 | 5.81k | if (res) |
911 | 5.81k | res->resultStatus = isError ? PGRES_FATAL_ERROR5.60k : PGRES_NONFATAL_ERROR210 ; |
912 | | |
913 | | /* |
914 | | * Read the fields and save into res. |
915 | | * |
916 | | * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether |
917 | | * we saw a PG_DIAG_STATEMENT_POSITION field. |
918 | | */ |
919 | 5.81k | for (;;) |
920 | 48.7k | { |
921 | 48.7k | if (pqGetc(&id, conn)) |
922 | 0 | goto fail; |
923 | 48.7k | if (id == '\0') |
924 | 5.81k | break; /* terminator found */ |
925 | 42.9k | if (pqGets(&workBuf, conn)) |
926 | 0 | goto fail; |
927 | 42.9k | pqSaveMessageField(res, id, workBuf.data); |
928 | 42.9k | if (id == PG_DIAG_SQLSTATE) |
929 | 5.81k | strlcpy(conn->last_sqlstate, workBuf.data, |
930 | 42.9k | sizeof(conn->last_sqlstate)); |
931 | 37.0k | else if (id == PG_DIAG_STATEMENT_POSITION) |
932 | 257 | have_position = true; |
933 | 42.9k | } |
934 | | |
935 | | /* |
936 | | * Save the active query text, if any, into res as well; but only if we |
937 | | * might need it for an error cursor display, which is only true if there |
938 | | * is a PG_DIAG_STATEMENT_POSITION field. |
939 | | */ |
940 | 5.81k | if (have_position && conn->last_query257 && res257 ) |
941 | 257 | res->errQuery = pqResultStrdup(res, conn->last_query); |
942 | | |
943 | | /* |
944 | | * Now build the "overall" error message for PQresultErrorMessage. |
945 | | */ |
946 | 5.81k | resetPQExpBuffer(&workBuf); |
947 | 5.81k | pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context); |
948 | | |
949 | | /* |
950 | | * Either save error as current async result, or just emit the notice. |
951 | | */ |
952 | 5.81k | if (isError) |
953 | 5.60k | { |
954 | 5.60k | if (res) |
955 | 5.60k | res->errMsg = pqResultStrdup(res, workBuf.data); |
956 | 5.60k | pqClearAsyncResult(conn); /* redundant, but be safe */ |
957 | 5.60k | conn->result = res; |
958 | 5.60k | if (PQExpBufferDataBroken(workBuf)) |
959 | 0 | printfPQExpBuffer(&conn->errorMessage, |
960 | 0 | libpq_gettext("out of memory")); |
961 | 5.60k | else |
962 | 5.60k | appendPQExpBufferStr(&conn->errorMessage, workBuf.data); |
963 | 5.60k | } |
964 | 211 | else |
965 | 211 | { |
966 | | /* if we couldn't allocate the result set, just discard the NOTICE */ |
967 | 211 | if (res) |
968 | 210 | { |
969 | | /* We can cheat a little here and not copy the message. */ |
970 | 210 | res->errMsg = workBuf.data; |
971 | 210 | if (res->noticeHooks.noticeRec != NULL) |
972 | 210 | res->noticeHooks.noticeRec(res->noticeHooks.noticeRecArg, res); |
973 | 210 | PQclear(res); |
974 | 210 | } |
975 | 211 | } |
976 | | |
977 | 5.81k | termPQExpBuffer(&workBuf); |
978 | 5.81k | return 0; |
979 | | |
980 | 0 | fail: |
981 | 0 | PQclear(res); |
982 | 0 | termPQExpBuffer(&workBuf); |
983 | 0 | return EOF; |
984 | 5.81k | } |
985 | | |
986 | | /* |
987 | | * Construct an error message from the fields in the given PGresult, |
988 | | * appending it to the contents of "msg". |
989 | | */ |
990 | | void |
991 | | pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, |
992 | | PGVerbosity verbosity, PGContextVisibility show_context) |
993 | 5.81k | { |
994 | 5.81k | const char *val; |
995 | 5.81k | const char *querytext = NULL; |
996 | 5.81k | int querypos = 0; |
997 | | |
998 | | /* If we couldn't allocate a PGresult, just say "out of memory" */ |
999 | 5.81k | if (res == NULL) |
1000 | 0 | { |
1001 | 0 | appendPQExpBuffer(msg, libpq_gettext("out of memory\n")); |
1002 | 0 | return; |
1003 | 0 | } |
1004 | | |
1005 | | /* |
1006 | | * If we don't have any broken-down fields, just return the base message. |
1007 | | * This mainly applies if we're given a libpq-generated error result. |
1008 | | */ |
1009 | 5.81k | if (res->errFields == NULL) |
1010 | 0 | { |
1011 | 0 | if (res->errMsg && res->errMsg[0]) |
1012 | 0 | appendPQExpBufferStr(msg, res->errMsg); |
1013 | 0 | else |
1014 | 0 | appendPQExpBuffer(msg, libpq_gettext("no error message available\n")); |
1015 | 0 | return; |
1016 | 0 | } |
1017 | | |
1018 | | /* Else build error message from relevant fields */ |
1019 | 5.81k | val = PQresultErrorField(res, PG_DIAG_SEVERITY); |
1020 | 5.81k | if (val) |
1021 | 5.81k | appendPQExpBuffer(msg, "%s: ", val); |
1022 | 5.81k | if (verbosity == PQERRORS_VERBOSE) |
1023 | 0 | { |
1024 | 0 | val = PQresultErrorField(res, PG_DIAG_SQLSTATE); |
1025 | 0 | if (val) |
1026 | 0 | appendPQExpBuffer(msg, "%s: ", val); |
1027 | 0 | } |
1028 | 5.81k | val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); |
1029 | 5.81k | if (val) |
1030 | 5.81k | appendPQExpBufferStr(msg, val); |
1031 | 5.81k | val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); |
1032 | 5.81k | if (val) |
1033 | 257 | { |
1034 | 257 | if (verbosity != PQERRORS_TERSE && res->errQuery != NULL) |
1035 | 257 | { |
1036 | | /* emit position as a syntax cursor display */ |
1037 | 257 | querytext = res->errQuery; |
1038 | 257 | querypos = atoi(val); |
1039 | 257 | } |
1040 | 0 | else |
1041 | 0 | { |
1042 | | /* emit position as text addition to primary message */ |
1043 | | /* translator: %s represents a digit string */ |
1044 | 0 | appendPQExpBuffer(msg, libpq_gettext(" at character %s"), |
1045 | 0 | val); |
1046 | 0 | } |
1047 | 257 | } |
1048 | 5.55k | else |
1049 | 5.55k | { |
1050 | 5.55k | val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); |
1051 | 5.55k | if (val) |
1052 | 0 | { |
1053 | 0 | querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); |
1054 | 0 | if (verbosity != PQERRORS_TERSE && querytext != NULL) |
1055 | 0 | { |
1056 | | /* emit position as a syntax cursor display */ |
1057 | 0 | querypos = atoi(val); |
1058 | 0 | } |
1059 | 0 | else |
1060 | 0 | { |
1061 | | /* emit position as text addition to primary message */ |
1062 | | /* translator: %s represents a digit string */ |
1063 | 0 | appendPQExpBuffer(msg, libpq_gettext(" at character %s"), |
1064 | 0 | val); |
1065 | 0 | } |
1066 | 0 | } |
1067 | 5.55k | } |
1068 | 5.81k | appendPQExpBufferChar(msg, '\n'); |
1069 | 5.81k | if (verbosity != PQERRORS_TERSE) |
1070 | 5.80k | { |
1071 | 5.80k | if (querytext && querypos > 0257 ) |
1072 | 257 | reportErrorPosition(msg, querytext, querypos, |
1073 | 257 | res->client_encoding); |
1074 | 5.80k | val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); |
1075 | 5.80k | if (val) |
1076 | 563 | appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val); |
1077 | 5.80k | val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); |
1078 | 5.80k | if (val) |
1079 | 132 | appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val); |
1080 | 5.80k | val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); |
1081 | 5.80k | if (val) |
1082 | 0 | appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val); |
1083 | 5.80k | if (show_context == PQSHOW_CONTEXT_ALWAYS || |
1084 | 5.80k | (show_context == PQSHOW_CONTEXT_ERRORS && |
1085 | 5.80k | res->resultStatus == PGRES_FATAL_ERROR)) |
1086 | 5.59k | { |
1087 | 5.59k | val = PQresultErrorField(res, PG_DIAG_CONTEXT); |
1088 | 5.59k | if (val) |
1089 | 100 | appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"), |
1090 | 100 | val); |
1091 | 5.59k | } |
1092 | 5.80k | } |
1093 | 5.81k | if (verbosity == PQERRORS_VERBOSE) |
1094 | 0 | { |
1095 | 0 | val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME); |
1096 | 0 | if (val) |
1097 | 0 | appendPQExpBuffer(msg, |
1098 | 0 | libpq_gettext("SCHEMA NAME: %s\n"), val); |
1099 | 0 | val = PQresultErrorField(res, PG_DIAG_TABLE_NAME); |
1100 | 0 | if (val) |
1101 | 0 | appendPQExpBuffer(msg, |
1102 | 0 | libpq_gettext("TABLE NAME: %s\n"), val); |
1103 | 0 | val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME); |
1104 | 0 | if (val) |
1105 | 0 | appendPQExpBuffer(msg, |
1106 | 0 | libpq_gettext("COLUMN NAME: %s\n"), val); |
1107 | 0 | val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME); |
1108 | 0 | if (val) |
1109 | 0 | appendPQExpBuffer(msg, |
1110 | 0 | libpq_gettext("DATATYPE NAME: %s\n"), val); |
1111 | 0 | val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME); |
1112 | 0 | if (val) |
1113 | 0 | appendPQExpBuffer(msg, |
1114 | 0 | libpq_gettext("CONSTRAINT NAME: %s\n"), val); |
1115 | 0 | } |
1116 | 5.81k | if (verbosity == PQERRORS_VERBOSE) |
1117 | 0 | { |
1118 | 0 | const char *valf; |
1119 | 0 | const char *vall; |
1120 | |
|
1121 | 0 | valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); |
1122 | 0 | vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); |
1123 | 0 | val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); |
1124 | 0 | if (val || valf || vall) |
1125 | 0 | { |
1126 | 0 | appendPQExpBufferStr(msg, libpq_gettext("LOCATION: ")); |
1127 | 0 | if (val) |
1128 | 0 | appendPQExpBuffer(msg, libpq_gettext("%s, "), val); |
1129 | 0 | if (valf && vall) /* unlikely we'd have just one */ |
1130 | 0 | appendPQExpBuffer(msg, libpq_gettext("%s:%s"), |
1131 | 0 | valf, vall); |
1132 | 0 | appendPQExpBufferChar(msg, '\n'); |
1133 | 0 | } |
1134 | 0 | } |
1135 | 5.81k | } |
1136 | | |
1137 | | /* |
1138 | | * Add an error-location display to the error message under construction. |
1139 | | * |
1140 | | * The cursor location is measured in logical characters; the query string |
1141 | | * is presumed to be in the specified encoding. |
1142 | | */ |
1143 | | static void |
1144 | | reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) |
1145 | 257 | { |
1146 | 1.54k | #define DISPLAY_SIZE 60 /* screen width limit, in screen cols */ |
1147 | 257 | #define MIN_RIGHT_CUT 1014 /* try to keep this far away from EOL */ |
1148 | | |
1149 | 257 | char *wquery; |
1150 | 257 | int slen, |
1151 | 257 | cno, |
1152 | 257 | i, |
1153 | 257 | *qidx, |
1154 | 257 | *scridx, |
1155 | 257 | qoffset, |
1156 | 257 | scroffset, |
1157 | 257 | ibeg, |
1158 | 257 | iend, |
1159 | 257 | loc_line; |
1160 | 257 | bool mb_encoding, |
1161 | 257 | beg_trunc, |
1162 | 257 | end_trunc; |
1163 | | |
1164 | | /* Convert loc from 1-based to 0-based; no-op if out of range */ |
1165 | 257 | loc--; |
1166 | 257 | if (loc < 0) |
1167 | 0 | return; |
1168 | | |
1169 | | /* Need a writable copy of the query */ |
1170 | 257 | wquery = strdup(query); |
1171 | 257 | if (wquery == NULL) |
1172 | 0 | return; /* fail silently if out of memory */ |
1173 | | |
1174 | | /* |
1175 | | * Each character might occupy multiple physical bytes in the string, and |
1176 | | * in some Far Eastern character sets it might take more than one screen |
1177 | | * column as well. We compute the starting byte offset and starting |
1178 | | * screen column of each logical character, and store these in qidx[] and |
1179 | | * scridx[] respectively. |
1180 | | */ |
1181 | | |
1182 | | /* we need a safe allocation size... */ |
1183 | 257 | slen = strlen(wquery) + 1; |
1184 | | |
1185 | 257 | qidx = (int *) malloc(slen * sizeof(int)); |
1186 | 257 | if (qidx == NULL) |
1187 | 0 | { |
1188 | 0 | free(wquery); |
1189 | 0 | return; |
1190 | 0 | } |
1191 | 257 | scridx = (int *) malloc(slen * sizeof(int)); |
1192 | 257 | if (scridx == NULL) |
1193 | 0 | { |
1194 | 0 | free(qidx); |
1195 | 0 | free(wquery); |
1196 | 0 | return; |
1197 | 0 | } |
1198 | | |
1199 | | /* We can optimize a bit if it's a single-byte encoding */ |
1200 | 257 | mb_encoding = (pg_encoding_max_length(encoding) != 1); |
1201 | | |
1202 | | /* |
1203 | | * Within the scanning loop, cno is the current character's logical |
1204 | | * number, qoffset is its offset in wquery, and scroffset is its starting |
1205 | | * logical screen column (all indexed from 0). "loc" is the logical |
1206 | | * character number of the error location. We scan to determine loc_line |
1207 | | * (the 1-based line number containing loc) and ibeg/iend (first character |
1208 | | * number and last+1 character number of the line containing loc). Note |
1209 | | * that qidx[] and scridx[] are filled only as far as iend. |
1210 | | */ |
1211 | 257 | qoffset = 0; |
1212 | 257 | scroffset = 0; |
1213 | 257 | loc_line = 1; |
1214 | 257 | ibeg = 0; |
1215 | 257 | iend = -1; /* -1 means not set yet */ |
1216 | | |
1217 | 11.1k | for (cno = 0; wquery[qoffset] != '\0'; cno++10.9k ) |
1218 | 10.9k | { |
1219 | 10.9k | char ch = wquery[qoffset]; |
1220 | | |
1221 | 10.9k | qidx[cno] = qoffset; |
1222 | 10.9k | scridx[cno] = scroffset; |
1223 | | |
1224 | | /* |
1225 | | * Replace tabs with spaces in the writable copy. (Later we might |
1226 | | * want to think about coping with their variable screen width, but |
1227 | | * not today.) |
1228 | | */ |
1229 | 10.9k | if (ch == '\t') |
1230 | 34 | wquery[qoffset] = ' '; |
1231 | | |
1232 | | /* |
1233 | | * If end-of-line, count lines and mark positions. Each \r or \n |
1234 | | * counts as a line except when \r \n appear together. |
1235 | | */ |
1236 | 10.9k | else if (ch == '\r' || ch == '\n') |
1237 | 49 | { |
1238 | 49 | if (cno < loc) |
1239 | 39 | { |
1240 | 39 | if (ch == '\r' || |
1241 | 39 | cno == 0 || |
1242 | 39 | wquery[qidx[cno - 1]] != '\r') |
1243 | 39 | loc_line++; |
1244 | | /* extract beginning = last line start before loc. */ |
1245 | 39 | ibeg = cno + 1; |
1246 | 39 | } |
1247 | 10 | else |
1248 | 10 | { |
1249 | | /* set extract end. */ |
1250 | 10 | iend = cno; |
1251 | | /* done scanning. */ |
1252 | 10 | break; |
1253 | 10 | } |
1254 | 49 | } |
1255 | | |
1256 | | /* Advance */ |
1257 | 10.9k | if (mb_encoding) |
1258 | 10.9k | { |
1259 | 10.9k | int w; |
1260 | | |
1261 | 10.9k | w = pg_encoding_dsplen(encoding, &wquery[qoffset]); |
1262 | | /* treat any non-tab control chars as width 1 */ |
1263 | 10.9k | if (w <= 0) |
1264 | 39 | w = 1; |
1265 | 10.9k | scroffset += w; |
1266 | 10.9k | qoffset += PQmblenBounded(&wquery[qoffset], encoding); |
1267 | 10.9k | } |
1268 | 0 | else |
1269 | 0 | { |
1270 | | /* We assume wide chars only exist in multibyte encodings */ |
1271 | 0 | scroffset++; |
1272 | 0 | qoffset++; |
1273 | 0 | } |
1274 | 10.9k | } |
1275 | | /* Fix up if we didn't find an end-of-line after loc */ |
1276 | 257 | if (iend < 0) |
1277 | 247 | { |
1278 | 247 | iend = cno; /* query length in chars, +1 */ |
1279 | 247 | qidx[iend] = qoffset; |
1280 | 247 | scridx[iend] = scroffset; |
1281 | 247 | } |
1282 | | |
1283 | | /* Print only if loc is within computed query length */ |
1284 | 257 | if (loc <= cno) |
1285 | 257 | { |
1286 | | /* If the line extracted is too long, we truncate it. */ |
1287 | 257 | beg_trunc = false; |
1288 | 257 | end_trunc = false; |
1289 | 257 | if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) |
1290 | 12 | { |
1291 | | /* |
1292 | | * We first truncate right if it is enough. This code might be |
1293 | | * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide |
1294 | | * character right there, but that should be okay. |
1295 | | */ |
1296 | 12 | if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT) |
1297 | 10 | { |
1298 | 1.22k | while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) |
1299 | 1.21k | iend--; |
1300 | 10 | end_trunc = true; |
1301 | 10 | } |
1302 | 2 | else |
1303 | 2 | { |
1304 | | /* Truncate right if not too close to loc. */ |
1305 | 2 | while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend]) |
1306 | 0 | { |
1307 | 0 | iend--; |
1308 | 0 | end_trunc = true; |
1309 | 0 | } |
1310 | | |
1311 | | /* Truncate left if still too long. */ |
1312 | 50 | while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) |
1313 | 48 | { |
1314 | 48 | ibeg++; |
1315 | 48 | beg_trunc = true; |
1316 | 48 | } |
1317 | 2 | } |
1318 | 12 | } |
1319 | | |
1320 | | /* truncate working copy at desired endpoint */ |
1321 | 257 | wquery[qidx[iend]] = '\0'; |
1322 | | |
1323 | | /* Begin building the finished message. */ |
1324 | 257 | i = msg->len; |
1325 | 257 | appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line); |
1326 | 257 | if (beg_trunc) |
1327 | 2 | appendPQExpBufferStr(msg, "..."); |
1328 | | |
1329 | | /* |
1330 | | * While we have the prefix in the msg buffer, compute its screen |
1331 | | * width. |
1332 | | */ |
1333 | 257 | scroffset = 0; |
1334 | 2.31k | for (; i < msg->len; i += 2.06k PQmblenBounded2.06k (&msg->data[i], encoding)) |
1335 | 2.06k | { |
1336 | 2.06k | int w = pg_encoding_dsplen(encoding, &msg->data[i]); |
1337 | | |
1338 | 2.06k | if (w <= 0) |
1339 | 0 | w = 1; |
1340 | 2.06k | scroffset += w; |
1341 | 2.06k | } |
1342 | | |
1343 | | /* Finish up the LINE message line. */ |
1344 | 257 | appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]); |
1345 | 257 | if (end_trunc) |
1346 | 10 | appendPQExpBufferStr(msg, "..."); |
1347 | 257 | appendPQExpBufferChar(msg, '\n'); |
1348 | | |
1349 | | /* Now emit the cursor marker line. */ |
1350 | 257 | scroffset += scridx[loc] - scridx[ibeg]; |
1351 | 6.01k | for (i = 0; i < scroffset; i++5.75k ) |
1352 | 5.75k | appendPQExpBufferChar(msg, ' '); |
1353 | 257 | appendPQExpBufferChar(msg, '^'); |
1354 | 257 | appendPQExpBufferChar(msg, '\n'); |
1355 | 257 | } |
1356 | | |
1357 | | /* Clean up. */ |
1358 | 257 | free(scridx); |
1359 | 257 | free(qidx); |
1360 | 257 | free(wquery); |
1361 | 257 | } |
1362 | | |
1363 | | |
1364 | | /* |
1365 | | * Attempt to read a ParameterStatus message. |
1366 | | * This is possible in several places, so we break it out as a subroutine. |
1367 | | * Entry: 'S' message type and length have already been consumed. |
1368 | | * Exit: returns 0 if successfully consumed message. |
1369 | | * returns EOF if not enough data. |
1370 | | */ |
1371 | | static int |
1372 | | getParameterStatus(PGconn *conn) |
1373 | 37.3k | { |
1374 | 37.3k | PQExpBufferData valueBuf; |
1375 | | |
1376 | | /* Get the parameter name */ |
1377 | 37.3k | if (pqGets(&conn->workBuffer, conn)) |
1378 | 0 | return EOF; |
1379 | | /* Get the parameter value (could be large) */ |
1380 | 37.3k | initPQExpBuffer(&valueBuf); |
1381 | 37.3k | if (pqGets(&valueBuf, conn)) |
1382 | 0 | { |
1383 | 0 | termPQExpBuffer(&valueBuf); |
1384 | 0 | return EOF; |
1385 | 0 | } |
1386 | | /* And save it */ |
1387 | 37.3k | pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data); |
1388 | 37.3k | termPQExpBuffer(&valueBuf); |
1389 | 37.3k | return 0; |
1390 | 37.3k | } |
1391 | | |
1392 | | |
1393 | | /* |
1394 | | * Attempt to read a Notify response message. |
1395 | | * This is possible in several places, so we break it out as a subroutine. |
1396 | | * Entry: 'A' message type and length have already been consumed. |
1397 | | * Exit: returns 0 if successfully consumed Notify message. |
1398 | | * returns EOF if not enough data. |
1399 | | */ |
1400 | | static int |
1401 | | getNotify(PGconn *conn) |
1402 | 0 | { |
1403 | 0 | int be_pid; |
1404 | 0 | char *svname; |
1405 | 0 | int nmlen; |
1406 | 0 | int extralen; |
1407 | 0 | PGnotify *newNotify; |
1408 | |
|
1409 | 0 | if (pqGetInt(&be_pid, 4, conn)) |
1410 | 0 | return EOF; |
1411 | 0 | if (pqGets(&conn->workBuffer, conn)) |
1412 | 0 | return EOF; |
1413 | | /* must save name while getting extra string */ |
1414 | 0 | svname = strdup(conn->workBuffer.data); |
1415 | 0 | if (!svname) |
1416 | 0 | return EOF; |
1417 | 0 | if (pqGets(&conn->workBuffer, conn)) |
1418 | 0 | { |
1419 | 0 | free(svname); |
1420 | 0 | return EOF; |
1421 | 0 | } |
1422 | | |
1423 | | /* |
1424 | | * Store the strings right after the PQnotify structure so it can all be |
1425 | | * freed at once. We don't use NAMEDATALEN because we don't want to tie |
1426 | | * this interface to a specific server name length. |
1427 | | */ |
1428 | 0 | nmlen = strlen(svname); |
1429 | 0 | extralen = strlen(conn->workBuffer.data); |
1430 | 0 | newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); |
1431 | 0 | if (newNotify) |
1432 | 0 | { |
1433 | 0 | newNotify->relname = (char *) newNotify + sizeof(PGnotify); |
1434 | 0 | strcpy(newNotify->relname, svname); |
1435 | 0 | newNotify->extra = newNotify->relname + nmlen + 1; |
1436 | 0 | strcpy(newNotify->extra, conn->workBuffer.data); |
1437 | 0 | newNotify->be_pid = be_pid; |
1438 | 0 | newNotify->next = NULL; |
1439 | 0 | if (conn->notifyTail) |
1440 | 0 | conn->notifyTail->next = newNotify; |
1441 | 0 | else |
1442 | 0 | conn->notifyHead = newNotify; |
1443 | 0 | conn->notifyTail = newNotify; |
1444 | 0 | } |
1445 | |
|
1446 | 0 | free(svname); |
1447 | 0 | return 0; |
1448 | 0 | } |
1449 | | |
1450 | | /* |
1451 | | * getCopyStart - process CopyInResponse, CopyOutResponse or |
1452 | | * CopyBothResponse message |
1453 | | * |
1454 | | * parseInput already read the message type and length. |
1455 | | */ |
1456 | | static int |
1457 | | getCopyStart(PGconn *conn, ExecStatusType copytype) |
1458 | 36 | { |
1459 | 36 | PGresult *result; |
1460 | 36 | int nfields; |
1461 | 36 | int i; |
1462 | | |
1463 | 36 | result = PQmakeEmptyPGresult(conn, copytype); |
1464 | 36 | if (!result) |
1465 | 0 | goto failure; |
1466 | | |
1467 | 36 | if (pqGetc(&conn->copy_is_binary, conn)) |
1468 | 0 | goto failure; |
1469 | 36 | result->binary = conn->copy_is_binary; |
1470 | | /* the next two bytes are the number of fields */ |
1471 | 36 | if (pqGetInt(&(result->numAttributes), 2, conn)) |
1472 | 0 | goto failure; |
1473 | 36 | nfields = result->numAttributes; |
1474 | | |
1475 | | /* allocate space for the attribute descriptors */ |
1476 | 36 | if (nfields > 0) |
1477 | 36 | { |
1478 | 36 | result->attDescs = (PGresAttDesc *) |
1479 | 36 | pqResultAlloc(result, nfields * sizeof(PGresAttDesc), true); |
1480 | 36 | if (!result->attDescs) |
1481 | 0 | goto failure; |
1482 | 36 | MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); |
1483 | 36 | } |
1484 | | |
1485 | 162 | for (i = 0; 36 i < nfields; i++126 ) |
1486 | 126 | { |
1487 | 126 | int format; |
1488 | | |
1489 | 126 | if (pqGetInt(&format, 2, conn)) |
1490 | 0 | goto failure; |
1491 | | |
1492 | | /* |
1493 | | * Since pqGetInt treats 2-byte integers as unsigned, we need to |
1494 | | * coerce these results to signed form. |
1495 | | */ |
1496 | 126 | format = (int) ((int16) format); |
1497 | 126 | result->attDescs[i].format = format; |
1498 | 126 | } |
1499 | | |
1500 | | /* Success! */ |
1501 | 36 | conn->result = result; |
1502 | 36 | return 0; |
1503 | | |
1504 | 0 | failure: |
1505 | 0 | PQclear(result); |
1506 | 0 | return EOF; |
1507 | 36 | } |
1508 | | |
1509 | | /* |
1510 | | * getReadyForQuery - process ReadyForQuery message |
1511 | | */ |
1512 | | static int |
1513 | | getReadyForQuery(PGconn *conn) |
1514 | 86.7k | { |
1515 | 86.7k | char xact_status; |
1516 | | |
1517 | 86.7k | if (pqGetc(&xact_status, conn)) |
1518 | 0 | return EOF; |
1519 | 86.7k | switch (xact_status) |
1520 | 86.7k | { |
1521 | 41.1k | case 'I': |
1522 | 41.1k | conn->xactStatus = PQTRANS_IDLE; |
1523 | 41.1k | break; |
1524 | 42.2k | case 'T': |
1525 | 42.2k | conn->xactStatus = PQTRANS_INTRANS; |
1526 | 42.2k | break; |
1527 | 3.22k | case 'E': |
1528 | 3.22k | conn->xactStatus = PQTRANS_INERROR; |
1529 | 3.22k | break; |
1530 | 0 | default: |
1531 | 0 | conn->xactStatus = PQTRANS_UNKNOWN; |
1532 | 0 | break; |
1533 | 86.7k | } |
1534 | | |
1535 | 86.6k | return 0; |
1536 | 86.7k | } |
1537 | | |
1538 | | /* |
1539 | | * getCopyDataMessage - fetch next CopyData message, process async messages |
1540 | | * |
1541 | | * Returns length word of CopyData message (> 0), or 0 if no complete |
1542 | | * message available, -1 if end of copy, -2 if error. |
1543 | | */ |
1544 | | static int |
1545 | | getCopyDataMessage(PGconn *conn) |
1546 | 0 | { |
1547 | 0 | char id; |
1548 | 0 | int msgLength; |
1549 | 0 | int avail; |
1550 | |
|
1551 | 0 | for (;;) |
1552 | 0 | { |
1553 | | /* |
1554 | | * Do we have the next input message? To make life simpler for async |
1555 | | * callers, we keep returning 0 until the next message is fully |
1556 | | * available, even if it is not Copy Data. |
1557 | | */ |
1558 | 0 | conn->inCursor = conn->inStart; |
1559 | 0 | if (pqGetc(&id, conn)) |
1560 | 0 | return 0; |
1561 | 0 | if (pqGetInt(&msgLength, 4, conn)) |
1562 | 0 | return 0; |
1563 | 0 | if (msgLength < 4) |
1564 | 0 | { |
1565 | 0 | handleSyncLoss(conn, id, msgLength); |
1566 | 0 | return -2; |
1567 | 0 | } |
1568 | 0 | avail = conn->inEnd - conn->inCursor; |
1569 | 0 | if (avail < msgLength - 4) |
1570 | 0 | { |
1571 | | /* |
1572 | | * Before returning, enlarge the input buffer if needed to hold |
1573 | | * the whole message. See notes in parseInput. |
1574 | | */ |
1575 | 0 | if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength - 4, |
1576 | 0 | conn)) |
1577 | 0 | { |
1578 | | /* |
1579 | | * XXX add some better recovery code... plan is to skip over |
1580 | | * the message using its length, then report an error. For the |
1581 | | * moment, just treat this like loss of sync (which indeed it |
1582 | | * might be!) |
1583 | | */ |
1584 | 0 | handleSyncLoss(conn, id, msgLength); |
1585 | 0 | return -2; |
1586 | 0 | } |
1587 | 0 | return 0; |
1588 | 0 | } |
1589 | | |
1590 | | /* |
1591 | | * If it's a legitimate async message type, process it. (NOTIFY |
1592 | | * messages are not currently possible here, but we handle them for |
1593 | | * completeness.) Otherwise, if it's anything except Copy Data, |
1594 | | * report end-of-copy. |
1595 | | */ |
1596 | 0 | switch (id) |
1597 | 0 | { |
1598 | 0 | case 'A': /* NOTIFY */ |
1599 | 0 | if (getNotify(conn)) |
1600 | 0 | return 0; |
1601 | 0 | break; |
1602 | 0 | case 'N': /* NOTICE */ |
1603 | 0 | if (pqGetErrorNotice3(conn, false)) |
1604 | 0 | return 0; |
1605 | 0 | break; |
1606 | 0 | case 'S': /* ParameterStatus */ |
1607 | 0 | if (getParameterStatus(conn)) |
1608 | 0 | return 0; |
1609 | 0 | break; |
1610 | 0 | case 'd': /* Copy Data, pass it back to caller */ |
1611 | 0 | return msgLength; |
1612 | 0 | case 'c': |
1613 | | |
1614 | | /* |
1615 | | * If this is a CopyDone message, exit COPY_OUT mode and let |
1616 | | * caller read status with PQgetResult(). If we're in |
1617 | | * COPY_BOTH mode, return to COPY_IN mode. |
1618 | | */ |
1619 | 0 | if (conn->asyncStatus == PGASYNC_COPY_BOTH) |
1620 | 0 | conn->asyncStatus = PGASYNC_COPY_IN; |
1621 | 0 | else |
1622 | 0 | conn->asyncStatus = PGASYNC_BUSY; |
1623 | 0 | return -1; |
1624 | 0 | default: /* treat as end of copy */ |
1625 | | |
1626 | | /* |
1627 | | * Any other message terminates either COPY_IN or COPY_BOTH |
1628 | | * mode. |
1629 | | */ |
1630 | 0 | conn->asyncStatus = PGASYNC_BUSY; |
1631 | 0 | return -1; |
1632 | 0 | } |
1633 | | |
1634 | | /* Drop the processed message and loop around for another */ |
1635 | 0 | conn->inStart = conn->inCursor; |
1636 | 0 | } |
1637 | 0 | } |
1638 | | |
1639 | | /* |
1640 | | * PQgetCopyData - read a row of data from the backend during COPY OUT |
1641 | | * or COPY BOTH |
1642 | | * |
1643 | | * If successful, sets *buffer to point to a malloc'd row of data, and |
1644 | | * returns row length (always > 0) as result. |
1645 | | * Returns 0 if no row available yet (only possible if async is true), |
1646 | | * -1 if end of copy (consult PQgetResult), or -2 if error (consult |
1647 | | * PQerrorMessage). |
1648 | | */ |
1649 | | int |
1650 | | pqGetCopyData3(PGconn *conn, char **buffer, int async) |
1651 | 0 | { |
1652 | 0 | int msgLength; |
1653 | |
|
1654 | 0 | for (;;) |
1655 | 0 | { |
1656 | | /* |
1657 | | * Collect the next input message. To make life simpler for async |
1658 | | * callers, we keep returning 0 until the next message is fully |
1659 | | * available, even if it is not Copy Data. |
1660 | | */ |
1661 | 0 | msgLength = getCopyDataMessage(conn); |
1662 | 0 | if (msgLength < 0) |
1663 | 0 | return msgLength; /* end-of-copy or error */ |
1664 | 0 | if (msgLength == 0) |
1665 | 0 | { |
1666 | | /* Don't block if async read requested */ |
1667 | 0 | if (async) |
1668 | 0 | return 0; |
1669 | | /* Need to load more data */ |
1670 | 0 | if (pqWait(true, false, conn) || |
1671 | 0 | pqReadData(conn) < 0) |
1672 | 0 | return -2; |
1673 | 0 | continue; |
1674 | 0 | } |
1675 | | |
1676 | | /* |
1677 | | * Drop zero-length messages (shouldn't happen anyway). Otherwise |
1678 | | * pass the data back to the caller. |
1679 | | */ |
1680 | 0 | msgLength -= 4; |
1681 | 0 | if (msgLength > 0) |
1682 | 0 | { |
1683 | 0 | *buffer = (char *) malloc(msgLength + 1); |
1684 | 0 | if (*buffer == NULL) |
1685 | 0 | { |
1686 | 0 | printfPQExpBuffer(&conn->errorMessage, |
1687 | 0 | libpq_gettext("out of memory\n")); |
1688 | 0 | return -2; |
1689 | 0 | } |
1690 | 0 | memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength); |
1691 | 0 | (*buffer)[msgLength] = '\0'; /* Add terminating null */ |
1692 | | |
1693 | | /* Mark message consumed */ |
1694 | 0 | conn->inStart = conn->inCursor + msgLength; |
1695 | |
|
1696 | 0 | return msgLength; |
1697 | 0 | } |
1698 | | |
1699 | | /* Empty, so drop it and loop around for another */ |
1700 | 0 | conn->inStart = conn->inCursor; |
1701 | 0 | } |
1702 | 0 | } |
1703 | | |
1704 | | /* |
1705 | | * PQgetline - gets a newline-terminated string from the backend. |
1706 | | * |
1707 | | * See fe-exec.c for documentation. |
1708 | | */ |
1709 | | int |
1710 | | pqGetline3(PGconn *conn, char *s, int maxlen) |
1711 | 0 | { |
1712 | 0 | int status; |
1713 | |
|
1714 | 0 | if (conn->sock == PGINVALID_SOCKET || |
1715 | 0 | (conn->asyncStatus != PGASYNC_COPY_OUT && |
1716 | 0 | conn->asyncStatus != PGASYNC_COPY_BOTH) || |
1717 | 0 | conn->copy_is_binary) |
1718 | 0 | { |
1719 | 0 | printfPQExpBuffer(&conn->errorMessage, |
1720 | 0 | libpq_gettext("PQgetline: not doing text COPY OUT\n")); |
1721 | 0 | *s = '\0'; |
1722 | 0 | return EOF; |
1723 | 0 | } |
1724 | | |
1725 | 0 | while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0) |
1726 | 0 | { |
1727 | | /* need to load more data */ |
1728 | 0 | if (pqWait(true, false, conn) || |
1729 | 0 | pqReadData(conn) < 0) |
1730 | 0 | { |
1731 | 0 | *s = '\0'; |
1732 | 0 | return EOF; |
1733 | 0 | } |
1734 | 0 | } |
1735 | | |
1736 | 0 | if (status < 0) |
1737 | 0 | { |
1738 | | /* End of copy detected; gin up old-style terminator */ |
1739 | 0 | strcpy(s, "\\."); |
1740 | 0 | return 0; |
1741 | 0 | } |
1742 | | |
1743 | | /* Add null terminator, and strip trailing \n if present */ |
1744 | 0 | if (s[status - 1] == '\n') |
1745 | 0 | { |
1746 | 0 | s[status - 1] = '\0'; |
1747 | 0 | return 0; |
1748 | 0 | } |
1749 | 0 | else |
1750 | 0 | { |
1751 | 0 | s[status] = '\0'; |
1752 | 0 | return 1; |
1753 | 0 | } |
1754 | 0 | } |
1755 | | |
1756 | | /* |
1757 | | * PQgetlineAsync - gets a COPY data row without blocking. |
1758 | | * |
1759 | | * See fe-exec.c for documentation. |
1760 | | */ |
1761 | | int |
1762 | | pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize) |
1763 | 0 | { |
1764 | 0 | int msgLength; |
1765 | 0 | int avail; |
1766 | |
|
1767 | 0 | if (conn->asyncStatus != PGASYNC_COPY_OUT |
1768 | 0 | && conn->asyncStatus != PGASYNC_COPY_BOTH) |
1769 | 0 | return -1; /* we are not doing a copy... */ |
1770 | | |
1771 | | /* |
1772 | | * Recognize the next input message. To make life simpler for async |
1773 | | * callers, we keep returning 0 until the next message is fully available |
1774 | | * even if it is not Copy Data. This should keep PQendcopy from blocking. |
1775 | | * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.) |
1776 | | */ |
1777 | 0 | msgLength = getCopyDataMessage(conn); |
1778 | 0 | if (msgLength < 0) |
1779 | 0 | return -1; /* end-of-copy or error */ |
1780 | 0 | if (msgLength == 0) |
1781 | 0 | return 0; /* no data yet */ |
1782 | | |
1783 | | /* |
1784 | | * Move data from libpq's buffer to the caller's. In the case where a |
1785 | | * prior call found the caller's buffer too small, we use |
1786 | | * conn->copy_already_done to remember how much of the row was already |
1787 | | * returned to the caller. |
1788 | | */ |
1789 | 0 | conn->inCursor += conn->copy_already_done; |
1790 | 0 | avail = msgLength - 4 - conn->copy_already_done; |
1791 | 0 | if (avail <= bufsize) |
1792 | 0 | { |
1793 | | /* Able to consume the whole message */ |
1794 | 0 | memcpy(buffer, &conn->inBuffer[conn->inCursor], avail); |
1795 | | /* Mark message consumed */ |
1796 | 0 | conn->inStart = conn->inCursor + avail; |
1797 | | /* Reset state for next time */ |
1798 | 0 | conn->copy_already_done = 0; |
1799 | 0 | return avail; |
1800 | 0 | } |
1801 | 0 | else |
1802 | 0 | { |
1803 | | /* We must return a partial message */ |
1804 | 0 | memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize); |
1805 | | /* The message is NOT consumed from libpq's buffer */ |
1806 | 0 | conn->copy_already_done += bufsize; |
1807 | 0 | return bufsize; |
1808 | 0 | } |
1809 | 0 | } |
1810 | | |
1811 | | /* |
1812 | | * PQendcopy |
1813 | | * |
1814 | | * See fe-exec.c for documentation. |
1815 | | */ |
1816 | | int |
1817 | | pqEndcopy3(PGconn *conn) |
1818 | 0 | { |
1819 | 0 | PGresult *result; |
1820 | |
|
1821 | 0 | if (conn->asyncStatus != PGASYNC_COPY_IN && |
1822 | 0 | conn->asyncStatus != PGASYNC_COPY_OUT && |
1823 | 0 | conn->asyncStatus != PGASYNC_COPY_BOTH) |
1824 | 0 | { |
1825 | 0 | printfPQExpBuffer(&conn->errorMessage, |
1826 | 0 | libpq_gettext("no COPY in progress\n")); |
1827 | 0 | return 1; |
1828 | 0 | } |
1829 | | |
1830 | | /* Send the CopyDone message if needed */ |
1831 | 0 | if (conn->asyncStatus == PGASYNC_COPY_IN || |
1832 | 0 | conn->asyncStatus == PGASYNC_COPY_BOTH) |
1833 | 0 | { |
1834 | 0 | if (pqPutMsgStart('c', false, conn) < 0 || |
1835 | 0 | pqPutMsgEnd(conn) < 0) |
1836 | 0 | return 1; |
1837 | | |
1838 | | /* |
1839 | | * If we sent the COPY command in extended-query mode, we must issue a |
1840 | | * Sync as well. |
1841 | | */ |
1842 | 0 | if (conn->queryclass != PGQUERY_SIMPLE) |
1843 | 0 | { |
1844 | 0 | if (pqPutMsgStart('S', false, conn) < 0 || |
1845 | 0 | pqPutMsgEnd(conn) < 0) |
1846 | 0 | return 1; |
1847 | 0 | } |
1848 | 0 | } |
1849 | | |
1850 | | /* |
1851 | | * make sure no data is waiting to be sent, abort if we are non-blocking |
1852 | | * and the flush fails |
1853 | | */ |
1854 | 0 | if (pqFlush(conn) && pqIsnonblocking(conn)) |
1855 | 0 | return 1; |
1856 | | |
1857 | | /* Return to active duty */ |
1858 | 0 | conn->asyncStatus = PGASYNC_BUSY; |
1859 | 0 | resetPQExpBuffer(&conn->errorMessage); |
1860 | | |
1861 | | /* |
1862 | | * Non blocking connections may have to abort at this point. If everyone |
1863 | | * played the game there should be no problem, but in error scenarios the |
1864 | | * expected messages may not have arrived yet. (We are assuming that the |
1865 | | * backend's packetizing will ensure that CommandComplete arrives along |
1866 | | * with the CopyDone; are there corner cases where that doesn't happen?) |
1867 | | */ |
1868 | 0 | if (pqIsnonblocking(conn) && PQisBusy(conn)) |
1869 | 0 | return 1; |
1870 | | |
1871 | | /* Wait for the completion response */ |
1872 | 0 | result = PQgetResult(conn); |
1873 | | |
1874 | | /* Expecting a successful result */ |
1875 | 0 | if (result && result->resultStatus == PGRES_COMMAND_OK) |
1876 | 0 | { |
1877 | 0 | PQclear(result); |
1878 | 0 | return 0; |
1879 | 0 | } |
1880 | | |
1881 | | /* |
1882 | | * Trouble. For backwards-compatibility reasons, we issue the error |
1883 | | * message as if it were a notice (would be nice to get rid of this |
1884 | | * silliness, but too many apps probably don't handle errors from |
1885 | | * PQendcopy reasonably). Note that the app can still obtain the error |
1886 | | * status from the PGconn object. |
1887 | | */ |
1888 | 0 | if (conn->errorMessage.len > 0) |
1889 | 0 | { |
1890 | | /* We have to strip the trailing newline ... pain in neck... */ |
1891 | 0 | char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; |
1892 | |
|
1893 | 0 | if (svLast == '\n') |
1894 | 0 | conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; |
1895 | 0 | pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); |
1896 | 0 | conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; |
1897 | 0 | } |
1898 | |
|
1899 | 0 | PQclear(result); |
1900 | |
|
1901 | 0 | return 1; |
1902 | 0 | } |
1903 | | |
1904 | | |
1905 | | /* |
1906 | | * PQfn - Send a function call to the POSTGRES backend. |
1907 | | * |
1908 | | * See fe-exec.c for documentation. |
1909 | | */ |
1910 | | PGresult * |
1911 | | pqFunctionCall3(PGconn *conn, Oid fnid, |
1912 | | int *result_buf, int *actual_result_len, |
1913 | | int result_is_int, |
1914 | | const PQArgBlock *args, int nargs) |
1915 | 0 | { |
1916 | 0 | bool needInput = false; |
1917 | 0 | ExecStatusType status = PGRES_FATAL_ERROR; |
1918 | 0 | char id; |
1919 | 0 | int msgLength; |
1920 | 0 | int avail; |
1921 | 0 | int i; |
1922 | | |
1923 | | /* PQfn already validated connection state */ |
1924 | |
|
1925 | 0 | if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ |
1926 | 0 | pqPutInt(fnid, 4, conn) < 0 || /* function id */ |
1927 | 0 | pqPutInt(1, 2, conn) < 0 || /* # of format codes */ |
1928 | 0 | pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ |
1929 | 0 | pqPutInt(nargs, 2, conn) < 0) /* # of args */ |
1930 | 0 | { |
1931 | 0 | pqHandleSendFailure(conn); |
1932 | 0 | return NULL; |
1933 | 0 | } |
1934 | | |
1935 | 0 | for (i = 0; i < nargs; ++i) |
1936 | 0 | { /* len.int4 + contents */ |
1937 | 0 | if (pqPutInt(args[i].len, 4, conn)) |
1938 | 0 | { |
1939 | 0 | pqHandleSendFailure(conn); |
1940 | 0 | return NULL; |
1941 | 0 | } |
1942 | 0 | if (args[i].len == -1) |
1943 | 0 | continue; /* it's NULL */ |
1944 | | |
1945 | 0 | if (args[i].isint) |
1946 | 0 | { |
1947 | 0 | if (pqPutInt(args[i].u.integer, args[i].len, conn)) |
1948 | 0 | { |
1949 | 0 | pqHandleSendFailure(conn); |
1950 | 0 | return NULL; |
1951 | 0 | } |
1952 | 0 | } |
1953 | 0 | else |
1954 | 0 | { |
1955 | 0 | if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) |
1956 | 0 | { |
1957 | 0 | pqHandleSendFailure(conn); |
1958 | 0 | return NULL; |
1959 | 0 | } |
1960 | 0 | } |
1961 | 0 | } |
1962 | | |
1963 | 0 | if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ |
1964 | 0 | { |
1965 | 0 | pqHandleSendFailure(conn); |
1966 | 0 | return NULL; |
1967 | 0 | } |
1968 | | |
1969 | 0 | if (pqPutMsgEnd(conn) < 0 || |
1970 | 0 | pqFlush(conn)) |
1971 | 0 | { |
1972 | 0 | pqHandleSendFailure(conn); |
1973 | 0 | return NULL; |
1974 | 0 | } |
1975 | | |
1976 | 0 | for (;;) |
1977 | 0 | { |
1978 | 0 | if (needInput) |
1979 | 0 | { |
1980 | | /* Wait for some data to arrive (or for the channel to close) */ |
1981 | 0 | if (pqWait(true, false, conn) || |
1982 | 0 | pqReadData(conn) < 0) |
1983 | 0 | break; |
1984 | 0 | } |
1985 | | |
1986 | | /* |
1987 | | * Scan the message. If we run out of data, loop around to try again. |
1988 | | */ |
1989 | 0 | needInput = true; |
1990 | |
|
1991 | 0 | conn->inCursor = conn->inStart; |
1992 | 0 | if (pqGetc(&id, conn)) |
1993 | 0 | continue; |
1994 | 0 | if (pqGetInt(&msgLength, 4, conn)) |
1995 | 0 | continue; |
1996 | | |
1997 | | /* |
1998 | | * Try to validate message type/length here. A length less than 4 is |
1999 | | * definitely broken. Large lengths should only be believed for a few |
2000 | | * message types. |
2001 | | */ |
2002 | 0 | if (msgLength < 4) |
2003 | 0 | { |
2004 | 0 | handleSyncLoss(conn, id, msgLength); |
2005 | 0 | break; |
2006 | 0 | } |
2007 | 0 | if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) |
2008 | 0 | { |
2009 | 0 | handleSyncLoss(conn, id, msgLength); |
2010 | 0 | break; |
2011 | 0 | } |
2012 | | |
2013 | | /* |
2014 | | * Can't process if message body isn't all here yet. |
2015 | | */ |
2016 | 0 | msgLength -= 4; |
2017 | 0 | avail = conn->inEnd - conn->inCursor; |
2018 | 0 | if (avail < msgLength) |
2019 | 0 | { |
2020 | | /* |
2021 | | * Before looping, enlarge the input buffer if needed to hold the |
2022 | | * whole message. See notes in parseInput. |
2023 | | */ |
2024 | 0 | if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, |
2025 | 0 | conn)) |
2026 | 0 | { |
2027 | | /* |
2028 | | * XXX add some better recovery code... plan is to skip over |
2029 | | * the message using its length, then report an error. For the |
2030 | | * moment, just treat this like loss of sync (which indeed it |
2031 | | * might be!) |
2032 | | */ |
2033 | 0 | handleSyncLoss(conn, id, msgLength); |
2034 | 0 | break; |
2035 | 0 | } |
2036 | 0 | continue; |
2037 | 0 | } |
2038 | | |
2039 | | /* |
2040 | | * We should see V or E response to the command, but might get N |
2041 | | * and/or A notices first. We also need to swallow the final Z before |
2042 | | * returning. |
2043 | | */ |
2044 | 0 | switch (id) |
2045 | 0 | { |
2046 | 0 | case 'V': /* function result */ |
2047 | 0 | if (pqGetInt(actual_result_len, 4, conn)) |
2048 | 0 | continue; |
2049 | 0 | if (*actual_result_len != -1) |
2050 | 0 | { |
2051 | 0 | if (result_is_int) |
2052 | 0 | { |
2053 | 0 | if (pqGetInt(result_buf, *actual_result_len, conn)) |
2054 | 0 | continue; |
2055 | 0 | } |
2056 | 0 | else |
2057 | 0 | { |
2058 | 0 | if (pqGetnchar((char *) result_buf, |
2059 | 0 | *actual_result_len, |
2060 | 0 | conn)) |
2061 | 0 | continue; |
2062 | 0 | } |
2063 | 0 | } |
2064 | | /* correctly finished function result message */ |
2065 | 0 | status = PGRES_COMMAND_OK; |
2066 | 0 | break; |
2067 | 0 | case 'E': /* error return */ |
2068 | 0 | if (pqGetErrorNotice3(conn, true)) |
2069 | 0 | continue; |
2070 | 0 | status = PGRES_FATAL_ERROR; |
2071 | 0 | break; |
2072 | 0 | case 'A': /* notify message */ |
2073 | | /* handle notify and go back to processing return values */ |
2074 | 0 | if (getNotify(conn)) |
2075 | 0 | continue; |
2076 | 0 | break; |
2077 | 0 | case 'N': /* notice */ |
2078 | | /* handle notice and go back to processing return values */ |
2079 | 0 | if (pqGetErrorNotice3(conn, false)) |
2080 | 0 | continue; |
2081 | 0 | break; |
2082 | 0 | case 'Z': /* backend is ready for new query */ |
2083 | 0 | if (getReadyForQuery(conn)) |
2084 | 0 | continue; |
2085 | | /* consume the message and exit */ |
2086 | 0 | conn->inStart += 5 + msgLength; |
2087 | | /* if we saved a result object (probably an error), use it */ |
2088 | 0 | if (conn->result) |
2089 | 0 | return pqPrepareAsyncResult(conn); |
2090 | 0 | return PQmakeEmptyPGresult(conn, status); |
2091 | 0 | case 'S': /* parameter status */ |
2092 | 0 | if (getParameterStatus(conn)) |
2093 | 0 | continue; |
2094 | 0 | break; |
2095 | 0 | default: |
2096 | | /* The backend violates the protocol. */ |
2097 | 0 | printfPQExpBuffer(&conn->errorMessage, |
2098 | 0 | libpq_gettext("protocol error: id=0x%x\n"), |
2099 | 0 | id); |
2100 | 0 | pqSaveErrorResult(conn); |
2101 | | /* trust the specified message length as what to skip */ |
2102 | 0 | conn->inStart += 5 + msgLength; |
2103 | 0 | return pqPrepareAsyncResult(conn); |
2104 | 0 | } |
2105 | | /* Completed this message, keep going */ |
2106 | | /* trust the specified message length as what to skip */ |
2107 | 0 | conn->inStart += 5 + msgLength; |
2108 | 0 | needInput = false; |
2109 | 0 | } |
2110 | | |
2111 | | /* |
2112 | | * We fall out of the loop only upon failing to read data. |
2113 | | * conn->errorMessage has been set by pqWait or pqReadData. We want to |
2114 | | * append it to any already-received error message. |
2115 | | */ |
2116 | 0 | pqSaveErrorResult(conn); |
2117 | 0 | return pqPrepareAsyncResult(conn); |
2118 | 0 | } |
2119 | | |
2120 | | |
2121 | | /* |
2122 | | * Construct startup packet |
2123 | | * |
2124 | | * Returns a malloc'd packet buffer, or NULL if out of memory |
2125 | | */ |
2126 | | char * |
2127 | | pqBuildStartupPacket3(PGconn *conn, int *packetlen, |
2128 | | const PQEnvironmentOption *options) |
2129 | 2.79k | { |
2130 | 2.79k | char *startpacket; |
2131 | | |
2132 | 2.79k | *packetlen = build_startup_packet(conn, NULL, options); |
2133 | 2.79k | startpacket = (char *) malloc(*packetlen); |
2134 | 2.79k | if (!startpacket) |
2135 | 0 | return NULL; |
2136 | 2.79k | *packetlen = build_startup_packet(conn, startpacket, options); |
2137 | 2.79k | return startpacket; |
2138 | 2.79k | } |
2139 | | |
2140 | | /* |
2141 | | * Build a startup packet given a filled-in PGconn structure. |
2142 | | * |
2143 | | * We need to figure out how much space is needed, then fill it in. |
2144 | | * To avoid duplicate logic, this routine is called twice: the first time |
2145 | | * (with packet == NULL) just counts the space needed, the second time |
2146 | | * (with packet == allocated space) fills it in. Return value is the number |
2147 | | * of bytes used. |
2148 | | */ |
2149 | | static int |
2150 | | build_startup_packet(const PGconn *conn, char *packet, |
2151 | | const PQEnvironmentOption *options) |
2152 | 5.59k | { |
2153 | 5.59k | int packet_len = 0; |
2154 | 5.59k | const PQEnvironmentOption *next_eo; |
2155 | 5.59k | const char *val; |
2156 | | |
2157 | | /* Protocol version comes first. */ |
2158 | 5.59k | if (packet) |
2159 | 2.79k | { |
2160 | 2.79k | ProtocolVersion pv = pg_hton32(conn->pversion); |
2161 | | |
2162 | 2.79k | memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion)); |
2163 | 2.79k | } |
2164 | 5.59k | packet_len += sizeof(ProtocolVersion); |
2165 | | |
2166 | | /* Add user name, database name, options */ |
2167 | | |
2168 | 5.59k | #define ADD_STARTUP_OPTION(optname, optval) \ |
2169 | 12.2k | do { \ |
2170 | 12.2k | if (packet) \ |
2171 | 12.2k | strcpy(packet + packet_len, optname); \ |
2172 | 12.2k | packet_len += strlen(optname) + 1; \ |
2173 | 12.2k | if (packet) \ |
2174 | 12.2k | strcpy(packet + packet_len, optval); \ |
2175 | 12.2k | packet_len += strlen(optval) + 1; \ |
2176 | 12.2k | } while(0) |
2177 | | |
2178 | 5.59k | if (conn->pguser && conn->pguser[0]) |
2179 | 5.59k | ADD_STARTUP_OPTION("user", conn->pguser); |
2180 | 5.59k | if (conn->dbName && conn->dbName[0]) |
2181 | 5.59k | ADD_STARTUP_OPTION("database", conn->dbName); |
2182 | 5.59k | if (conn->replication && conn->replication[0]0 ) |
2183 | 0 | ADD_STARTUP_OPTION("replication", conn->replication); |
2184 | 5.59k | if (conn->pgoptions && conn->pgoptions[0]) |
2185 | 230 | ADD_STARTUP_OPTION("options", conn->pgoptions); |
2186 | 5.59k | if (conn->send_appname) |
2187 | 5.59k | { |
2188 | | /* Use appname if present, otherwise use fallback */ |
2189 | 5.59k | val = conn->appname ? conn->appname230 : conn->fbappname5.36k ; |
2190 | 5.59k | if (val && val[0]296 ) |
2191 | 296 | ADD_STARTUP_OPTION("application_name", val); |
2192 | 5.59k | } |
2193 | | |
2194 | 5.59k | if (conn->client_encoding_initial && conn->client_encoding_initial[0]30 ) |
2195 | 30 | ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial); |
2196 | | |
2197 | | /* Add any environment-driven GUC settings needed */ |
2198 | 22.3k | for (next_eo = options; next_eo->envName; next_eo++16.7k ) |
2199 | 16.7k | { |
2200 | 16.7k | if ((val = getenv(next_eo->envName)) != NULL) |
2201 | 460 | { |
2202 | 460 | if (pg_strcasecmp(val, "default") != 0) |
2203 | 460 | ADD_STARTUP_OPTION(next_eo->pgName, val); |
2204 | 460 | } |
2205 | 16.7k | } |
2206 | | |
2207 | | /* Add trailing terminator */ |
2208 | 5.59k | if (packet) |
2209 | 2.79k | packet[packet_len] = '\0'; |
2210 | 5.59k | packet_len++; |
2211 | | |
2212 | 5.59k | return packet_len; |
2213 | 5.59k | } |