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