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