1 /*---------
2 * Module: qresult.c
3 *
4 * Description: This module contains functions related to
5 * managing result information (i.e, fetching rows
6 * from the backend, managing the tuple cache, etc.)
7 * and retrieving it. Depending on the situation, a
8 * QResultClass will hold either data from the backend
9 * or a manually built result.
10 *
11 * Classes: QResultClass (Functions prefix: "QR_")
12 *
13 * API functions: none
14 *
15 * Comments: See "readme.txt" for copyright and license information.
16 *---------
17 */
18
19 #include "qresult.h"
20 #include "statement.h"
21
22 #include <libpq-fe.h>
23
24 #include "misc.h"
25 #include <stdio.h>
26 #include <string.h>
27 #include <limits.h>
28
29 static BOOL QR_prepare_for_tupledata(QResultClass *self);
30 static BOOL QR_read_tuples_from_pgres(QResultClass *, PGresult **pgres);
31
32 /*
33 * Used for building a Manual Result only
34 * All info functions call this function to create the manual result set.
35 */
36 void
QR_set_num_fields(QResultClass * self,int new_num_fields)37 QR_set_num_fields(QResultClass *self, int new_num_fields)
38 {
39 if (!self) return;
40 MYLOG(0, "entering\n");
41
42 CI_set_num_fields(QR_get_fields(self), new_num_fields);
43
44 MYLOG(0, "leaving\n");
45 }
46
47
48 void
QR_set_position(QResultClass * self,SQLLEN pos)49 QR_set_position(QResultClass *self, SQLLEN pos)
50 {
51 self->tupleField = self->backend_tuples + ((QR_get_rowstart_in_cache(self) + pos) * self->num_fields);
52 }
53
54
55 void
QR_set_cache_size(QResultClass * self,SQLLEN cache_size)56 QR_set_cache_size(QResultClass *self, SQLLEN cache_size)
57 {
58 self->cache_size = cache_size;
59 }
60
61
62 void
QR_set_reqsize(QResultClass * self,Int4 reqsize)63 QR_set_reqsize(QResultClass *self, Int4 reqsize)
64 {
65 self->rowset_size_include_ommitted = reqsize;
66 }
67
68 void
QR_set_cursor(QResultClass * self,const char * name)69 QR_set_cursor(QResultClass *self, const char *name)
70 {
71 ConnectionClass *conn = QR_get_conn(self);
72
73 if (self->cursor_name)
74 {
75 if (name &&
76 0 == strcmp(name, self->cursor_name))
77 return;
78 free(self->cursor_name);
79 if (conn)
80 {
81 CONNLOCK_ACQUIRE(conn);
82 conn->ncursors--;
83 CONNLOCK_RELEASE(conn);
84 }
85 self->cursTuple = -1;
86 QR_set_no_cursor(self);
87 }
88 else if (NULL == name)
89 return;
90 if (name)
91 {
92 self->cursor_name = strdup(name);
93 if (conn)
94 {
95 CONNLOCK_ACQUIRE(conn);
96 conn->ncursors++;
97 CONNLOCK_RELEASE(conn);
98 }
99 }
100 else
101 {
102 QResultClass *res;
103
104 self->cursor_name = NULL;
105 for (res = QR_nextr(self); NULL != res; res = QR_nextr(res))
106 {
107 if (NULL != res->cursor_name)
108 free(res->cursor_name);
109 res->cursor_name = NULL;
110 }
111 }
112 }
113
114
115 void
QR_set_num_cached_rows(QResultClass * self,SQLLEN num_rows)116 QR_set_num_cached_rows(QResultClass *self, SQLLEN num_rows)
117 {
118 self->num_cached_rows = num_rows;
119 if (QR_synchronize_keys(self))
120 self->num_cached_keys = self->num_cached_rows;
121 }
122
123 void
QR_set_rowstart_in_cache(QResultClass * self,SQLLEN start)124 QR_set_rowstart_in_cache(QResultClass *self, SQLLEN start)
125 {
126 if (QR_synchronize_keys(self))
127 self->key_base = start;
128 self->base = start;
129 }
130
131 void
QR_inc_rowstart_in_cache(QResultClass * self,SQLLEN base_inc)132 QR_inc_rowstart_in_cache(QResultClass *self, SQLLEN base_inc)
133 {
134 if (!QR_has_valid_base(self))
135 MYLOG(0, " called while the cache is not ready\n");
136 self->base += base_inc;
137 if (QR_synchronize_keys(self))
138 self->key_base = self->base;
139 }
140
141 void
QR_set_fields(QResultClass * self,ColumnInfoClass * fields)142 QR_set_fields(QResultClass *self, ColumnInfoClass *fields)
143 {
144 ColumnInfoClass *curfields = QR_get_fields(self);
145
146 if (curfields == fields)
147 return;
148
149 /*
150 * Unlink the old columninfo from this result set, freeing it if this
151 * was the last reference.
152 */
153 if (NULL != curfields)
154 {
155 if (curfields->refcount > 1)
156 curfields->refcount--;
157 else
158 CI_Destructor(curfields);
159 }
160 self->fields = fields;
161 if (NULL != fields)
162 fields->refcount++;
163 }
164
165 /*
166 * CLASS QResult
167 */
168 QResultClass *
QR_Constructor(void)169 QR_Constructor(void)
170 {
171 QResultClass *rv;
172
173 MYLOG(0, "entering\n");
174 rv = (QResultClass *) malloc(sizeof(QResultClass));
175
176 if (rv != NULL)
177 {
178 ColumnInfoClass *fields;
179
180 rv->rstatus = PORES_EMPTY_QUERY;
181 rv->pstatus = 0;
182
183 /* construct the column info */
184 rv->fields = NULL;
185 if (fields = CI_Constructor(), NULL == fields)
186 {
187 free(rv);
188 return NULL;
189 }
190 QR_set_fields(rv, fields);
191 rv->backend_tuples = NULL;
192 rv->sqlstate[0] = '\0';
193 rv->message = NULL;
194 rv->messageref = NULL;
195 rv->command = NULL;
196 rv->notice = NULL;
197 rv->conn = NULL;
198 QR_nextr(rv) = NULL;
199 rv->count_backend_allocated = 0;
200 rv->count_keyset_allocated = 0;
201 rv->num_total_read = 0;
202 rv->num_cached_rows = 0;
203 rv->num_cached_keys = 0;
204 rv->fetch_number = 0;
205 rv->flags = 0; /* must be cleared before calling QR_set_rowstart_in_cache() */
206 QR_set_rowstart_in_cache(rv, -1);
207 rv->key_base = -1;
208 rv->recent_processed_row_count = -1;
209 rv->cursTuple = -1;
210 rv->move_offset = 0;
211 rv->num_fields = 0;
212 rv->num_key_fields = PG_NUM_NORMAL_KEYS; /* CTID + OID */
213 rv->tupleField = NULL;
214 rv->cursor_name = NULL;
215 rv->aborted = FALSE;
216
217 rv->cache_size = 0;
218 rv->cmd_fetch_size = 0;
219 rv->rowset_size_include_ommitted = 1;
220 rv->move_direction = 0;
221 rv->keyset = NULL;
222 rv->reload_count = 0;
223 rv->rb_alloc = 0;
224 rv->rb_count = 0;
225 rv->dataFilled = FALSE;
226 rv->rollback = NULL;
227 rv->ad_alloc = 0;
228 rv->ad_count = 0;
229 rv->added_keyset = NULL;
230 rv->added_tuples = NULL;
231 rv->up_alloc = 0;
232 rv->up_count = 0;
233 rv->updated = NULL;
234 rv->updated_keyset = NULL;
235 rv->updated_tuples = NULL;
236 rv->dl_alloc = 0;
237 rv->dl_count = 0;
238 rv->deleted = NULL;
239 rv->deleted_keyset = NULL;
240 }
241
242 MYLOG(0, "leaving %p\n", rv);
243 return rv;
244 }
245
246
247 void
QR_close_result(QResultClass * self,BOOL destroy)248 QR_close_result(QResultClass *self, BOOL destroy)
249 {
250 ConnectionClass *conn;
251 QResultClass *next;
252 BOOL top = TRUE;
253
254 if (!self) return;
255 MYLOG(0, "entering\n");
256
257 while(self)
258 {
259 /*
260 * If conn is defined, then we may have used "backend_tuples", so in
261 * case we need to, free it up. Also, close the cursor.
262 */
263 if ((conn = QR_get_conn(self)) && conn->pqconn)
264 {
265 if (CC_is_in_trans(conn) || QR_is_withhold(self))
266 {
267 if (!QR_close(self)) /* close the cursor if there is one */
268 {
269 }
270 }
271 }
272
273 QR_free_memory(self); /* safe to call anyway */
274
275 /*
276 * Should have been freed in the close() but just in case...
277 * QR_set_cursor clears the cursor name of all the chained results too,
278 * so we only need to do this for the first result in the chain.
279 */
280 if (top)
281 QR_set_cursor(self, NULL);
282
283 /* Free up column info */
284 if (destroy)
285 QR_set_fields(self, NULL);
286
287 /* Free command info (this is from strdup()) */
288 if (self->command)
289 {
290 free(self->command);
291 self->command = NULL;
292 }
293
294 /* Free message info (this is from strdup()) */
295 if (self->message)
296 {
297 free(self->message);
298 self->message = NULL;
299 }
300
301 /* Free notice info (this is from strdup()) */
302 if (self->notice)
303 {
304 free(self->notice);
305 self->notice = NULL;
306 }
307 /* Destruct the result object in the chain */
308 next = QR_nextr(self);
309 QR_detach(self);
310 if (destroy)
311 free(self);
312
313 /* Repeat for the next result in the chain */
314 self = next;
315 destroy = TRUE; /* always destroy chained results */
316 top = FALSE;
317 }
318
319 MYLOG(0, "leaving\n");
320 }
321
322 void
QR_reset_for_re_execute(QResultClass * self)323 QR_reset_for_re_execute(QResultClass *self)
324 {
325 MYLOG(0, "entering for %p\n", self);
326 if (!self) return;
327 QR_close_result(self, FALSE);
328 /* reset flags etc */
329 self->flags = 0;
330 QR_set_rowstart_in_cache(self, -1);
331 self->recent_processed_row_count = -1;
332 /* clear error info etc */
333 self->rstatus = PORES_EMPTY_QUERY;
334 self->aborted = FALSE;
335 self->sqlstate[0] = '\0';
336 self->messageref = NULL;
337
338 MYLOG(0, "leaving\n");
339 }
340
341 void
QR_Destructor(QResultClass * self)342 QR_Destructor(QResultClass *self)
343 {
344 MYLOG(0, "entering\n");
345 if (!self) return;
346 QR_close_result(self, TRUE);
347
348 MYLOG(0, "leaving\n");
349 }
350
351
352 void
QR_set_command(QResultClass * self,const char * msg)353 QR_set_command(QResultClass *self, const char *msg)
354 {
355 if (self->command)
356 free(self->command);
357
358 self->command = msg ? strdup(msg) : NULL;
359 }
360
361
362 void
QR_set_message(QResultClass * self,const char * msg)363 QR_set_message(QResultClass *self, const char *msg)
364 {
365 if (self->message)
366 free(self->message);
367 self->messageref = NULL;
368
369 self->message = msg ? strdup(msg) : NULL;
370 }
371
372 void
QR_add_message(QResultClass * self,const char * msg)373 QR_add_message(QResultClass *self, const char *msg)
374 {
375 char *message = self->message;
376 size_t alsize, pos, addlen;
377
378 if (!msg || !msg[0])
379 return;
380 addlen = strlen(msg);
381 if (message)
382 {
383 pos = strlen(message) + 1;
384 alsize = pos + addlen + 1;
385 }
386 else
387 {
388 pos = 0;
389 alsize = addlen + 1;
390 }
391 if (message = realloc(message, alsize), NULL == message)
392 return;
393 if (pos > 0)
394 message[pos - 1] = ';';
395 strncpy_null(message + pos, msg, addlen + 1);
396 self->message = message;
397 }
398
399
400 void
QR_set_notice(QResultClass * self,const char * msg)401 QR_set_notice(QResultClass *self, const char *msg)
402 {
403 if (self->notice)
404 free(self->notice);
405
406 self->notice = msg ? strdup(msg) : NULL;
407 }
408
409 void
QR_add_notice(QResultClass * self,const char * msg)410 QR_add_notice(QResultClass *self, const char *msg)
411 {
412 char *message = self->notice;
413 size_t alsize, pos, addlen;
414
415 if (!msg || !msg[0])
416 return;
417 addlen = strlen(msg);
418 if (message)
419 {
420 pos = strlen(message) + 1;
421 alsize = pos + addlen + 1;
422 }
423 else
424 {
425 pos = 0;
426 alsize = addlen + 1;
427 }
428 if (message = realloc(message, alsize), NULL == message)
429 return;
430 if (pos > 0)
431 message[pos - 1] = ';';
432 strncpy_null(message + pos, msg, addlen + 1);
433 self->notice = message;
434 }
435
436
QR_AddNew(QResultClass * self)437 TupleField *QR_AddNew(QResultClass *self)
438 {
439 size_t alloc;
440 UInt4 num_fields;
441
442 if (!self) return NULL;
443 MYLOG(DETAIL_LOG_LEVEL, FORMAT_ULEN "th row(%d fields) alloc=" FORMAT_LEN "\n", self->num_cached_rows, QR_NumResultCols(self), self->count_backend_allocated);
444 if (num_fields = QR_NumResultCols(self), !num_fields) return NULL;
445 if (self->num_fields <= 0)
446 {
447 self->num_fields = num_fields;
448 QR_set_reached_eof(self);
449 }
450 alloc = self->count_backend_allocated;
451 if (!self->backend_tuples)
452 {
453 self->num_cached_rows = 0;
454 alloc = TUPLE_MALLOC_INC;
455 QR_MALLOC_return_with_error(self->backend_tuples, TupleField, alloc * sizeof(TupleField) * num_fields, self, "Out of memory in QR_AddNew.", NULL);
456 }
457 else if (self->num_cached_rows >= self->count_backend_allocated)
458 {
459 alloc = self->count_backend_allocated * 2;
460 QR_REALLOC_return_with_error(self->backend_tuples, TupleField, alloc * sizeof(TupleField) * num_fields, self, "Out of memory in QR_AddNew.", NULL);
461 }
462 self->count_backend_allocated = alloc;
463
464 if (self->backend_tuples)
465 {
466 memset(self->backend_tuples + num_fields * self->num_cached_rows, 0, num_fields * sizeof(TupleField));
467 self->num_cached_rows++;
468 self->ad_count++;
469 }
470 return self->backend_tuples + num_fields * (self->num_cached_rows - 1);
471 }
472
473 void
QR_free_memory(QResultClass * self)474 QR_free_memory(QResultClass *self)
475 {
476 SQLLEN num_backend_rows = self->num_cached_rows;
477 int num_fields = self->num_fields;
478
479 MYLOG(0, "entering fcount=" FORMAT_LEN "\n", num_backend_rows);
480
481 if (self->backend_tuples)
482 {
483 ClearCachedRows(self->backend_tuples, num_fields, num_backend_rows);
484 free(self->backend_tuples);
485 self->count_backend_allocated = 0;
486 self->backend_tuples = NULL;
487 self->dataFilled = FALSE;
488 self->tupleField = NULL;
489 }
490 if (self->keyset)
491 {
492 ConnectionClass *conn = QR_get_conn(self);
493
494 free(self->keyset);
495 self->keyset = NULL;
496 self->count_keyset_allocated = 0;
497 if (self->reload_count > 0 && conn && conn->pqconn)
498 {
499 char plannm[32];
500
501 SPRINTF_FIXED(plannm, "_KEYSET_%p", self);
502 if (CC_is_in_error_trans(conn))
503 {
504 CC_mark_a_object_to_discard(conn, 's',plannm);
505 }
506 else
507 {
508 QResultClass *res;
509 char cmd[64];
510
511 SPRINTF_FIXED(cmd, "DEALLOCATE \"%s\"", plannm);
512 res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
513 QR_Destructor(res);
514 }
515 }
516 self->reload_count = 0;
517 }
518 if (self->rollback)
519 {
520 free(self->rollback);
521 self->rb_alloc = 0;
522 self->rb_count = 0;
523 self->rollback = NULL;
524 }
525 if (self->deleted)
526 {
527 free(self->deleted);
528 self->deleted = NULL;
529 }
530 if (self->deleted_keyset)
531 {
532 free(self->deleted_keyset);
533 self->deleted_keyset = NULL;
534 }
535 self->dl_alloc = 0;
536 self->dl_count = 0;
537 /* clear added info */
538 if (self->added_keyset)
539 {
540 free(self->added_keyset);
541 self->added_keyset = NULL;
542 }
543 if (self->added_tuples)
544 {
545 ClearCachedRows(self->added_tuples, num_fields, self->ad_count);
546 free(self->added_tuples);
547 self->added_tuples = NULL;
548 }
549 self->ad_alloc = 0;
550 self->ad_count = 0;
551 /* clear updated info */
552 if (self->updated)
553 {
554 free(self->updated);
555 self->updated = NULL;
556 }
557 if (self->updated_keyset)
558 {
559 free(self->updated_keyset);
560 self->updated_keyset = NULL;
561 }
562 if (self->updated_tuples)
563 {
564 ClearCachedRows(self->updated_tuples, num_fields, self->up_count);
565 free(self->updated_tuples);
566 self->updated_tuples = NULL;
567 }
568 self->up_alloc = 0;
569 self->up_count = 0;
570
571 self->num_total_read = 0;
572 self->num_cached_rows = 0;
573 self->num_cached_keys = 0;
574 self->cursTuple = -1;
575 self->pstatus = 0;
576
577 MYLOG(0, "leaving\n");
578 }
579
580
581 BOOL
QR_from_PGresult(QResultClass * self,StatementClass * stmt,ConnectionClass * conn,const char * cursor,PGresult ** pgres)582 QR_from_PGresult(QResultClass *self, StatementClass *stmt, ConnectionClass *conn, const char *cursor, PGresult **pgres)
583 {
584 int num_io_params, num_cached_rows;
585 int i;
586 Int2 paramType;
587 IPDFields *ipdopts;
588 Int2 lf;
589 int new_num_fields;
590 OID new_adtid, new_relid = 0, new_attid = 0;
591 Int2 new_adtsize;
592 Int4 new_atttypmod = -1;
593 char *new_field_name;
594 Int2 dummy1, dummy2;
595 int cidx;
596 BOOL reached_eof_now = FALSE;
597
598 if (NULL != conn)
599 /* First, get column information */
600 QR_set_conn(self, conn);
601
602 /* at first read in the number of fields that are in the query */
603 new_num_fields = PQnfields(*pgres);
604 QLOG(0, "\tnFields: %d\n", new_num_fields);
605
606 /* according to that allocate memory */
607 QR_set_num_fields(self, new_num_fields);
608 if (NULL == QR_get_fields(self)->coli_array)
609 return FALSE;
610
611 /* now read in the descriptions */
612 for (lf = 0; lf < new_num_fields; lf++)
613 {
614 new_field_name = PQfname(*pgres, lf);
615 new_relid = PQftable(*pgres, lf);
616 new_attid = PQftablecol(*pgres, lf);
617 new_adtid = (OID) PQftype(*pgres, lf);
618 new_adtsize = (Int2) PQfsize(*pgres, lf);
619 new_atttypmod = (Int4) PQfmod(*pgres, lf);
620
621 /* Subtract the header length */
622 switch (new_adtid)
623 {
624 case PG_TYPE_DATETIME:
625 case PG_TYPE_TIMESTAMP_NO_TMZONE:
626 case PG_TYPE_TIME:
627 case PG_TYPE_TIME_WITH_TMZONE:
628 break;
629 default:
630 new_atttypmod -= 4;
631 }
632 if (new_atttypmod < 0)
633 new_atttypmod = -1;
634
635 QLOG(0, "\tfieldname='%s', adtid=%d, adtsize=%d, atttypmod=%d (rel,att)=(%d,%d)\n", new_field_name, new_adtid, new_adtsize, new_atttypmod, new_relid, new_attid);
636
637 CI_set_field_info(QR_get_fields(self), lf, new_field_name, new_adtid, new_adtsize, new_atttypmod, new_relid, new_attid);
638
639 QR_set_rstatus(self, PORES_FIELDS_OK);
640 self->num_fields = CI_get_num_fields(QR_get_fields(self));
641 if (QR_haskeyset(self))
642 self->num_fields -= self->num_key_fields;
643 if (stmt && conn)
644 {
645 num_io_params = CountParameters(stmt, NULL, &dummy1, &dummy2);
646 if (stmt->proc_return > 0 ||
647 num_io_params > 0)
648 {
649 ipdopts = SC_get_IPDF(stmt);
650 extend_iparameter_bindings(ipdopts, stmt->num_params);
651 for (i = 0, cidx = 0; i < stmt->num_params; i++)
652 {
653 if (i < stmt->proc_return)
654 ipdopts->parameters[i].paramType = SQL_PARAM_OUTPUT;
655 paramType =ipdopts->parameters[i].paramType;
656 if (SQL_PARAM_OUTPUT == paramType ||
657 SQL_PARAM_INPUT_OUTPUT == paramType)
658 {
659 MYLOG(DETAIL_LOG_LEVEL, "[%d].PGType %u->%u\n", i, PIC_get_pgtype(ipdopts->parameters[i]), CI_get_oid(QR_get_fields(self), cidx));
660 PIC_set_pgtype(ipdopts->parameters[i], CI_get_oid(QR_get_fields(self), cidx));
661 cidx++;
662 }
663 }
664 }
665 }
666 }
667
668
669 /* Then, get the data itself */
670 num_cached_rows = self->num_cached_rows;
671 if (!QR_read_tuples_from_pgres(self, pgres))
672 return FALSE;
673
674 MYLOG(DETAIL_LOG_LEVEL, "!!%p->cursTup=" FORMAT_LEN " total_read=" FORMAT_ULEN "\n", self, self->cursTuple, self->num_total_read);
675 if (!QR_once_reached_eof(self) && self->cursTuple >= (Int4) self->num_total_read)
676 self->num_total_read = self->cursTuple + 1;
677 /* EOF is 'fetched < fetch requested' */
678 if (self->num_cached_rows - num_cached_rows < self->cmd_fetch_size)
679 {
680 MYLOG(0, "detect EOF " FORMAT_ULEN " - %d < " FORMAT_ULEN "\n", self->num_cached_rows, num_cached_rows, self->cmd_fetch_size);
681 reached_eof_now = TRUE;
682 QR_set_reached_eof(self);
683 }
684 if (reached_eof_now && self->cursTuple < (Int4) self->num_total_read)
685 self->cursTuple = self->num_total_read;
686
687 if (NULL != conn)
688 {
689 /* Force a read to occur in next_tuple */
690 QR_set_next_in_cache(self, (SQLLEN) 0);
691 QR_set_rowstart_in_cache(self, 0);
692 self->key_base = 0;
693 }
694
695 /*
696 * Also fill in command tag. (Typically, it's SELECT, but can also be
697 * a FETCH.)
698 */
699 QR_set_command(self, PQcmdStatus(*pgres));
700 QR_set_cursor(self, cursor);
701 if (NULL == cursor)
702 QR_set_reached_eof(self);
703 return TRUE;
704 }
705
706
707 /*
708 * Procedure needed when closing cursors.
709 */
710 void
QR_on_close_cursor(QResultClass * self)711 QR_on_close_cursor(QResultClass *self)
712 {
713 QR_set_cursor(self, NULL);
714 }
715
716 /*
717 * Close the cursor and end the transaction (if no cursors left)
718 * We only close the cursor if other cursors are used.
719 */
720 int
QR_close(QResultClass * self)721 QR_close(QResultClass *self)
722 {
723 ConnectionClass *conn;
724 QResultClass *res;
725 int ret = TRUE;
726
727 conn = QR_get_conn(self);
728 if (self && QR_get_cursor(self))
729 {
730 if (CC_is_in_error_trans(conn))
731 {
732 if (QR_is_withhold(self))
733 CC_mark_a_object_to_discard(conn, 'p', QR_get_cursor(self));
734 }
735 else
736 {
737 BOOL does_commit = FALSE;
738 unsigned int flag = 0;
739 char buf[64];
740
741 flag = READ_ONLY_QUERY;
742 if (QR_needs_survival_check(self))
743 flag |= (ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN);
744
745 SPRINTF_FIXED(buf, "close \"%s\"", QR_get_cursor(self));
746 /* End the transaction if there are no cursors left on this conn */
747 if (CC_is_in_trans(conn) &&
748 CC_does_autocommit(conn) &&
749 CC_cursor_count(conn) <= 1)
750 {
751 MYLOG(0, "End transaction on conn=%p\n", conn);
752 if ((ROLLBACK_ON_ERROR & flag) == 0)
753 {
754 STRCAT_FIXED(buf, ";commit");
755 flag |= END_WITH_COMMIT;
756 QR_set_cursor(self, NULL);
757 }
758 else
759 does_commit = TRUE;
760 }
761
762 MYLOG(DETAIL_LOG_LEVEL, " Case I CC_send_query %s flag=%x\n", buf, flag);
763 res = CC_send_query(conn, buf, NULL, flag, NULL);
764 QR_Destructor(res);
765 if (does_commit)
766 {
767 if (!CC_commit(conn))
768 {
769 QR_set_rstatus(self, PORES_FATAL_ERROR);
770 QR_set_message(self, "Error ending transaction on autocommit.");
771 ret = FALSE;
772 }
773 }
774 }
775
776 QR_on_close_cursor(self);
777 if (!ret)
778 return ret;
779
780 #ifdef NOT_USED
781 /* End the transaction if there are no cursors left on this conn */
782 if (CC_does_autocommit(conn) && CC_cursor_count(conn) == 0)
783 {
784 MYLOG(0, "End transaction on conn=%p\n", conn);
785
786 if (!CC_commit(conn))
787 {
788 QR_set_rstatus(self, PORES_FATAL_ERROR);
789 QR_set_message(self, "Error ending transaction.");
790 ret = FALSE;
791 }
792 }
793 #endif /* NOT_USED */
794 }
795
796 return ret;
797 }
798
799 /*
800 * Allocate memory for receiving next tuple.
801 */
802 static BOOL
QR_prepare_for_tupledata(QResultClass * self)803 QR_prepare_for_tupledata(QResultClass *self)
804 {
805 BOOL haskeyset = QR_haskeyset(self);
806 SQLULEN num_total_rows = QR_get_num_total_tuples(self);
807
808 MYLOG(DETAIL_LOG_LEVEL, "entering %p->num_fields=%d\n", self, self->num_fields);
809 if (!QR_get_cursor(self))
810 {
811
812 if (self->num_fields > 0 &&
813 num_total_rows >= self->count_backend_allocated)
814 {
815 SQLLEN tuple_size = self->count_backend_allocated;
816
817 MYLOG(0, "REALLOC: old_count = " FORMAT_LEN ", size = " FORMAT_SIZE_T "\n", tuple_size, (size_t) (self->num_fields * sizeof(TupleField) * tuple_size));
818 if (tuple_size < 1)
819 tuple_size = TUPLE_MALLOC_INC;
820 else
821 tuple_size *= 2;
822 QR_REALLOC_return_with_error(self->backend_tuples, TupleField, tuple_size * self->num_fields * sizeof(TupleField), self, "Out of memory while reading tuples.", FALSE);
823 self->count_backend_allocated = tuple_size;
824 }
825 if (haskeyset &&
826 self->num_cached_keys >= self->count_keyset_allocated)
827 {
828 SQLLEN tuple_size = self->count_keyset_allocated;
829
830 if (tuple_size < 1)
831 tuple_size = TUPLE_MALLOC_INC;
832 else
833 tuple_size *= 2;
834 QR_REALLOC_return_with_error(self->keyset, KeySet, sizeof(KeySet) * tuple_size, self, "Out of mwmory while allocating keyset", FALSE);
835 memset(&self->keyset[self->count_keyset_allocated],
836 0,
837 (tuple_size - self->count_keyset_allocated) * sizeof(KeySet));
838 self->count_keyset_allocated = tuple_size;
839 }
840 }
841 return TRUE;
842 }
843
enlargeKeyCache(QResultClass * self,SQLLEN add_size,const char * message)844 static SQLLEN enlargeKeyCache(QResultClass *self, SQLLEN add_size, const char *message)
845 {
846 size_t alloc, alloc_req;
847 Int4 num_fields = self->num_fields;
848 BOOL curs = (NULL != QR_get_cursor(self));
849
850 if (add_size <= 0)
851 return self->count_keyset_allocated;
852 alloc = self->count_backend_allocated;
853 if (num_fields > 0 && ((alloc_req = (Int4)self->num_cached_rows + add_size) > alloc || !self->backend_tuples))
854 {
855 if (1 > alloc)
856 {
857 if (curs)
858 alloc = alloc_req;
859 else
860 alloc = (alloc_req > TUPLE_MALLOC_INC ? alloc_req : TUPLE_MALLOC_INC);
861 }
862 else
863 {
864 do
865 {
866 alloc *= 2;
867 } while (alloc < alloc_req);
868 }
869 self->count_backend_allocated = 0;
870 QR_REALLOC_return_with_error(self->backend_tuples, TupleField, num_fields * sizeof(TupleField) * alloc, self, message, -1);
871 self->count_backend_allocated = alloc;
872 }
873 alloc = self->count_keyset_allocated;
874 if (QR_haskeyset(self) && ((alloc_req = (Int4)self->num_cached_keys + add_size) > alloc || !self->keyset))
875 {
876 if (1 > alloc)
877 {
878 if (curs)
879 alloc = alloc_req;
880 else
881 alloc = (alloc_req > TUPLE_MALLOC_INC ? alloc_req : TUPLE_MALLOC_INC);
882 }
883 else
884 {
885 do
886 {
887 alloc *= 2;
888 } while (alloc < alloc_req);
889 }
890 self->count_keyset_allocated = 0;
891 QR_REALLOC_return_with_error(self->keyset, KeySet, sizeof(KeySet) * alloc, self, message, -1);
892 self->count_keyset_allocated = alloc;
893 }
894 return alloc;
895 }
896
QR_move_cursor_to_last(QResultClass * self,StatementClass * stmt)897 SQLLEN QR_move_cursor_to_last(QResultClass *self, StatementClass *stmt)
898 {
899 char movecmd[64];
900 QResultClass *res;
901 SQLLEN moved;
902 ConnectionClass *conn = SC_get_conn(stmt);
903
904 if (!QR_get_cursor(self))
905 return 0;
906 if (QR_once_reached_eof(self) &&
907 self->cursTuple >= self->num_total_read)
908 return 0;
909 SPRINTF_FIXED(movecmd,
910 "move all in \"%s\"", QR_get_cursor(self));
911 res = CC_send_query(conn, movecmd, NULL, READ_ONLY_QUERY, stmt);
912 if (!QR_command_maybe_successful(res))
913 {
914 QR_Destructor(res);
915 SC_set_error(stmt, STMT_EXEC_ERROR, "move error occured", __FUNCTION__);
916 return (-1);
917 }
918 moved = (-1);
919 if (sscanf(res->command, "MOVE " FORMAT_ULEN, &moved) > 0)
920 {
921 moved++;
922 self->cursTuple += moved;
923 if (!QR_once_reached_eof(self))
924 {
925 self->num_total_read = self->cursTuple;
926 QR_set_reached_eof(self);
927 }
928 }
929 QR_Destructor(res);
930
931 return moved;
932 }
933
934 /* This function is called by fetch_tuples() AND SQLFetch() */
935 int
QR_next_tuple(QResultClass * self,StatementClass * stmt)936 QR_next_tuple(QResultClass *self, StatementClass *stmt)
937 {
938 CSTR func = "QR_next_tuple";
939 int ret = TRUE;
940
941 /* Speed up access */
942 SQLLEN fetch_number = self->fetch_number, cur_fetch = 0;
943 SQLLEN num_total_rows;
944 SQLLEN num_backend_rows = self->num_cached_rows, num_rows_in;
945 Int4 num_fields = self->num_fields, fetch_size, req_size;
946 SQLLEN offset = 0, end_tuple;
947 char boundary_adjusted = FALSE;
948 TupleField *the_tuples = self->backend_tuples;
949 QResultClass *res;
950
951 /* QR_set_command() dups this string so doesn't need static */
952 char fetch[128];
953 QueryInfo qi;
954 ConnectionClass *conn;
955 ConnInfo *ci;
956 BOOL reached_eof_now = FALSE, curr_eof; /* detecting EOF is pretty important */
957
958 MYLOG(DETAIL_LOG_LEVEL, "Oh %p->fetch_number=" FORMAT_LEN "\n", self, self->fetch_number);
959 MYLOG(DETAIL_LOG_LEVEL, "in total_read=" FORMAT_ULEN " cursT=" FORMAT_LEN " currT=" FORMAT_LEN " ad=%d total=" FORMAT_ULEN " rowsetSize=%d\n", self->num_total_read, self->cursTuple, stmt->currTuple, self->ad_count, QR_get_num_total_tuples(self), self->rowset_size_include_ommitted);
960
961 num_total_rows = QR_get_num_total_tuples(self);
962 conn = QR_get_conn(self);
963 curr_eof = FALSE;
964 req_size = QR_get_reqsize(self);
965 /* Determine the optimum cache size. */
966 ci = &(conn->connInfo);
967 fetch_size = ci->drivers.fetch_max;
968 if ((Int4)req_size > fetch_size)
969 fetch_size = req_size;
970 if (QR_once_reached_eof(self) && self->cursTuple >= (Int4) QR_get_num_total_read(self))
971 curr_eof = TRUE;
972 #define return DONT_CALL_RETURN_FROM_HERE???
973 #define RETURN(code) { ret = code; goto cleanup;}
974 ENTER_CONN_CS(conn);
975 if (0 != self->move_offset)
976 {
977 char movecmd[256];
978 QResultClass *mres = NULL;
979 SQLULEN movement, moved;
980
981 movement = self->move_offset;
982 if (QR_is_moving_backward(self))
983 {
984 if (fetch_size > req_size)
985 {
986 SQLLEN incr_move = fetch_size - (req_size < 0 ? 1 : req_size);
987
988 movement += incr_move;
989 if (movement > (UInt4)(self->cursTuple + 1))
990 movement = self->cursTuple + 1;
991 }
992 else
993 self->cache_size = req_size;
994 MYLOG(DETAIL_LOG_LEVEL, "cache=" FORMAT_ULEN " rowset=%d movement=" FORMAT_ULEN "\n", self->cache_size, req_size, movement);
995 SPRINTF_FIXED(movecmd,
996 "move backward " FORMAT_ULEN " in \"%s\"",
997 movement, QR_get_cursor(self));
998 }
999 else if (QR_is_moving_forward(self))
1000 SPRINTF_FIXED(movecmd,
1001 "move " FORMAT_ULEN " in \"%s\"",
1002 movement, QR_get_cursor(self));
1003 else
1004 {
1005 SPRINTF_FIXED(movecmd,
1006 "move all in \"%s\"",
1007 QR_get_cursor(self));
1008 movement = INT_MAX;
1009 }
1010 mres = CC_send_query(conn, movecmd, NULL, READ_ONLY_QUERY, stmt);
1011 if (!QR_command_maybe_successful(mres))
1012 {
1013 QR_Destructor(mres);
1014 SC_set_error(stmt, STMT_EXEC_ERROR, "move error occured", func);
1015 RETURN(-1)
1016 }
1017 moved = movement;
1018 if (sscanf(mres->command, "MOVE " FORMAT_ULEN, &moved) > 0)
1019 {
1020 MYLOG(DETAIL_LOG_LEVEL, "moved=" FORMAT_ULEN " ? " FORMAT_ULEN "\n", moved, movement);
1021 if (moved < movement)
1022 {
1023 if (0 < moved)
1024 moved++;
1025 else if (QR_is_moving_backward(self) && self->cursTuple < 0)
1026 ;
1027 else if (QR_is_moving_not_backward(self) && curr_eof)
1028 ;
1029 else
1030 moved++;
1031 if (QR_is_moving_not_backward(self))
1032 {
1033 curr_eof = TRUE;
1034 if (!QR_once_reached_eof(self))
1035 {
1036 self->num_total_read = self->cursTuple + moved;
1037 QR_set_reached_eof(self);
1038 }
1039 }
1040 }
1041 }
1042 /* ... by the following call */
1043 QR_set_rowstart_in_cache(self, -1);
1044 if (QR_is_moving_backward(self))
1045 {
1046 self->cursTuple -= moved;
1047 offset = moved - self->move_offset;
1048 }
1049 else
1050 {
1051 self->cursTuple += moved;
1052 offset = self->move_offset - moved;
1053 }
1054 QR_Destructor(mres);
1055
1056 self->move_offset = 0;
1057 num_backend_rows = self->num_cached_rows;
1058 }
1059 else if (fetch_number < num_backend_rows)
1060 {
1061 if (!self->dataFilled) /* should never occur */
1062 {
1063 SC_set_error(stmt, STMT_EXEC_ERROR, "Hmm where are fetched data?", func);
1064 RETURN(-1)
1065 }
1066 /* return a row from cache */
1067 MYLOG(0, "fetch_number < fcount: returning tuple " FORMAT_LEN ", fcount = " FORMAT_LEN "\n", fetch_number, num_backend_rows);
1068 self->tupleField = the_tuples + (fetch_number * num_fields);
1069 MYLOG(DETAIL_LOG_LEVEL, "tupleField=%p\n", self->tupleField);
1070 /* move to next row */
1071 QR_inc_next_in_cache(self);
1072 RETURN(TRUE)
1073 }
1074 else if (QR_once_reached_eof(self))
1075 {
1076 BOOL reached_eod = FALSE;
1077
1078 if (stmt->currTuple + 1 >= num_total_rows)
1079 reached_eod = TRUE;
1080 if (reached_eod)
1081 {
1082 MYLOG(0, "next_tuple: fetch end\n");
1083 self->tupleField = NULL;
1084 /* end of tuples */
1085 RETURN(-1)
1086 }
1087 }
1088
1089 end_tuple = req_size + QR_get_rowstart_in_cache(self);
1090 /*
1091 * See if we need to fetch another group of rows. We may be being
1092 * called from send_query(), and if so, don't send another fetch,
1093 * just fall through and read the tuples.
1094 */
1095 self->tupleField = NULL;
1096
1097 if (!QR_get_cursor(self))
1098 {
1099 MYLOG(0, "ALL_ROWS: done, fcount = " FORMAT_ULEN ", fetch_number = " FORMAT_LEN "\n", QR_get_num_total_tuples(self), fetch_number);
1100 self->tupleField = NULL;
1101 QR_set_reached_eof(self);
1102 RETURN(-1) /* end of tuples */
1103 }
1104
1105 if (QR_get_rowstart_in_cache(self) >= num_backend_rows ||
1106 QR_is_moving(self))
1107 {
1108 TupleField *tuple = self->backend_tuples;
1109
1110 /* not a correction */
1111 self->cache_size = fetch_size;
1112 /* clear obsolete tuples */
1113 MYLOG(DETAIL_LOG_LEVEL, "clear obsolete " FORMAT_LEN " tuples\n", num_backend_rows);
1114 ClearCachedRows(tuple, num_fields, num_backend_rows);
1115 self->dataFilled = FALSE;
1116 QR_stop_movement(self);
1117 self->move_offset = 0;
1118 QR_set_next_in_cache(self, offset + 1);
1119 }
1120 else
1121 {
1122 /*
1123 * The rowset boundary doesn't match that of
1124 * the inner resultset. Enlarge the resultset
1125 * and fetch the rest of the rowset.
1126 */
1127 /* The next fetch size is */
1128 fetch_size = (Int4) (end_tuple - num_backend_rows);
1129 if (fetch_size <= 0)
1130 {
1131 MYLOG(0, "corrupted fetch_size end_tuple=" FORMAT_LEN " <= cached_rows=" FORMAT_LEN "\n", end_tuple, num_backend_rows);
1132 RETURN(-1)
1133 }
1134 /* and enlarge the cache size */
1135 self->cache_size += fetch_size;
1136 offset = self->fetch_number;
1137 QR_inc_next_in_cache(self);
1138 boundary_adjusted = TRUE;
1139 }
1140
1141 if (enlargeKeyCache(self, self->cache_size - num_backend_rows, "Out of memory while reading tuples") < 0)
1142 RETURN(FALSE)
1143
1144 /* Send a FETCH command to get more rows */
1145 SPRINTF_FIXED(fetch,
1146 "fetch %d in \"%s\"",
1147 fetch_size, QR_get_cursor(self));
1148
1149 MYLOG(0, "sending actual fetch (%d) query '%s'\n", fetch_size, fetch);
1150 if (!boundary_adjusted)
1151 {
1152 QR_set_num_cached_rows(self, 0);
1153 QR_set_rowstart_in_cache(self, offset);
1154 }
1155 num_rows_in = self->num_cached_rows;
1156
1157 /* don't read ahead for the next tuple (self) ! */
1158 qi.row_size = self->cache_size;
1159 qi.fetch_size = fetch_size;
1160 qi.result_in = self;
1161 qi.cursor = NULL;
1162 res = CC_send_query(conn, fetch, &qi, READ_ONLY_QUERY, stmt);
1163 if (!QR_command_maybe_successful(res))
1164 {
1165 if (!QR_get_message(self))
1166 QR_set_message(self, "Error fetching next group.");
1167 RETURN(FALSE)
1168 }
1169 cur_fetch = 0;
1170
1171 self->tupleField = NULL;
1172
1173 curr_eof = reached_eof_now = (QR_once_reached_eof(self) && self->cursTuple >= (Int4)self->num_total_read);
1174 MYLOG(DETAIL_LOG_LEVEL, "reached_eof_now=%d\n", reached_eof_now);
1175
1176 MYLOG(0, ": PGresult: fetch_total = " FORMAT_ULEN " & this_fetch = " FORMAT_ULEN "\n", self->num_total_read, self->num_cached_rows);
1177 MYLOG(0, ": PGresult: cursTuple = " FORMAT_LEN ", offset = " FORMAT_LEN "\n", self->cursTuple, offset);
1178
1179 cur_fetch = self->num_cached_rows - num_rows_in;
1180 if (!ret)
1181 RETURN(ret)
1182
1183 {
1184 SQLLEN start_idx = 0;
1185
1186 num_backend_rows = self->num_cached_rows;
1187 if (reached_eof_now)
1188 {
1189 MYLOG(0, "reached eof now\n");
1190 QR_set_reached_eof(self);
1191 if (self->ad_count > 0 && cur_fetch < fetch_size)
1192 {
1193 /* We have to append the tuples(keys) info from the added tuples(keys) here */
1194 SQLLEN add_size;
1195 TupleField *tuple, *added_tuple;
1196
1197 start_idx = CacheIdx2GIdx(offset, stmt, self) - self->num_total_read;
1198 if (curr_eof && start_idx >= 0)
1199 {
1200 add_size = self->ad_count - start_idx;
1201 if (0 == num_backend_rows)
1202 {
1203 offset = 0;
1204 QR_set_rowstart_in_cache(self, offset);
1205 QR_set_next_in_cache(self, offset);
1206 }
1207 }
1208 else
1209 {
1210 start_idx = 0;
1211 add_size = self->ad_count;
1212 }
1213 if (add_size > fetch_size - cur_fetch)
1214 add_size = fetch_size - cur_fetch;
1215 else if (add_size < 0)
1216 add_size = 0;
1217 MYLOG(DETAIL_LOG_LEVEL, "will add " FORMAT_LEN " added_tuples from " FORMAT_LEN " and select the " FORMAT_LEN "th added tuple " FORMAT_LEN "\n", add_size, start_idx, offset - num_backend_rows + start_idx, cur_fetch);
1218 if (enlargeKeyCache(self, add_size, "Out of memory while adding tuples") < 0)
1219 RETURN(FALSE)
1220 /* append the KeySet info first */
1221 memcpy(self->keyset + num_backend_rows, (void *)(self->added_keyset + start_idx), sizeof(KeySet) * add_size);
1222 /* and append the tuples info */
1223 tuple = self->backend_tuples + num_fields * num_backend_rows;
1224 memset(tuple, 0, sizeof(TupleField) * num_fields * add_size);
1225 added_tuple = self->added_tuples + num_fields * start_idx;
1226 ReplaceCachedRows(tuple, added_tuple, num_fields, add_size);
1227 self->num_cached_rows += add_size;
1228 self->num_cached_keys += add_size;
1229 num_backend_rows = self->num_cached_rows;
1230 }
1231 }
1232 if (offset < num_backend_rows)
1233 {
1234 /* set to first row */
1235 self->tupleField = self->backend_tuples + (offset * num_fields);
1236 }
1237 else
1238 {
1239 /* We are surely done here (we read 0 tuples) */
1240 MYLOG(0, " 'C': DONE (fcount == " FORMAT_LEN ")\n", num_backend_rows);
1241 ret = -1; /* end of tuples */
1242 }
1243 }
1244
1245 /*
1246 If the cursor operation was invoked inside this function,
1247 we have to set the status bits here.
1248 */
1249 if (self->keyset && (self->dl_count > 0 || self->up_count > 0))
1250 {
1251 SQLLEN i, lf;
1252 SQLLEN lidx, hidx, lkidx, hkidx;
1253 SQLLEN *deleted = self->deleted, *updated = self->updated;
1254
1255 num_backend_rows = QR_get_num_cached_tuples(self);
1256 lidx = CacheIdx2GIdx(num_rows_in, stmt, self);
1257 hidx = CacheIdx2GIdx(num_backend_rows, stmt, self);
1258 lkidx = GIdx2KResIdx(lidx, stmt, self);
1259 hkidx = GIdx2KResIdx(hidx, stmt, self);
1260 /* For simplicty, use CURS_NEEDS_REREAD bit to mark the row */
1261 for (i = lkidx; i < hkidx; i++)
1262 self->keyset[i].status |= CURS_NEEDS_REREAD;
1263 /* deleted info */
1264 for (i = 0; i < self->dl_count && hidx > deleted[i]; i++)
1265 {
1266 if (lidx <= deleted[i])
1267 {
1268 lf = GIdx2KResIdx(deleted[i], stmt, self);
1269 if (lf >= 0 && lf < self->num_cached_keys)
1270 {
1271 self->keyset[lf].status = self->deleted_keyset[i].status;
1272 /* mark the row off */
1273 self->keyset[lf].status &= (~CURS_NEEDS_REREAD);
1274 }
1275 }
1276 }
1277 for (i = self->up_count - 1; i >= 0; i--)
1278 {
1279 if (hidx > updated[i] &&
1280 lidx <= updated[i])
1281 {
1282 lf = GIdx2KResIdx(updated[i], stmt, self);
1283 /* in case the row is marked off */
1284 if (0 == (self->keyset[lf].status & CURS_NEEDS_REREAD))
1285 continue;
1286 self->keyset[lf] = self->updated_keyset[i];
1287 ReplaceCachedRows(self->backend_tuples + lf * num_fields, self->updated_tuples + i * num_fields, num_fields, 1);
1288 self->keyset[lf].status &= (~CURS_NEEDS_REREAD);
1289 }
1290 }
1291 /* reset CURS_NEEDS_REREAD bit */
1292 for (i = 0; i < num_backend_rows; i++)
1293 {
1294 self->keyset[i].status &= (~CURS_NEEDS_REREAD);
1295 /*MYLOG(DETAIL_LOG_LEVEL, "keyset[%d].status=%x\n", i, self->keyset[i].status);*/
1296 }
1297 }
1298
1299 cleanup:
1300 LEAVE_CONN_CS(conn);
1301 #undef RETURN
1302 #undef return
1303 MYLOG(DETAIL_LOG_LEVEL, "returning %d offset=" FORMAT_LEN "\n", ret, offset);
1304 return ret;
1305 }
1306
1307 /*
1308 * Read tuples from a libpq PGresult object into QResultClass.
1309 *
1310 * The result status of the passed-in PGresult should be either
1311 * PGRES_TUPLES_OK, or PGRES_SINGLE_TUPLE. If it's PGRES_SINGLE_TUPLE,
1312 * this function will call PQgetResult() to read all the available tuples.
1313 */
1314 static BOOL
QR_read_tuples_from_pgres(QResultClass * self,PGresult ** pgres)1315 QR_read_tuples_from_pgres(QResultClass *self, PGresult **pgres)
1316 {
1317 Int2 field_lf;
1318 int len;
1319 char *value;
1320 char *buffer;
1321 int ci_num_fields = QR_NumResultCols(self); /* speed up access */
1322 int num_fields = self->num_fields; /* speed up access */
1323 ColumnInfoClass *flds;
1324 int effective_cols;
1325 char tidoidbuf[32];
1326 int rowno;
1327 int nrows;
1328 int resStatus;
1329 int numTotalRows = 0;
1330
1331 /* set the current row to read the fields into */
1332 effective_cols = QR_NumPublicResultCols(self);
1333
1334 flds = QR_get_fields(self);
1335
1336 nextrow:
1337 resStatus = PQresultStatus(*pgres);
1338 switch (resStatus)
1339 {
1340 case PGRES_TUPLES_OK:
1341 QLOG(0, "\tok: - 'T' - %s\n", PQcmdStatus(*pgres));
1342 break;
1343 case PGRES_SINGLE_TUPLE:
1344 break;
1345
1346 case PGRES_NONFATAL_ERROR:
1347 case PGRES_BAD_RESPONSE:
1348 case PGRES_FATAL_ERROR:
1349 default:
1350 handle_pgres_error(self->conn, *pgres, "read_tuples", self, TRUE);
1351 QR_set_rstatus(self, PORES_FATAL_ERROR);
1352 return FALSE;
1353 }
1354
1355 nrows = PQntuples(*pgres);
1356 numTotalRows += nrows;
1357
1358 for (rowno = 0; rowno < nrows; rowno++)
1359 {
1360 TupleField *this_tuplefield;
1361 KeySet *this_keyset = NULL;
1362
1363 if (!QR_prepare_for_tupledata(self))
1364 return FALSE;
1365
1366 this_tuplefield = self->backend_tuples + (self->num_cached_rows * num_fields);
1367 if (QR_haskeyset(self))
1368 {
1369 /* this_keyset = self->keyset + self->cursTuple + 1; */
1370 this_keyset = self->keyset + self->num_cached_keys;
1371 this_keyset->status = 0;
1372 }
1373
1374 QLOG(TUPLE_LOG_LEVEL, "\t");
1375 for (field_lf = 0; field_lf < ci_num_fields; field_lf++)
1376 {
1377 BOOL isnull = FALSE;
1378
1379 isnull = PQgetisnull(*pgres, rowno, field_lf);
1380
1381 if (isnull)
1382 {
1383 this_tuplefield[field_lf].len = 0;
1384 this_tuplefield[field_lf].value = 0;
1385 QPRINTF(TUPLE_LOG_LEVEL, " (null)");
1386 continue;
1387 }
1388 else
1389 {
1390 len = PQgetlength(*pgres, rowno, field_lf);
1391 value = PQgetvalue(*pgres, rowno, field_lf);
1392 if (field_lf >= effective_cols)
1393 buffer = tidoidbuf;
1394 else
1395 {
1396 QR_MALLOC_return_with_error(buffer, char, len + 1, self, "Out of memory in allocating item buffer.", FALSE);
1397 }
1398 memcpy(buffer, value, len);
1399 buffer[len] = '\0';
1400
1401 QPRINTF(TUPLE_LOG_LEVEL, " '%s'(%d)", buffer, len);
1402
1403 if (field_lf >= effective_cols)
1404 {
1405 if (NULL == this_keyset)
1406 {
1407 char emsg[128];
1408
1409 QR_set_rstatus(self, PORES_INTERNAL_ERROR);
1410 SPRINTF_FIXED(emsg, "Internal Error -- this_keyset == NULL ci_num_fields=%d effective_cols=%d", ci_num_fields, effective_cols);
1411 QR_set_message(self, emsg);
1412 return FALSE;
1413 }
1414 if (field_lf == effective_cols)
1415 sscanf(buffer, "(%u,%hu)",
1416 &this_keyset->blocknum, &this_keyset->offset);
1417 else
1418 this_keyset->oid = strtoul(buffer, NULL, 10);
1419 }
1420 else
1421 {
1422 this_tuplefield[field_lf].len = len;
1423 this_tuplefield[field_lf].value = buffer;
1424
1425 /*
1426 * This can be used to set the longest length of the column
1427 * for any row in the tuple cache. It would not be accurate
1428 * for varchar and text fields to use this since a tuple cache
1429 * is only 100 rows. Bpchar can be handled since the strlen of
1430 * all rows is fixed, assuming there are not 100 nulls in a
1431 * row!
1432 */
1433
1434 if (flds && flds->coli_array && CI_get_display_size(flds, field_lf) < len)
1435 CI_get_display_size(flds, field_lf) = len;
1436 }
1437 }
1438 }
1439 QPRINTF(TUPLE_LOG_LEVEL, "\n");
1440 self->cursTuple++;
1441 if (self->num_fields > 0)
1442 {
1443 QR_inc_num_cache(self);
1444 }
1445 else if (QR_haskeyset(self))
1446 self->num_cached_keys++;
1447
1448 if (self->cursTuple >= self->num_total_read)
1449 self->num_total_read = self->cursTuple + 1;
1450 }
1451
1452 if (resStatus == PGRES_SINGLE_TUPLE)
1453 {
1454 /* Process next row */
1455 PQclear(*pgres);
1456
1457 *pgres = PQgetResult(self->conn->pqconn);
1458 goto nextrow;
1459 }
1460
1461 self->dataFilled = TRUE;
1462 self->tupleField = self->backend_tuples + (self->fetch_number * self->num_fields);
1463 MYLOG(DETAIL_LOG_LEVEL, "tupleField=%p\n", self->tupleField);
1464
1465 QR_set_rstatus(self, PORES_TUPLES_OK);
1466
1467 return TRUE;
1468 }
1469
1470 int
QR_search_by_fieldname(const QResultClass * self,const char * name)1471 QR_search_by_fieldname(const QResultClass *self, const char *name)
1472 {
1473 int i;
1474 char *col_name;
1475
1476 for (i = 0; i < QR_NumResultCols(self); i++)
1477 {
1478 col_name = QR_get_fieldname(self, i);
1479 if (strcmp(col_name, name) == 0)
1480 return i;
1481 }
1482
1483 return -1;
1484 }
1485