Merge commit 'origin/master'
[versaplex.git] / vxodbc / results.cc
blobb44006e0c2c009752153e275902242d1f0c37aac
1 /*
2 * Description: This module contains functions related to
3 * retrieving result information through the ODBC API.
4 */
5 #include "psqlodbc.h"
7 #include <string.h>
8 #include "psqlodbc.h"
9 #include "dlg_specific.h"
10 #include "environ.h"
11 #include "connection.h"
12 #include "statement.h"
13 #include "bind.h"
14 #include "qresult.h"
15 #include "convert.h"
16 #include "pgtypes.h"
18 #include <stdio.h>
19 #include <limits.h>
20 #include <assert.h>
22 #include "pgapifunc.h"
23 #include <wvstring.h>
26 RETCODE SQL_API PGAPI_RowCount(HSTMT hstmt, SQLLEN FAR * pcrow)
28 CSTR func = "PGAPI_RowCount";
29 StatementClass *stmt = (StatementClass *) hstmt;
30 QResultClass *res;
31 ConnInfo *ci;
33 mylog("%s: entering...\n", func);
34 if (!stmt)
36 SC_log_error(func, NULL_STRING, NULL);
37 return SQL_INVALID_HANDLE;
39 ci = &(SC_get_conn(stmt)->connInfo);
40 if (stmt->proc_return > 0)
42 if (pcrow)
44 *pcrow = 0;
45 inolog("returning RowCount=%d\n", *pcrow);
47 return SQL_SUCCESS;
50 res = SC_get_Curres(stmt);
51 if (res && pcrow)
53 if (stmt->status != STMT_FINISHED)
55 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
56 "Can't get row count while statement is still executing.",
57 func);
58 return SQL_ERROR;
60 if (res->recent_processed_row_count >= 0)
62 *pcrow = res->recent_processed_row_count;
63 mylog("**** %s: THE ROWS: *pcrow = %d\n", func, *pcrow);
65 return SQL_SUCCESS;
66 } else if (QR_NumResultCols(res) > 0)
68 *pcrow =
69 QR_get_num_total_tuples(res) - res->dl_count;
70 mylog("RowCount=%d\n", *pcrow);
71 return SQL_SUCCESS;
75 *pcrow = -1;
76 return SQL_SUCCESS;
79 static BOOL SC_pre_execute_ok(StatementClass * stmt, BOOL build_fi,
80 int col_idx, const char *func)
82 Int2 num_fields = SC_pre_execute(stmt);
83 QResultClass *result = SC_get_Curres(stmt);
84 BOOL exec_ok = TRUE;
86 mylog("%s: result = %p, status = %d, numcols = %d\n", func, result,
87 stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
88 /****if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) ****/
89 if (!QR_command_maybe_successful(result) || num_fields < 0)
91 /* no query has been executed on this statement */
92 WvString e("Statement error (PORES) #%s, num_fields=%s",
93 result->rstatus, num_fields);
94 SC_set_error(stmt, STMT_EXEC_ERROR, e, func);
95 exec_ok = FALSE;
96 } else if (col_idx >= 0 && col_idx < num_fields)
98 OID reloid = QR_get_relid(result, col_idx);
99 IRDFields *irdflds = SC_get_IRDF(stmt);
100 FIELD_INFO *fi;
101 TABLE_INFO *ti = NULL;
103 inolog("build_fi=%d reloid=%u\n", build_fi, reloid);
104 if (build_fi && 0 != QR_get_attid(result, col_idx))
105 getCOLIfromTI(func, NULL, stmt, reloid, &ti);
106 inolog("nfields=%d\n", irdflds->nfields);
107 if (irdflds->fi && col_idx < (int) irdflds->nfields)
109 fi = irdflds->fi[col_idx];
110 if (fi)
112 if (ti)
114 if (NULL == fi->ti)
115 fi->ti = ti;
116 if (!FI_is_applicable(fi)
117 && 0 != (ti->flags & TI_COLATTRIBUTE))
118 fi->flag |= FIELD_COL_ATTRIBUTE;
120 fi->basetype = QR_get_field_type(result, col_idx);
121 if (0 == fi->columntype)
122 fi->columntype = fi->basetype;
126 return exec_ok;
130 * This returns the number of columns associated with the database
131 * attached to "hstmt".
133 RETCODE SQL_API
134 PGAPI_NumResultCols(HSTMT hstmt, SQLSMALLINT FAR * pccol)
136 CSTR func = "PGAPI_NumResultCols";
137 StatementClass *stmt = (StatementClass *) hstmt;
138 QResultClass *result;
139 char parse_ok;
140 ConnInfo *ci;
141 RETCODE ret = SQL_SUCCESS;
143 mylog("%s: entering...\n", func);
144 if (!stmt)
146 SC_log_error(func, NULL_STRING, NULL);
147 return SQL_INVALID_HANDLE;
149 ci = &(SC_get_conn(stmt)->connInfo);
151 SC_clear_error(stmt);
152 #define return DONT_CALL_RETURN_FROM_HERE???
153 /* StartRollbackState(stmt); */
155 if (stmt->proc_return > 0)
157 *pccol = 0;
158 goto cleanup;
160 parse_ok = FALSE;
161 if (!stmt->catalog_result && SC_is_parse_forced(stmt)
162 && stmt->statement_type == STMT_TYPE_SELECT)
164 if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
166 mylog("%s: calling parse_statement on stmt=%p\n", func,
167 stmt);
168 parse_statement(stmt, FALSE);
171 if (SC_parsed_status(stmt) != STMT_PARSE_FATAL)
173 parse_ok = TRUE;
174 *pccol = SC_get_IRDF(stmt)->nfields;
175 mylog("PARSE: %s: *pccol = %d\n", func, *pccol);
179 if (!parse_ok)
181 if (!SC_pre_execute_ok(stmt, FALSE, -1, func))
183 ret = SQL_ERROR;
184 goto cleanup;
187 result = SC_get_Curres(stmt);
188 *pccol = QR_NumPublicResultCols(result);
191 cleanup:
192 #undef return
193 if (stmt->internal)
194 ret = DiscardStatementSvp(stmt, ret, FALSE);
195 return ret;
200 * Return information about the database column the user wants
201 * information about.
203 RETCODE SQL_API
204 PGAPI_DescribeCol(HSTMT hstmt,
205 SQLUSMALLINT icol,
206 SQLCHAR FAR * szColName,
207 SQLSMALLINT cbColNameMax,
208 SQLSMALLINT FAR * pcbColName,
209 SQLSMALLINT FAR * pfSqlType,
210 SQLULEN FAR * pcbColDef,
211 SQLSMALLINT FAR * pibScale,
212 SQLSMALLINT FAR * pfNullable)
214 icol--; // our own indexes are zero-based
215 StatementClass *stmt = (StatementClass *)hstmt;
216 mylog("entering (%p,%d)\n", stmt->result, icol);
217 QResultClass *res = stmt->result;
218 if (!res)
220 mylog("no result exists!\n");
221 return SQL_ERROR;
223 if (QR_NumResultCols(res) <= icol)
225 mylog("not enough columns in result!\n");
226 return SQL_ERROR;
229 strncpy((char *)szColName, QR_get_fieldname(res, icol), cbColNameMax);
230 *pcbColName = strlen((char *)szColName);
232 int ft = QR_get_field_type(res, icol);
233 mylog("ppp: ft: %d\n", ft);
234 switch (ft)
236 case PG_TYPE_BOOL:
237 *pfSqlType = SQL_BIT;
238 *pcbColDef = 1;
239 *pibScale = 0;
240 break;
241 case PG_TYPE_CHAR:
242 *pfSqlType = SQL_TINYINT;
243 *pcbColDef = 3;
244 *pibScale = 0;
245 break;
246 case PG_TYPE_INT4:
247 *pfSqlType = SQL_INTEGER;
248 *pcbColDef = pgtype_column_size(stmt, ft, icol, 10);
249 *pibScale = 0;
250 break;
251 case PG_TYPE_NUMERIC:
252 *pfSqlType = SQL_DECIMAL;
253 *pcbColDef = 19;//pgtype_column_size(stmt, ft, icol, 10);
254 *pibScale = 4;//pgtype_decimal_digits(stmt, ft, icol);
255 break;
256 case PG_TYPE_FLOAT8:
257 *pfSqlType = SQL_DOUBLE;
258 *pcbColDef = pgtype_column_size(stmt, ft, icol, 10);
259 *pibScale = pgtype_precision(stmt, ft, icol, 10);
260 break;
261 case VX_TYPE_DATETIME:
262 *pfSqlType = SQL_VARCHAR;
263 *pcbColDef = 19;
264 *pibScale = 1;
265 break;
266 default:
267 *pfSqlType = SQL_VARCHAR;
268 *pcbColDef = QR_get_fieldsize(res, icol);
269 if (*pcbColDef < 4)
270 *pcbColDef = 4;
271 *pibScale = 1;
272 break;
275 *pfNullable = 1;
277 return SQL_SUCCESS;
280 /* Returns result column descriptor information for a result set. */
281 RETCODE SQL_API
282 PGAPI_ColAttributes(HSTMT hstmt,
283 SQLUSMALLINT icol,
284 SQLUSMALLINT fDescType,
285 PTR rgbDesc,
286 SQLSMALLINT cbDescMax,
287 SQLSMALLINT FAR * pcbDesc, SQLLEN FAR * pfDesc)
289 CSTR func = "PGAPI_ColAttributes";
290 StatementClass *stmt = (StatementClass *) hstmt;
291 IRDFields *irdflds;
292 OID field_type = 0;
293 Int2 col_idx;
294 ConnectionClass *conn;
295 ConnInfo *ci;
296 int unknown_sizes;
297 int cols = 0;
298 RETCODE result;
299 const char *p = NULL;
300 SQLLEN value = 0;
301 const FIELD_INFO *fi = NULL;
302 const TABLE_INFO *ti = NULL;
303 QResultClass *res;
305 mylog("%s: entering..col=%d %d len=%d.\n", func, icol, fDescType,
306 cbDescMax);
308 if (!stmt)
310 SC_log_error(func, NULL_STRING, NULL);
311 return SQL_INVALID_HANDLE;
314 if (pcbDesc)
315 *pcbDesc = 0;
316 irdflds = SC_get_IRDF(stmt);
317 conn = SC_get_conn(stmt);
318 ci = &(conn->connInfo);
321 * Dont check for bookmark column. This is the responsibility of the
322 * driver manager. For certain types of arguments, the column number
323 * is ignored anyway, so it may be 0.
326 res = SC_get_Curres(stmt);
327 if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
329 inolog("answering bookmark info\n");
330 switch (fDescType)
332 case SQL_DESC_OCTET_LENGTH:
333 if (pfDesc)
334 *pfDesc = 4;
335 break;
336 case SQL_DESC_TYPE:
337 if (pfDesc)
338 *pfDesc =
339 stmt->options.use_bookmarks ==
340 SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
341 break;
343 return SQL_SUCCESS;
345 col_idx = icol - 1;
347 /* atoi(ci->unknown_sizes); */
348 unknown_sizes = UNKNOWNS_AS_MAX;
350 /* not appropriate for SQLColAttributes() */
351 if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
352 unknown_sizes = UNKNOWNS_AS_MAX;
354 if (!stmt->catalog_result && SC_is_parse_forced(stmt)
355 && stmt->statement_type == STMT_TYPE_SELECT)
357 if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
359 mylog("%s: calling parse_statement\n", func);
360 parse_statement(stmt, FALSE);
363 cols = irdflds->nfields;
366 * Column Count is a special case. The Column number is ignored
367 * in this case.
369 if (fDescType == SQL_DESC_COUNT)
371 if (pfDesc)
372 *pfDesc = cols;
374 return SQL_SUCCESS;
377 if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi)
379 if (col_idx >= cols)
381 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
382 "Invalid column number in ColAttributes.",
383 func);
384 return SQL_ERROR;
389 if (col_idx < irdflds->nfields && irdflds->fi)
390 fi = irdflds->fi[col_idx];
391 if (FI_is_applicable(fi))
392 field_type = FI_type(fi);
393 else
395 BOOL build_fi = FALSE;
397 fi = NULL;
398 if (PROTOCOL_74(ci))
400 switch (fDescType)
402 case SQL_COLUMN_OWNER_NAME:
403 case SQL_COLUMN_TABLE_NAME:
404 case SQL_COLUMN_TYPE:
405 case SQL_COLUMN_TYPE_NAME:
406 case SQL_COLUMN_AUTO_INCREMENT:
407 case SQL_DESC_NULLABLE:
408 case SQL_DESC_BASE_TABLE_NAME:
409 case SQL_DESC_BASE_COLUMN_NAME:
410 case SQL_COLUMN_UPDATABLE:
411 build_fi = TRUE;
412 break;
415 if (!SC_pre_execute_ok(stmt, build_fi, col_idx, func))
416 return SQL_ERROR;
418 res = SC_get_Curres(stmt);
419 cols = QR_NumPublicResultCols(res);
422 * Column Count is a special case. The Column number is ignored
423 * in this case.
425 if (fDescType == SQL_DESC_COUNT)
427 if (pfDesc)
428 *pfDesc = cols;
430 return SQL_SUCCESS;
433 if (col_idx >= cols)
435 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
436 "Invalid column number in ColAttributes.",
437 func);
438 return SQL_ERROR;
441 field_type = QR_get_field_type(res, col_idx);
442 if (col_idx < irdflds->nfields && irdflds->fi)
443 fi = irdflds->fi[col_idx];
445 if (FI_is_applicable(fi))
447 ti = fi->ti;
448 field_type = FI_type(fi);
451 mylog("colAttr: col %d field_type=%d fi,ti=%p,%p\n", col_idx,
452 field_type, fi, ti);
454 switch (fDescType)
456 case SQL_COLUMN_AUTO_INCREMENT: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
457 if (fi && fi->auto_increment)
458 value = TRUE;
459 else
460 value = pgtype_auto_increment(stmt, field_type);
461 if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
462 value = FALSE;
463 mylog("AUTO_INCREMENT=%d\n", value);
465 break;
467 case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */
468 value = pgtype_case_sensitive(stmt, field_type);
469 break;
472 * This special case is handled above.
474 * case SQL_COLUMN_COUNT:
476 case SQL_COLUMN_DISPLAY_SIZE: /* == SQL_DESC_DISPLAY_SIZE */
477 value = (fi
478 && 0 !=
479 fi->display_size) ? fi->
480 display_size : pgtype_display_size(stmt, field_type,
481 col_idx, unknown_sizes);
483 mylog("%s: col %d, display_size= %d\n", func, col_idx, value);
485 break;
487 case SQL_COLUMN_LABEL: /* == SQL_DESC_LABEL */
488 if (fi && (NAME_IS_VALID(fi->column_alias)))
490 p = GET_NAME(fi->column_alias);
492 mylog("%s: COLUMN_LABEL = '%s'\n", func, p);
493 break;
495 /* otherwise same as column name -- FALL THROUGH!!! */
497 case SQL_DESC_NAME:
498 inolog("fi=%p", fi);
499 if (fi)
500 inolog(" (%s,%s)", PRINT_NAME(fi->column_alias),
501 PRINT_NAME(fi->column_name));
502 p = fi ? (NAME_IS_NULL(fi->column_alias) ?
503 SAFE_NAME(fi->column_name) : GET_NAME(fi->
504 column_alias)) :
505 QR_get_fieldname(res, col_idx);
507 mylog("%s: COLUMN_NAME = '%s'\n", func, p);
508 break;
510 case SQL_COLUMN_LENGTH:
511 value = (fi
512 && fi->length >
513 0) ? fi->length : pgtype_buffer_length(stmt,
514 field_type,
515 col_idx,
516 unknown_sizes);
517 if (0 > value)
518 /* if (-1 == value) I'm not sure which is right */
519 value = 0;
521 mylog("%s: col %d, column_length = %d\n", func, col_idx, value);
522 break;
524 case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
525 value = pgtype_money(stmt, field_type);
526 inolog("COLUMN_MONEY=%d\n", value);
527 break;
529 case SQL_DESC_NULLABLE:
530 if (SC_has_outer_join(stmt))
531 value = TRUE;
532 else
533 value =
534 fi ? fi->nullable : pgtype_nullable(stmt, field_type);
535 inolog("COLUMN_NULLABLE=%d\n", value);
536 break;
538 case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
539 p = ti ? SAFE_NAME(ti->schema_name) : NULL_STRING;
540 mylog("schema_name=%s\n", p);
541 break;
543 case SQL_COLUMN_PRECISION: /* in 2.x */
544 value = (fi
545 && fi->column_size >
546 0) ? fi->column_size : pgtype_column_size(stmt,
547 field_type,
548 col_idx,
549 unknown_sizes);
550 if (value < 0)
551 value = 0;
553 mylog("%s: col %d, column_size = %d\n", func, col_idx, value);
554 break;
556 case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */
557 p = ti ? CurrCatString(conn) : NULL_STRING; /* empty string means *not supported* */
558 break;
560 case SQL_COLUMN_SCALE: /* in 2.x */
561 value = pgtype_decimal_digits(stmt, field_type, col_idx);
562 inolog("COLUMN_SCALE=%d\n", value);
563 if (value < 0)
564 value = 0;
565 break;
567 case SQL_COLUMN_SEARCHABLE: /* == SQL_DESC_SEARCHABLE */
568 value = pgtype_searchable(stmt, field_type);
569 break;
571 case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */
572 p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
574 mylog("%s: TABLE_NAME = '%s'\n", func, p);
575 break;
577 case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
578 value = pgtype_to_concise_type(stmt, field_type, col_idx);
579 mylog("COLUMN_TYPE=%d\n", value);
580 break;
582 case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
583 p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment);
584 break;
586 case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */
587 value = pgtype_unsigned(stmt, field_type);
588 if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
589 value = SQL_TRUE;
591 break;
593 case SQL_COLUMN_UPDATABLE: /* == SQL_DESC_UPDATABLE */
596 * Neither Access or Borland care about this.
598 * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
599 * else
601 value =
602 fi ? (fi->
603 updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY)
604 : (QR_get_attid(res, col_idx) >
605 0 ? SQL_ATTR_WRITE : (PROTOCOL_74(ci) ? SQL_ATTR_READONLY
606 : SQL_ATTR_READWRITE_UNKNOWN));
607 if (SQL_ATTR_READONLY != value)
609 const char *name =
610 fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res,
611 col_idx);
612 if (stricmp(name, OID_NAME) == 0
613 || stricmp(name, "ctid") == 0
614 || stricmp(name, "xmin") == 0)
615 value = SQL_ATTR_READONLY;
616 else if (conn->ms_jet && fi && fi->auto_increment)
617 value = SQL_ATTR_READONLY;
620 mylog("%s: UPDATEABLE = %d\n", func, value);
621 break;
622 case SQL_DESC_BASE_COLUMN_NAME:
624 p = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res,
625 col_idx);
627 mylog("%s: BASE_COLUMN_NAME = '%s'\n", func, p);
628 break;
629 case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */
630 p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
632 mylog("%s: BASE_TABLE_NAME = '%s'\n", func, p);
633 break;
634 case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */
635 value = (fi
636 && fi->length >
637 0) ? fi->length : pgtype_desclength(stmt, field_type,
638 col_idx,
639 unknown_sizes);
640 if (-1 == value)
641 value = 0;
643 mylog("%s: col %d, desc_length = %d\n", func, col_idx, value);
644 break;
645 case SQL_DESC_OCTET_LENGTH:
646 value = (fi
647 && fi->length >
648 0) ? fi->length : pgtype_transfer_octet_length(stmt,
649 field_type,
650 col_idx,
651 unknown_sizes);
652 if (-1 == value)
653 value = 0;
654 mylog("%s: col %d, octet_length = %d\n", func, col_idx, value);
655 break;
656 case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */
657 if (value = FI_precision(fi), value <= 0)
658 value =
659 pgtype_precision(stmt, field_type, col_idx,
660 unknown_sizes);
661 if (value < 0)
662 value = 0;
664 mylog("%s: col %d, desc_precision = %d\n", func, col_idx,
665 value);
666 break;
667 case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */
668 value = pgtype_scale(stmt, field_type, col_idx);
669 if (value < 0)
670 value = 0;
671 break;
672 case SQL_DESC_LOCAL_TYPE_NAME:
673 p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment);
674 break;
675 case SQL_DESC_TYPE:
676 value = pgtype_to_sqldesctype(stmt, field_type, col_idx);
677 break;
678 case SQL_DESC_NUM_PREC_RADIX:
679 value = pgtype_radix(stmt, field_type);
680 break;
681 case SQL_DESC_LITERAL_PREFIX:
682 p = pgtype_literal_prefix(stmt, field_type);
683 break;
684 case SQL_DESC_LITERAL_SUFFIX:
685 p = pgtype_literal_suffix(stmt, field_type);
686 break;
687 case SQL_DESC_UNNAMED:
688 value = (fi && NAME_IS_NULL(fi->column_name)
689 && NAME_IS_NULL(fi->
690 column_alias)) ? SQL_UNNAMED :
691 SQL_NAMED;
692 break;
693 case 1211:
694 value = 0;
695 break;
696 case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
697 SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER,
698 "this request may be for MS SQL Server", func);
699 return SQL_ERROR;
700 default:
701 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
702 "ColAttribute for this type not implemented yet",
703 func);
704 return SQL_ERROR;
707 result = SQL_SUCCESS;
709 if (p)
710 { /* char/binary data */
711 size_t len = strlen(p);
713 if (rgbDesc)
715 strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
717 if (len >= cbDescMax)
719 result = SQL_SUCCESS_WITH_INFO;
720 SC_set_error(stmt, STMT_TRUNCATED,
721 "The buffer was too small for the rgbDesc.",
722 func);
726 if (pcbDesc)
727 *pcbDesc = (SQLSMALLINT) len;
728 } else
730 /* numeric data */
731 if (pfDesc)
732 *pfDesc = value;
735 return result;
739 /* Returns result data for a single column in the current row. */
740 RETCODE SQL_API
741 PGAPI_GetData(HSTMT hstmt,
742 SQLUSMALLINT icol,
743 SQLSMALLINT fCType,
744 PTR rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue)
746 CSTR func = "PGAPI_GetData";
747 QResultClass *res;
748 StatementClass *stmt = (StatementClass *) hstmt;
749 UInt2 num_cols;
750 SQLLEN num_rows;
751 OID field_type;
752 void *value = NULL;
753 RETCODE result = SQL_SUCCESS;
754 char get_bookmark = FALSE;
755 ConnInfo *ci;
756 SQLSMALLINT target_type;
758 mylog("%s: enter, stmt=%p icol=%d\n", func, stmt, icol);
760 if (!stmt)
762 SC_log_error(func, NULL_STRING, NULL);
763 return SQL_INVALID_HANDLE;
765 ci = &(SC_get_conn(stmt)->connInfo);
766 res = SC_get_Curres(stmt);
768 if (STMT_EXECUTING == stmt->status)
770 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
771 "Can't get data while statement is still executing.",
772 func);
773 return SQL_ERROR;
776 if (stmt->status != STMT_FINISHED)
778 SC_set_error(stmt, STMT_STATUS_ERROR,
779 "GetData can only be called after the successful execution on a SQL statement",
780 func);
781 return SQL_ERROR;
784 if (SQL_ARD_TYPE == fCType)
786 ARDFields *opts;
787 BindInfoClass *binfo = NULL;
789 opts = SC_get_ARDF(stmt);
790 if (0 == icol)
791 binfo = opts->bookmark;
792 else if (icol <= opts->allocated && opts->bindings)
793 binfo = &opts->bindings[icol - 1];
794 if (binfo)
796 target_type = binfo->returntype;
797 mylog("SQL_ARD_TYPE=%d\n", target_type);
798 } else
800 SC_set_error(stmt, STMT_STATUS_ERROR,
801 "GetData can't determine the type via ARD",
802 func);
803 return SQL_ERROR;
805 } else
806 target_type = fCType;
807 if (icol == 0)
809 if (stmt->options.use_bookmarks == SQL_UB_OFF)
811 SC_set_error(stmt, STMT_COLNUM_ERROR,
812 "Attempt to retrieve bookmark with bookmark usage disabled",
813 func);
814 return SQL_ERROR;
817 /* Make sure it is the bookmark data type */
818 switch (target_type)
820 case SQL_C_BOOKMARK:
821 case SQL_C_VARBOOKMARK:
822 break;
823 default:
824 inolog
825 ("GetData Column 0 is type %d not of type SQL_C_BOOKMARK",
826 target_type);
827 SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE,
828 "Column 0 is not of type SQL_C_BOOKMARK",
829 func);
830 return SQL_ERROR;
833 get_bookmark = TRUE;
834 } else
836 /* use zero-based column numbers */
837 icol--;
839 /* make sure the column number is valid */
840 num_cols = QR_NumPublicResultCols(res);
841 if (icol >= num_cols)
843 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
844 "Invalid column number.", func);
845 return SQL_ERROR;
849 #define return DONT_CALL_RETURN_FROM_HERE???
850 /* StartRollbackState(stmt); */
852 /* make sure we're positioned on a valid row */
853 num_rows = QR_get_num_total_tuples(res);
854 if ((stmt->currTuple < 0) || (stmt->currTuple >= num_rows))
856 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
857 "Not positioned on a valid row for GetData.",
858 func);
859 result = SQL_ERROR;
860 goto cleanup;
862 mylog(" num_rows = %d\n", num_rows);
864 if (!get_bookmark)
866 SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
867 value = QR_get_value_backend_row(res, curt, icol);
868 inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple,
869 QR_get_rowstart_in_cache(res),
870 SC_get_rowset_start(stmt));
871 mylog(" value = '%s'\n", value ? value : "(null)");
875 if (get_bookmark)
877 BOOL contents_get = FALSE;
879 if (rgbValue)
881 if (SQL_C_BOOKMARK == target_type || 4 <= cbValueMax)
883 contents_get = TRUE;
884 *((SQLULEN *) rgbValue) = SC_get_bookmark(stmt);
887 if (pcbValue)
888 *pcbValue = sizeof(SQLULEN);
890 if (contents_get)
891 result = SQL_SUCCESS;
892 else
894 SC_set_error(stmt, STMT_TRUNCATED,
895 "The buffer was too small for the GetData.",
896 func);
897 result = SQL_SUCCESS_WITH_INFO;
899 goto cleanup;
902 field_type = QR_get_field_type(res, icol);
904 mylog
905 ("**** %s: icol = %d, target_type = %d, field_type = %d, value = '%s'\n",
906 func, icol, target_type, field_type, value ? value : "(null)");
908 SC_set_current_col(stmt, icol);
910 result = copy_and_convert_field(stmt, field_type, value,
911 target_type, rgbValue, cbValueMax,
912 pcbValue, pcbValue);
914 switch (result)
916 case COPY_OK:
917 result = SQL_SUCCESS;
918 break;
920 case COPY_UNSUPPORTED_TYPE:
921 SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
922 "Received an unsupported type from Postgres.",
923 func);
924 result = SQL_ERROR;
925 break;
927 case COPY_UNSUPPORTED_CONVERSION:
928 SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
929 "Couldn't handle the necessary data type conversion.",
930 func);
931 result = SQL_ERROR;
932 break;
934 case COPY_RESULT_TRUNCATED:
935 SC_set_error(stmt, STMT_TRUNCATED,
936 "The buffer was too small for the GetData.", func);
937 result = SQL_SUCCESS_WITH_INFO;
938 break;
940 case COPY_GENERAL_ERROR: /* error msg already filled in */
941 result = SQL_ERROR;
942 break;
944 case COPY_NO_DATA_FOUND:
945 /* SC_log_error(func, "no data found", stmt); */
946 result = SQL_NO_DATA_FOUND;
947 break;
949 default:
950 SC_set_error(stmt, STMT_INTERNAL_ERROR,
951 "Unrecognized return value from copy_and_convert_field.",
952 func);
953 result = SQL_ERROR;
954 break;
957 cleanup:
958 #undef return
959 if (stmt->internal)
960 result = DiscardStatementSvp(stmt, result, FALSE);
961 return result;
966 * Returns data for bound columns in the current row ("hstmt->iCursor"),
967 * advances the cursor.
969 RETCODE SQL_API PGAPI_Fetch(HSTMT hstmt)
971 CSTR func = "PGAPI_Fetch";
972 StatementClass *stmt = (StatementClass *) hstmt;
973 ARDFields *opts;
974 QResultClass *res;
975 BindInfoClass *bookmark;
976 RETCODE retval = SQL_SUCCESS;
978 mylog("%s: stmt = %p, stmt->result= %p\n", func, stmt,
979 stmt ? SC_get_Curres(stmt) : NULL);
981 if (!stmt)
983 SC_log_error(func, NULL_STRING, NULL);
984 return SQL_INVALID_HANDLE;
987 SC_clear_error(stmt);
989 if (!(res = SC_get_Curres(stmt)))
991 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
992 "Null statement result in PGAPI_Fetch.", func);
993 return SQL_ERROR;
996 /* Not allowed to bind a bookmark column when using SQLFetch. */
997 opts = SC_get_ARDF(stmt);
998 if ((bookmark = opts->bookmark) && bookmark->buffer)
1000 SC_set_error(stmt, STMT_COLNUM_ERROR,
1001 "Not allowed to bind a bookmark column when using PGAPI_Fetch",
1002 func);
1003 return SQL_ERROR;
1006 if (stmt->status == STMT_EXECUTING)
1008 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
1009 "Can't fetch while statement is still executing.",
1010 func);
1011 return SQL_ERROR;
1014 if (stmt->status != STMT_FINISHED)
1016 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
1017 "Fetch can only be called after the successful execution on a SQL statement",
1018 func);
1019 return SQL_ERROR;
1022 if (opts->bindings == NULL)
1024 if (stmt->statement_type != STMT_TYPE_SELECT)
1025 return SQL_NO_DATA_FOUND;
1026 /* just to avoid a crash if the user insists on calling this */
1027 /* function even if SQL_ExecDirect has reported an Error */
1028 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1029 "Bindings were not allocated properly.", func);
1030 return SQL_ERROR;
1032 #define return DONT_CALL_RETURN_FROM_HERE???
1033 /* StartRollbackState(stmt); */
1034 if (stmt->rowset_start < 0)
1035 SC_set_rowset_start(stmt, 0, TRUE);
1036 QR_set_rowset_size(res, 1);
1037 /* QR_inc_rowstart_in_cache(res, stmt->last_fetch_count_include_ommitted); */
1038 SC_inc_rowset_start(stmt, stmt->last_fetch_count_include_ommitted);
1040 retval = SC_fetch(stmt);
1041 #undef return
1042 if (stmt->internal)
1043 retval = DiscardStatementSvp(stmt, retval, FALSE);
1044 return retval;
1047 static RETCODE SQL_API
1048 SC_pos_reload_needed(StatementClass * stmt, SQLULEN req_size,
1049 UDWORD flag);
1050 SQLLEN getNthValid(const QResultClass * res, SQLLEN sta,
1051 UWORD orientation, SQLULEN nth, SQLLEN * nearest)
1053 SQLLEN i, num_tuples = QR_get_num_total_tuples(res), nearp;
1054 SQLULEN count;
1055 KeySet *keyset;
1057 if (!QR_once_reached_eof(res))
1058 num_tuples = INT_MAX;
1059 /* Note that the parameter nth is 1-based */
1060 inolog("get %dth Valid data from %d to %s [dlt=%d]", nth, sta,
1061 orientation == SQL_FETCH_PRIOR ? "backward" : "forward",
1062 res->dl_count);
1063 if (0 == res->dl_count)
1065 if (SQL_FETCH_PRIOR == orientation)
1067 if (sta + 1 >= (SQLLEN) nth)
1069 *nearest = sta + 1 - nth;
1070 return nth;
1072 *nearest = -1;
1073 return -(SQLLEN) (sta + 1);
1074 } else
1076 nearp = sta - 1 + nth;
1077 if (nearp < num_tuples)
1079 *nearest = nearp;
1080 return nth;
1082 *nearest = num_tuples;
1083 return -(SQLLEN) (num_tuples - sta);
1086 count = 0;
1087 if (QR_get_cursor(res))
1089 SQLLEN *deleted = (SQLLEN *)res->deleted;
1091 *nearest = sta - 1 + nth;
1092 if (SQL_FETCH_PRIOR == orientation)
1094 for (i = res->dl_count - 1;
1095 i >= 0 && *nearest <= (SQLLEN) deleted[i]; i--)
1097 inolog("deleted[%d]=%d\n", i, deleted[i]);
1098 if (sta >= (SQLLEN) deleted[i])
1099 (*nearest)--;
1101 inolog("nearest=%d\n", *nearest);
1102 if (*nearest < 0)
1104 *nearest = -1;
1105 count = sta + 1;
1106 } else
1107 return nth;
1108 } else
1110 if (!QR_once_reached_eof(res))
1111 num_tuples = INT_MAX;
1112 for (i = 0;
1113 i < res->dl_count && *nearest >= (SQLLEN) deleted[i];
1114 i++)
1116 if (sta <= (SQLLEN) deleted[i])
1117 (*nearest)++;
1119 if (*nearest >= num_tuples)
1121 *nearest = num_tuples;
1122 count = *nearest - sta;
1123 } else
1124 return nth;
1126 } else if (SQL_FETCH_PRIOR == orientation)
1128 for (i = sta, keyset = res->keyset + sta; i >= 0; i--, keyset--)
1130 if (0 ==
1131 (keyset->
1132 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
1133 CURS_OTHER_DELETED)))
1135 *nearest = i;
1136 inolog(" nearest=%d\n", *nearest);
1137 if (++count == nth)
1138 return count;
1141 *nearest = -1;
1142 } else
1144 for (i = sta, keyset = res->keyset + sta;
1145 i < num_tuples; i++, keyset++)
1147 if (0 ==
1148 (keyset->
1149 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
1150 CURS_OTHER_DELETED)))
1152 *nearest = i;
1153 inolog(" nearest=%d\n", *nearest);
1154 if (++count == nth)
1155 return count;
1158 *nearest = num_tuples;
1160 inolog(" nearest not found\n");
1161 return -(SQLLEN) count;
1164 static void
1165 move_cursor_position_if_needed(StatementClass * self,
1166 QResultClass * res)
1168 SQLLEN move_offset;
1171 * The move direction must be initialized to is_not_moving or
1172 * is_moving_from_the_last in advance.
1174 if (!QR_get_cursor(res))
1176 QR_stop_movement(res); /* for safety */
1177 res->move_offset = 0;
1178 return;
1180 inolog("BASE=%d numb=%d curr=%d cursT=%d\n",
1181 QR_get_rowstart_in_cache(res), res->num_cached_rows,
1182 self->currTuple, res->cursTuple);
1184 /* retrieve "move from the last" case first */
1185 if (QR_is_moving_from_the_last(res))
1187 mylog("must MOVE from the last\n");
1188 if (QR_once_reached_eof(res) || self->rowset_start <= QR_get_num_total_tuples(res)) /* this shouldn't happen */
1189 mylog("strange situation in move from the last\n");
1190 if (0 == res->move_offset)
1191 res->move_offset = INT_MAX - self->rowset_start;
1192 else
1194 inolog("!!move_offset=%d calc=%d\n", res->move_offset,
1195 INT_MAX - self->rowset_start);
1197 return;
1200 /* normal case */
1201 res->move_offset = 0;
1202 move_offset = self->currTuple - res->cursTuple;
1203 if (QR_get_rowstart_in_cache(res) >= 0 &&
1204 QR_get_rowstart_in_cache(res) <= res->num_cached_rows)
1206 QR_set_next_in_cache(res,
1207 (QR_get_rowstart_in_cache(res) <
1208 0) ? 0 : QR_get_rowstart_in_cache(res));
1209 return;
1211 if (0 == move_offset)
1212 return;
1213 if (move_offset > 0)
1215 QR_set_move_forward(res);
1216 res->move_offset = move_offset;
1217 } else
1219 QR_set_move_backward(res);
1220 res->move_offset = -move_offset;
1225 * return NO_DATA_FOUND macros
1226 * save_rowset_start or num_tuples must be defined
1228 #define EXTFETCH_RETURN_BOF(stmt, res) \
1230 inolog("RETURN_BOF\n"); \
1231 SC_set_rowset_start(stmt, -1, TRUE); \
1232 stmt->currTuple = -1; \
1233 /* move_cursor_position_if_needed(stmt, res); */ \
1234 return SQL_NO_DATA_FOUND; \
1236 #define EXTFETCH_RETURN_EOF(stmt, res) \
1238 inolog("RETURN_EOF\n"); \
1239 SC_set_rowset_start(stmt, num_tuples, TRUE); \
1240 stmt->currTuple = -1; \
1241 /* move_cursor_position_if_needed(stmt, res); */ \
1242 return SQL_NO_DATA_FOUND; \
1245 /* This fetchs a block of data (rowset). */
1246 RETCODE SQL_API
1247 PGAPI_ExtendedFetch(HSTMT hstmt,
1248 SQLUSMALLINT fFetchType,
1249 SQLLEN irow,
1250 SQLULEN FAR * pcrow,
1251 SQLUSMALLINT FAR * rgfRowStatus,
1252 SQLLEN bookmark_offset, SQLLEN rowsetSize)
1254 CSTR func = "PGAPI_ExtendedFetch";
1255 StatementClass *stmt = (StatementClass *) hstmt;
1256 ARDFields *opts;
1257 QResultClass *res;
1258 BindInfoClass *bookmark;
1259 SQLLEN num_tuples, i, fc_io;
1260 SQLLEN save_rowset_size, progress_size;
1261 SQLLEN save_rowset_start, rowset_start;
1262 RETCODE result = SQL_SUCCESS;
1263 char truncated, error, should_set_rowset_start = FALSE;
1264 ConnInfo *ci;
1265 SQLLEN currp;
1266 UWORD pstatus;
1267 BOOL currp_is_valid, reached_eof;
1269 mylog("stmt=%p rowsetSize=%d\n", stmt, rowsetSize);
1271 if (!stmt)
1273 SC_log_error(func, NULL_STRING, NULL);
1274 return SQL_INVALID_HANDLE;
1276 ci = &(SC_get_conn(stmt)->connInfo);
1278 /* if (SC_is_fetchcursor(stmt) && !stmt->manual_result) */
1279 if (SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type)
1281 if (fFetchType != SQL_FETCH_NEXT)
1283 SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE,
1284 "The fetch type for PGAPI_ExtendedFetch isn't allowed with ForwardOnly cursor.",
1285 func);
1286 return SQL_ERROR;
1290 SC_clear_error(stmt);
1292 if (!(res = SC_get_Curres(stmt)))
1294 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1295 "Null statement result in PGAPI_ExtendedFetch.",
1296 func);
1297 return SQL_ERROR;
1300 opts = SC_get_ARDF(stmt);
1302 * If a bookmark colunmn is bound but bookmark usage is off, then
1303 * error
1305 if ((bookmark = opts->bookmark) && bookmark->buffer
1306 && stmt->options.use_bookmarks == SQL_UB_OFF)
1308 SC_set_error(stmt, STMT_COLNUM_ERROR,
1309 "Attempt to retrieve bookmark with bookmark usage disabled",
1310 func);
1311 return SQL_ERROR;
1314 if (stmt->status == STMT_EXECUTING)
1316 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
1317 "Can't fetch while statement is still executing.",
1318 func);
1319 return SQL_ERROR;
1322 if (stmt->status != STMT_FINISHED)
1324 SC_set_error(stmt, STMT_STATUS_ERROR,
1325 "ExtendedFetch can only be called after the successful execution on a SQL statement",
1326 func);
1327 return SQL_ERROR;
1330 if (opts->bindings == NULL)
1332 if (stmt->statement_type != STMT_TYPE_SELECT)
1333 return SQL_NO_DATA_FOUND;
1334 /* just to avoid a crash if the user insists on calling this */
1335 /* function even if SQL_ExecDirect has reported an Error */
1336 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1337 "Bindings were not allocated properly.", func);
1338 return SQL_ERROR;
1341 /* Initialize to no rows fetched */
1342 if (rgfRowStatus)
1343 for (i = 0; i < rowsetSize; i++)
1344 *(rgfRowStatus + i) = SQL_ROW_NOROW;
1346 if (pcrow)
1347 *pcrow = 0;
1349 num_tuples = QR_get_num_total_tuples(res);
1350 reached_eof = QR_once_reached_eof(res) && QR_get_cursor(res);
1352 inolog("num_tuples=%d\n", num_tuples);
1353 /* Save and discard the saved rowset size */
1354 save_rowset_start = SC_get_rowset_start(stmt);
1355 save_rowset_size = stmt->save_rowset_size;
1356 stmt->save_rowset_size = -1;
1357 rowset_start = SC_get_rowset_start(stmt);
1359 QR_stop_movement(res);
1360 res->move_offset = 0;
1361 switch (fFetchType)
1363 case SQL_FETCH_NEXT:
1366 * From the odbc spec... If positioned before the start of the
1367 * RESULT SET, then this should be equivalent to
1368 * SQL_FETCH_FIRST.
1371 progress_size =
1372 (save_rowset_size > 0 ? save_rowset_size : rowsetSize);
1373 if (rowset_start < 0)
1374 SC_set_rowset_start(stmt, 0, TRUE);
1375 else if (res->keyset)
1377 if (stmt->last_fetch_count <= progress_size)
1379 SC_inc_rowset_start(stmt,
1380 stmt->
1381 last_fetch_count_include_ommitted);
1382 progress_size -= stmt->last_fetch_count;
1384 if (progress_size > 0)
1386 if (getNthValid(res, SC_get_rowset_start(stmt),
1387 SQL_FETCH_NEXT, progress_size + 1,
1388 &rowset_start) <= 0)
1390 EXTFETCH_RETURN_EOF(stmt, res)} else
1391 should_set_rowset_start = TRUE;
1393 } else
1394 SC_inc_rowset_start(stmt, progress_size);
1395 mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d, rowst=%d\n",
1396 num_tuples, stmt->currTuple, rowset_start);
1397 break;
1399 case SQL_FETCH_PRIOR:
1400 mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n",
1401 num_tuples, stmt->currTuple);
1404 * From the odbc spec... If positioned after the end of the
1405 * RESULT SET, then this should be equivalent to
1406 * SQL_FETCH_LAST.
1408 if (SC_get_rowset_start(stmt) <= 0)
1410 EXTFETCH_RETURN_BOF(stmt, res)}
1411 if (SC_get_rowset_start(stmt) >= num_tuples)
1413 if (rowsetSize > num_tuples)
1415 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1416 "fetch prior from eof and before the beginning",
1417 func);
1419 SC_set_rowset_start(stmt,
1420 num_tuples <=
1421 0 ? 0 : (num_tuples - rowsetSize),
1422 TRUE);
1423 } else if (QR_haskeyset(res))
1425 if (i =
1426 getNthValid(res, SC_get_rowset_start(stmt) - 1,
1427 SQL_FETCH_PRIOR, rowsetSize, &rowset_start),
1428 i < -1)
1430 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1431 "fetch prior and before the beggining",
1432 func);
1433 SC_set_rowset_start(stmt, 0, TRUE);
1434 } else if (i <= 0)
1436 EXTFETCH_RETURN_BOF(stmt, res)} else
1437 should_set_rowset_start = TRUE;
1438 } else if (SC_get_rowset_start(stmt) < rowsetSize)
1440 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1441 "fetch prior from eof and before the beggining",
1442 func);
1443 SC_set_rowset_start(stmt, 0, TRUE);
1444 } else
1445 SC_inc_rowset_start(stmt, -rowsetSize);
1446 break;
1448 case SQL_FETCH_FIRST:
1449 mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n",
1450 num_tuples, stmt->currTuple);
1452 SC_set_rowset_start(stmt, 0, TRUE);
1453 break;
1455 case SQL_FETCH_LAST:
1456 mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n",
1457 num_tuples, stmt->currTuple);
1459 if (!reached_eof)
1461 QR_set_move_from_the_last(res);
1462 res->move_offset = rowsetSize;
1464 SC_set_rowset_start(stmt,
1465 num_tuples <=
1466 0 ? 0 : (num_tuples - rowsetSize), TRUE);
1467 break;
1469 case SQL_FETCH_ABSOLUTE:
1470 mylog
1471 ("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n",
1472 num_tuples, stmt->currTuple, irow);
1474 /* Position before result set, but dont fetch anything */
1475 if (irow == 0)
1477 EXTFETCH_RETURN_BOF(stmt, res)}
1478 /* Position before the desired row */
1479 else if (irow > 0)
1481 if (getNthValid(res, 0, SQL_FETCH_NEXT, irow, &rowset_start)
1482 <= 0)
1484 EXTFETCH_RETURN_EOF(stmt, res)} else
1485 should_set_rowset_start = TRUE;
1487 /* Position with respect to the end of the result set */
1488 else
1490 if (getNthValid
1491 (res, num_tuples - 1, SQL_FETCH_PRIOR, -irow,
1492 &rowset_start) <= 0)
1494 EXTFETCH_RETURN_BOF(stmt, res)} else
1496 if (!reached_eof)
1498 QR_set_move_from_the_last(res);
1499 res->move_offset = -irow;
1501 should_set_rowset_start = TRUE;
1504 break;
1506 case SQL_FETCH_RELATIVE:
1509 * Refresh the current rowset -- not currently implemented,
1510 * but lie anyway
1512 if (irow == 0)
1513 break;
1515 if (irow > 0)
1517 if (getNthValid
1518 (res, SC_get_rowset_start(stmt) + 1, SQL_FETCH_NEXT,
1519 irow, &rowset_start) <= 0)
1521 EXTFETCH_RETURN_EOF(stmt, res)} else
1522 should_set_rowset_start = TRUE;
1523 } else
1525 if (getNthValid
1526 (res, SC_get_rowset_start(stmt) - 1, SQL_FETCH_PRIOR,
1527 -irow, &rowset_start) <= 0)
1529 EXTFETCH_RETURN_BOF(stmt, res)} else
1530 should_set_rowset_start = TRUE;
1532 break;
1534 case SQL_FETCH_BOOKMARK:
1536 SQLLEN bidx = SC_resolve_bookmark(irow);
1538 if (bidx < 0)
1540 if (!reached_eof)
1542 QR_set_move_from_the_last(res);
1543 res->move_offset = 1 + res->ad_count + bidx;
1545 bidx = num_tuples - 1 - res->ad_count - bidx;
1548 rowset_start = bidx;
1549 if (bookmark_offset >= 0)
1551 if (getNthValid
1552 (res, bidx, SQL_FETCH_NEXT, bookmark_offset + 1,
1553 &rowset_start) <= 0)
1555 EXTFETCH_RETURN_EOF(stmt, res)} else
1556 should_set_rowset_start = TRUE;
1557 } else
1558 if (getNthValid
1559 (res, bidx, SQL_FETCH_PRIOR, 1 - bookmark_offset,
1560 &rowset_start) <= 0)
1562 stmt->currTuple = -1;
1563 EXTFETCH_RETURN_BOF(stmt, res)} else
1564 should_set_rowset_start = TRUE;
1566 break;
1568 default:
1569 SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE,
1570 "Unsupported PGAPI_ExtendedFetch Direction", func);
1571 return SQL_ERROR;
1575 * CHECK FOR PROPER CURSOR STATE
1579 * Handle Declare Fetch style specially because the end is not really
1580 * the end...
1582 if (!should_set_rowset_start)
1583 rowset_start = SC_get_rowset_start(stmt);
1585 /* If *new* rowset is after the result_set, return no data found */
1586 if (rowset_start >= num_tuples)
1588 EXTFETCH_RETURN_EOF(stmt, res)}
1590 /* If *new* rowset is prior to result_set, return no data found */
1591 if (rowset_start < 0)
1593 if (rowset_start + rowsetSize <= 0)
1595 EXTFETCH_RETURN_BOF(stmt, res)} else
1596 { /* overlap with beginning of result set,
1597 * so get first rowset */
1598 SC_set_rowset_start(stmt, 0, TRUE);
1600 should_set_rowset_start = FALSE;
1602 #define return DONT_CALL_RETURN_FROM_HERE???
1603 /* increment the base row in the tuple cache */
1604 QR_set_rowset_size(res, (Int4) rowsetSize);
1605 /* set the rowset_start if needed */
1606 if (should_set_rowset_start)
1607 SC_set_rowset_start(stmt, rowset_start, TRUE);
1608 /* currTuple is always 1 row prior to the rowset start */
1609 stmt->currTuple = RowIdx2GIdx(-1, stmt);
1611 QR_set_rowstart_in_cache(res, SC_get_rowset_start(stmt));
1613 if (res->keyset && !QR_get_cursor(res))
1615 UDWORD flag = 0;
1616 SQLLEN rowset_end, req_size;
1618 getNthValid(res, rowset_start, SQL_FETCH_NEXT, rowsetSize,
1619 &rowset_end);
1620 req_size = rowset_end - rowset_start + 1;
1621 if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
1623 if (fFetchType != SQL_FETCH_NEXT ||
1624 QR_get_rowstart_in_cache(res) + req_size >
1625 QR_get_num_cached_tuples(res))
1626 flag = 1;
1628 if (SQL_RD_ON == stmt->options.retrieve_data || flag != 0)
1630 SC_pos_reload_needed(stmt, req_size, flag);
1633 /* Physical Row advancement occurs for each row fetched below */
1635 mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
1637 truncated = error = FALSE;
1639 currp = -1;
1640 stmt->bind_row = 0; /* set the binding location */
1641 result = SC_fetch(stmt);
1642 if (SQL_ERROR == result)
1643 goto cleanup;
1644 if (SQL_NO_DATA_FOUND != result && res->keyset)
1646 currp = GIdx2KResIdx(SC_get_rowset_start(stmt), stmt, res);
1647 inolog("currp=%d\n", currp);
1648 if (currp < 0)
1650 result = SQL_ERROR;
1651 mylog("rowset_start=%d but currp=%d\n",
1652 SC_get_rowset_start(stmt), currp);
1653 SC_set_error(stmt, STMT_INTERNAL_ERROR,
1654 "rowset_start not in the keyset", func);
1655 goto cleanup;
1658 for (i = 0, fc_io = 0;
1659 SQL_NO_DATA_FOUND != result && SQL_ERROR != result; currp++)
1661 fc_io++;
1662 currp_is_valid = FALSE;
1663 if (res->keyset)
1665 if (currp < res->num_cached_keys)
1667 currp_is_valid = TRUE;
1668 res->keyset[currp].status &= ~CURS_IN_ROWSET; /* Off the flag first */
1669 } else
1671 mylog("Umm current row is out of keyset\n");
1672 break;
1675 inolog("ExtFetch result=%d\n", result);
1676 if (currp_is_valid && SQL_SUCCESS_WITH_INFO == result
1677 && 0 == stmt->last_fetch_count)
1679 inolog("just skipping deleted row %d\n", currp);
1680 QR_set_rowset_size(res, (Int4) (rowsetSize - i + fc_io));
1681 result = SC_fetch(stmt);
1682 if (SQL_ERROR == result)
1683 break;
1684 continue;
1687 /* Determine Function status */
1688 if (result == SQL_SUCCESS_WITH_INFO)
1689 truncated = TRUE;
1690 else if (result == SQL_ERROR)
1691 error = TRUE;
1693 /* Determine Row Status */
1694 if (rgfRowStatus)
1696 if (result == SQL_ERROR)
1697 *(rgfRowStatus + i) = SQL_ROW_ERROR;
1698 else if (currp_is_valid)
1700 pstatus =
1701 (res->keyset[currp].status & KEYSET_INFO_PUBLIC);
1702 if (pstatus != 0 && pstatus != SQL_ROW_ADDED)
1704 rgfRowStatus[i] = pstatus;
1705 } else
1706 rgfRowStatus[i] = SQL_ROW_SUCCESS;
1707 /* refresh the status */
1708 /* if (SQL_ROW_DELETED != pstatus) */
1709 res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC);
1710 } else
1711 *(rgfRowStatus + i) = SQL_ROW_SUCCESS;
1713 if (SQL_ERROR != result && currp_is_valid)
1714 res->keyset[currp].status |= CURS_IN_ROWSET; /* This is the unique place where the CURS_IN_ROWSET bit is turned on */
1715 i++;
1716 if (i >= rowsetSize)
1717 break;
1718 stmt->bind_row = (SQLSETPOSIROW) i; /* set the binding location */
1719 result = SC_fetch(stmt);
1721 if (SQL_ERROR == result)
1722 goto cleanup;
1724 /* Save the fetch count for SQLSetPos */
1725 stmt->last_fetch_count = i;
1727 currp = KResIdx2GIdx(currp, stmt, res);
1728 stmt->last_fetch_count_include_ommitted = GIdx2RowIdx(currp, stmt);
1730 stmt->last_fetch_count_include_ommitted = fc_io;
1732 /* Reset next binding row */
1733 stmt->bind_row = 0;
1735 /* Move the cursor position to the first row in the result set. */
1736 stmt->currTuple = RowIdx2GIdx(0, stmt);
1738 /* Set the number of rows retrieved */
1739 if (pcrow)
1740 *pcrow = i;
1741 inolog("pcrow=%d\n", i);
1743 if (i == 0)
1744 /* Only DeclareFetch should wind up here */
1745 result = SQL_NO_DATA_FOUND;
1746 else if (error)
1747 result = SQL_ERROR;
1748 else if (truncated)
1749 result = SQL_SUCCESS_WITH_INFO;
1750 else if (SC_get_errornumber(stmt) == STMT_POS_BEFORE_RECORDSET)
1751 result = SQL_SUCCESS_WITH_INFO;
1752 else
1753 result = SQL_SUCCESS;
1755 cleanup:
1756 #undef return
1757 if (stmt->internal)
1758 result = DiscardStatementSvp(stmt, result, FALSE);
1759 return result;
1764 * This determines whether there are more results sets available for
1765 * the "hstmt".
1767 /* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
1768 RETCODE SQL_API PGAPI_MoreResults(HSTMT hstmt)
1770 CSTR func = "PGAPI_MoreResults";
1771 StatementClass *stmt = (StatementClass *) hstmt;
1772 QResultClass *res;
1773 RETCODE ret = SQL_SUCCESS;
1775 mylog("%s: entering...\n", func);
1776 if (stmt && (res = SC_get_Curres(stmt)))
1777 SC_set_Curres(stmt, res->next);
1778 if (res = SC_get_Curres(stmt), res)
1780 SQLSMALLINT num_p;
1782 if (stmt->multi_statement < 0)
1783 PGAPI_NumParams(stmt, &num_p);
1784 if (stmt->multi_statement > 0)
1786 const char *cmdstr;
1788 SC_initialize_cols_info(stmt, FALSE, TRUE);
1789 stmt->statement_type = STMT_TYPE_UNKNOWN;
1790 if (cmdstr = QR_get_command(res), NULL != cmdstr)
1791 stmt->statement_type = statement_type(cmdstr);
1792 stmt->join_info = 0;
1793 SC_clear_parse_method(stmt);
1795 stmt->diag_row_count = res->recent_processed_row_count;
1796 SC_set_rowset_start(stmt, -1, FALSE);
1797 stmt->currTuple = -1;
1798 } else
1800 PGAPI_FreeStmt(hstmt, SQL_CLOSE);
1801 ret = SQL_NO_DATA_FOUND;
1803 mylog("%s: returning %d\n", func, ret);
1804 return ret;
1809 * Stuff for updatable cursors.
1811 static Int2 getNumResultCols(const QResultClass * res)
1813 Int2 res_cols = QR_NumPublicResultCols(res);
1814 return res_cols;
1816 static OID getOid(const QResultClass * res, SQLLEN index)
1818 return res->keyset[index].oid;
1820 static void getTid(const QResultClass * res, SQLLEN index,
1821 UInt4 * blocknum, UInt2 * offset)
1823 *blocknum = res->keyset[index].blocknum;
1824 *offset = res->keyset[index].offset;
1826 static void KeySetSet(const TupleField * tuple, int num_fields,
1827 int num_key_fields, KeySet * keyset)
1829 sscanf((const char *)tuple[num_fields - num_key_fields].value, "(%u,%hu)",
1830 &keyset->blocknum, &keyset->offset);
1831 if (num_key_fields > 1)
1832 sscanf((const char *)tuple[num_fields - 1].value, "%u", &keyset->oid);
1833 else
1834 keyset->oid = 0;
1838 SQLLEN ClearCachedRows(TupleField * tuple, int num_fields,
1839 SQLLEN num_rows)
1841 SQLLEN i;
1843 for (i = 0; i < num_fields * num_rows; i++, tuple++)
1845 if (tuple->value)
1847 inolog("freeing tuple[%d][%d].value=%p\n", i / num_fields,
1848 i % num_fields, tuple->value);
1849 free(tuple->value);
1850 tuple->value = NULL;
1852 tuple->len = -1;
1854 return i;
1857 SQLLEN ReplaceCachedRows(TupleField * otuple, const TupleField * ituple,
1858 int num_fields, SQLLEN num_rows)
1860 SQLLEN i;
1862 inolog("ReplaceCachedRows %p num_fields=%d num_rows=%d\n", otuple,
1863 num_fields, num_rows);
1864 for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
1866 if (otuple->value)
1868 free(otuple->value);
1869 otuple->value = NULL;
1871 if (ituple->value)
1873 otuple->value = strdup((const char *)ituple->value);
1874 inolog("[%d,%d] %s copied\n", i / num_fields,
1875 i % num_fields, otuple->value);
1877 otuple->len = ituple->len;
1879 return i;
1882 static
1883 int MoveCachedRows(TupleField * otuple, TupleField * ituple,
1884 Int2 num_fields, SQLLEN num_rows)
1886 int i;
1888 inolog("MoveCachedRows %p num_fields=%d num_rows=%d\n", otuple,
1889 num_fields, num_rows);
1890 for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
1892 if (otuple->value)
1894 free(otuple->value);
1895 otuple->value = NULL;
1897 if (ituple->value)
1899 otuple->value = ituple->value;
1900 ituple->value = NULL;
1901 inolog("[%d,%d] %s copied\n", i / num_fields,
1902 i % num_fields, otuple->value);
1904 otuple->len = ituple->len;
1905 ituple->len = -1;
1907 return i;
1910 static BOOL tupleExists(const StatementClass * stmt,
1911 const KeySet * keyset)
1913 char selstr[256];
1914 const TABLE_INFO *ti = stmt->ti[0];
1915 QResultClass *res;
1916 RETCODE ret = FALSE;
1918 if (NAME_IS_VALID(ti->schema_name))
1919 snprintf(selstr, sizeof(selstr),
1920 "select 1 from \"%s\".\"%s\" where ctid = '(%d,%d)'",
1921 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name),
1922 keyset->blocknum, keyset->offset);
1923 else
1924 snprintf(selstr, sizeof(selstr),
1925 "select 1 from \"%s\" where ctid = '(%d,%d)'",
1926 SAFE_NAME(ti->table_name), keyset->blocknum,
1927 keyset->offset);
1928 res = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, NULL);
1929 if (QR_command_maybe_successful(res) && 1 == res->num_cached_rows)
1930 ret = TRUE;
1931 QR_Destructor(res);
1932 return ret;
1935 static BOOL enlargeAdded(QResultClass * res, UInt4 number,
1936 const StatementClass * stmt)
1938 UInt4 alloc;
1939 KeySet *added_keyset;
1940 TupleField *added_tuples;
1941 int num_fields = res->num_fields;
1943 alloc = res->ad_alloc;
1944 if (0 == alloc)
1945 alloc = number > 10 ? number : 10;
1946 else
1947 while (alloc < number)
1949 alloc *= 2;
1952 if (alloc <= res->ad_alloc)
1953 return TRUE;
1954 if (added_keyset = (KeySet *)
1955 realloc(res->added_keyset, sizeof(KeySet) * alloc),
1956 !added_keyset)
1958 res->ad_alloc = 0;
1959 return FALSE;
1961 added_tuples = res->added_tuples;
1962 if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
1963 if (added_tuples = (TupleField *)
1964 realloc(res->added_tuples,
1965 sizeof(TupleField) * num_fields * alloc),
1966 !added_tuples)
1968 if (added_keyset)
1969 free(added_keyset);
1970 added_keyset = NULL;
1972 res->added_keyset = added_keyset;
1973 res->added_tuples = added_tuples;
1974 if (!added_keyset)
1976 res->ad_alloc = 0;
1977 return FALSE;
1979 res->ad_alloc = alloc;
1980 return TRUE;
1982 static void AddAdded(StatementClass * stmt, QResultClass * res,
1983 SQLLEN index, const TupleField * tuple_added)
1985 KeySet *added_keyset, *keyset, keys;
1986 TupleField *added_tuples = NULL, *tuple;
1987 UInt4 ad_count;
1988 Int2 num_fields;
1990 if (!res)
1991 return;
1992 num_fields = res->num_fields;
1993 inolog("AddAdded index=%d, tuple=%p, num_fields=%d\n", index,
1994 tuple_added, num_fields);
1995 ad_count = res->ad_count;
1996 res->ad_count++;
1997 if (QR_get_cursor(res))
1998 index = -(SQLLEN) res->ad_count;
1999 if (!tuple_added)
2000 return;
2001 KeySetSet(tuple_added, num_fields + res->num_key_fields,
2002 res->num_key_fields, &keys);
2003 keys.status = SQL_ROW_ADDED;
2004 // VX_CLEANUP: It might be possible to extend the carnage from here.
2005 keys.status |= CURS_SELF_ADDED;
2007 if (!QR_get_cursor(res))
2008 return;
2009 if (ad_count > 0 && 0 == res->ad_alloc)
2010 return;
2011 if (!enlargeAdded(res, ad_count + 1, stmt))
2012 return;
2013 added_keyset = res->added_keyset;
2014 added_tuples = res->added_tuples;
2016 keyset = added_keyset + ad_count;
2017 *keyset = keys;
2018 if (added_tuples)
2020 tuple = added_tuples + num_fields * ad_count;
2021 memset(tuple, 0, sizeof(TupleField) * num_fields);
2022 ReplaceCachedRows(tuple, tuple_added, num_fields, 1);
2026 static void RemoveAdded(QResultClass *, SQLLEN);
2027 static void RemoveUpdated(QResultClass *, SQLLEN);
2028 static void RemoveUpdatedAfterTheKey(QResultClass *, SQLLEN,
2029 const KeySet *);
2030 static void RemoveDeleted(QResultClass *, SQLLEN);
2031 static void RemoveAdded(QResultClass * res, SQLLEN index)
2033 SQLLEN rmidx, mv_count;
2034 Int2 num_fields = res->num_fields;
2035 KeySet *added_keyset;
2036 TupleField *added_tuples;
2038 mylog("RemoveAdded index=%d\n", index);
2039 if (index < 0)
2040 rmidx = -index - 1;
2041 else
2042 rmidx = index - res->num_total_read;
2043 if (rmidx >= res->ad_count)
2044 return;
2045 added_keyset = res->added_keyset + rmidx;
2046 added_tuples = res->added_tuples + num_fields * rmidx;
2047 ClearCachedRows(added_tuples, num_fields, 1);
2048 mv_count = res->ad_count - rmidx - 1;
2049 if (mv_count > 0)
2051 memmove(added_keyset, added_keyset + 1,
2052 mv_count * sizeof(KeySet));
2053 memmove(added_tuples, added_tuples + num_fields,
2054 mv_count * num_fields * sizeof(TupleField));
2056 RemoveDeleted(res, index);
2057 RemoveUpdated(res, index);
2058 res->ad_count--;
2059 mylog("RemoveAdded removed=1 count=%d\n", res->ad_count);
2062 static void CommitAdded(QResultClass * res)
2064 KeySet *added_keyset;
2065 int i;
2066 UWORD status;
2068 mylog("CommitAdded res=%p\n", res);
2069 if (!res || !res->added_keyset)
2070 return;
2071 added_keyset = res->added_keyset;
2072 for (i = res->ad_count - 1; i >= 0; i--)
2074 status = added_keyset[i].status;
2075 if (0 != (status & CURS_SELF_ADDING))
2077 status |= CURS_SELF_ADDED;
2078 status &= ~CURS_SELF_ADDING;
2080 if (0 != (status & CURS_SELF_UPDATING))
2082 status |= CURS_SELF_UPDATED;
2083 status &= ~CURS_SELF_UPDATING;
2085 if (0 != (status & CURS_SELF_DELETING))
2087 status |= CURS_SELF_DELETED;
2088 status &= ~CURS_SELF_DELETING;
2090 if (status != added_keyset[i].status)
2092 inolog("!!Commit Added=%d(%d)\n",
2093 QR_get_num_total_read(res) + i, i);
2094 added_keyset[i].status = status;
2100 int AddDeleted(QResultClass * res, SQLULEN index, KeySet * keyset)
2102 int i;
2103 Int2 dl_count, new_alloc;
2104 SQLULEN *deleted;
2105 KeySet *deleted_keyset;
2106 UWORD status;
2107 Int2 num_fields = res->num_fields;
2109 inolog("AddDeleted %d\n", index);
2110 if (!res)
2111 return FALSE;
2112 dl_count = res->dl_count;
2113 res->dl_count++;
2114 if (!QR_get_cursor(res))
2115 return TRUE;
2116 if (!res->deleted)
2118 dl_count = 0;
2119 new_alloc = 10;
2120 QR_MALLOC_return_with_error(res->deleted, SQLULEN,
2121 sizeof(SQLULEN) * new_alloc, res,
2122 "Deleted index malloc error",
2123 FALSE);
2124 QR_MALLOC_return_with_error(res->deleted_keyset, KeySet,
2125 sizeof(KeySet) * new_alloc, res,
2126 "Deleted keyset malloc error",
2127 FALSE);
2128 deleted = res->deleted;
2129 deleted_keyset = res->deleted_keyset;
2130 res->dl_alloc = new_alloc;
2131 } else
2133 if (dl_count >= res->dl_alloc)
2135 new_alloc = res->dl_alloc * 2;
2136 res->dl_alloc = 0;
2137 QR_REALLOC_return_with_error(res->deleted, SQLULEN,
2138 sizeof(SQLULEN) * new_alloc,
2139 res,
2140 "Dleted index realloc error",
2141 FALSE);
2142 deleted = res->deleted;
2143 QR_REALLOC_return_with_error(res->deleted_keyset, KeySet,
2144 sizeof(KeySet) * new_alloc,
2145 res,
2146 "Dleted KeySet realloc error",
2147 FALSE);
2148 deleted_keyset = res->deleted_keyset;
2149 res->dl_alloc = new_alloc;
2151 /* sort deleted indexes in ascending order */
2152 for (i = 0, deleted = res->deleted, deleted_keyset =
2153 res->deleted_keyset; i < dl_count;
2154 i++, deleted++, deleted_keyset += num_fields)
2156 if (index < *deleted)
2157 break;
2159 memmove(deleted + 1, deleted, sizeof(SQLLEN) * (dl_count - i));
2160 memmove(deleted_keyset + 1, deleted_keyset,
2161 sizeof(KeySet) * (dl_count - i));
2163 *deleted = index;
2164 *deleted_keyset = *keyset;
2165 status = keyset->status;
2166 status &= (~KEYSET_INFO_PUBLIC);
2167 status |= SQL_ROW_DELETED;
2168 // VX_CLEANUP: It might be possible to extend the carnage from here.
2170 status &=
2171 ~(CURS_SELF_ADDING | CURS_SELF_UPDATING |
2172 CURS_SELF_DELETING);
2173 status |= CURS_SELF_DELETED;
2175 deleted_keyset->status = status;
2176 res->dl_count = dl_count + 1;
2178 return TRUE;
2181 static void RemoveDeleted(QResultClass * res, SQLLEN index)
2183 int i, mv_count, rm_count = 0;
2184 SQLLEN pidx, midx;
2185 SQLULEN *deleted, num_read = QR_get_num_total_read(res);
2186 KeySet *deleted_keyset;
2188 mylog("RemoveDeleted index=%d\n", index);
2189 if (index < 0)
2191 midx = index;
2192 pidx = num_read - index - 1;
2193 } else
2195 pidx = index;
2196 if (index >= num_read)
2197 midx = num_read - index - 1;
2198 else
2199 midx = index;
2201 for (i = 0; i < res->dl_count; i++)
2203 if (pidx == res->deleted[i] || midx == res->deleted[i])
2205 mv_count = res->dl_count - i - 1;
2206 if (mv_count > 0)
2208 deleted = res->deleted + i;
2209 deleted_keyset = res->deleted_keyset + i;
2210 memmove(deleted, deleted + 1,
2211 mv_count * sizeof(SQLULEN));
2212 memmove(deleted_keyset, deleted_keyset + 1,
2213 mv_count * sizeof(KeySet));
2215 res->dl_count--;
2216 rm_count++;
2219 mylog("RemoveDeleted removed count=%d,%d\n", rm_count,
2220 res->dl_count);
2223 static void CommitDeleted(QResultClass * res)
2225 int i;
2226 SQLULEN *deleted;
2227 KeySet *deleted_keyset;
2228 UWORD status;
2230 if (!res->deleted)
2231 return;
2233 for (i = 0, deleted = res->deleted, deleted_keyset =
2234 res->deleted_keyset; i < res->dl_count;
2235 i++, deleted++, deleted_keyset++)
2237 status = deleted_keyset->status;
2238 if (0 != (status & CURS_SELF_ADDING))
2240 status |= CURS_SELF_ADDED;
2241 status &= ~CURS_SELF_ADDING;
2243 if (0 != (status & CURS_SELF_UPDATING))
2245 status |= CURS_SELF_UPDATED;
2246 status &= ~CURS_SELF_UPDATING;
2248 if (0 != (status & CURS_SELF_DELETING))
2250 status |= CURS_SELF_DELETED;
2251 status &= ~CURS_SELF_DELETING;
2253 if (status != deleted_keyset->status)
2255 inolog("!!Commit Deleted=%d(%d)\n", *deleted, i);
2256 deleted_keyset->status = status;
2261 static BOOL enlargeUpdated(QResultClass * res, Int4 number,
2262 const StatementClass * stmt)
2264 Int2 alloc;
2265 SQLULEN *updated;
2266 KeySet *updated_keyset;
2267 TupleField *updated_tuples = NULL;
2269 alloc = res->up_alloc;
2270 if (0 == alloc)
2271 alloc = number > 10 ? number : 10;
2272 else
2273 while (alloc < number)
2275 alloc *= 2;
2277 if (alloc <= res->up_alloc)
2278 return TRUE;
2280 if (updated = (SQLUINTEGER *)
2281 realloc(res->updated, sizeof(UInt4) * alloc), !updated)
2283 if (res->updated_keyset)
2285 free(res->updated_keyset);
2286 res->updated_keyset = NULL;
2288 res->up_alloc = 0;
2289 return FALSE;
2291 if (updated_keyset = (KeySet *)
2292 realloc(res->updated_keyset, sizeof(KeySet) * alloc),
2293 !updated_keyset)
2295 free(res->updated);
2296 res->updated = NULL;
2297 res->up_alloc = 0;
2298 return FALSE;
2300 if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
2301 if (updated_tuples = (TupleField *)
2302 realloc(res->updated_tuples,
2303 sizeof(TupleField) * res->num_fields * alloc),
2304 !updated_tuples)
2306 free(res->updated);
2307 res->updated = NULL;
2308 free(res->updated_keyset);
2309 res->updated_keyset = NULL;
2310 res->up_alloc = 0;
2311 return FALSE;
2313 res->updated = updated;
2314 res->updated_keyset = updated_keyset;
2315 res->updated_tuples = updated_tuples;
2316 res->up_alloc = alloc;
2318 return TRUE;
2321 static void AddUpdated(StatementClass * stmt, SQLLEN index)
2323 QResultClass *res;
2324 SQLULEN *updated;
2325 KeySet *updated_keyset, *keyset;
2326 TupleField *updated_tuples = NULL, *tuple_updated, *tuple;
2327 SQLULEN kres_ridx;
2328 UInt2 up_count;
2329 SQLLEN upd_idx, upd_add_idx;
2330 Int2 num_fields;
2331 int i;
2332 UWORD status;
2334 inolog("AddUpdated index=%d\n", index);
2335 if (!stmt)
2336 return;
2337 if (res = SC_get_Curres(stmt), !res)
2338 return;
2339 if (!res->keyset)
2340 return;
2341 kres_ridx = GIdx2KResIdx(index, stmt, res);
2342 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
2343 return;
2344 keyset = res->keyset + kres_ridx;
2345 if (!QR_get_cursor(res))
2346 return;
2347 up_count = res->up_count;
2348 if (up_count > 0 && 0 == res->up_alloc)
2349 return;
2350 num_fields = res->num_fields;
2351 tuple_updated = res->backend_tuples + kres_ridx * num_fields;
2352 if (!tuple_updated)
2353 return;
2354 upd_idx = -1;
2355 upd_add_idx = -1;
2356 updated = res->updated;
2357 updated_keyset = res->updated_keyset;
2358 status = keyset->status;
2359 status &= (~KEYSET_INFO_PUBLIC);
2360 status |= SQL_ROW_UPDATED;
2362 for (i = up_count - 1; i >= 0; i--)
2364 if (updated[i] == index)
2365 break;
2367 if (i >= 0)
2368 upd_idx = i;
2369 else
2371 SQLLEN num_totals = QR_get_num_total_tuples(res);
2372 if (index >= num_totals)
2373 upd_add_idx = num_totals - index;
2375 status |= CURS_SELF_UPDATED;
2376 status &=
2377 ~(CURS_SELF_ADDING | CURS_SELF_UPDATING |
2378 CURS_SELF_DELETING);
2380 tuple = NULL;
2381 /* update the corresponding add(updat)ed info */
2382 if (upd_add_idx >= 0)
2384 res->added_keyset[upd_add_idx].status = status;
2385 if (res->added_tuples)
2387 tuple = res->added_tuples + num_fields * upd_add_idx;
2388 ClearCachedRows(tuple, num_fields, 1);
2390 } else if (upd_idx >= 0)
2392 res->updated_keyset[upd_idx].status = status;
2393 if (res->updated_tuples)
2395 tuple = res->added_tuples + num_fields * upd_add_idx;
2396 ClearCachedRows(tuple, num_fields, 1);
2398 } else
2400 if (!enlargeUpdated(res, res->up_count + 1, stmt))
2401 return;
2402 updated = res->updated;
2403 updated_keyset = res->updated_keyset;
2404 updated_tuples = res->updated_tuples;
2405 upd_idx = up_count;
2406 updated[up_count] = index;
2407 updated_keyset[up_count] = *keyset;
2408 updated_keyset[up_count].status = status;
2409 if (updated_tuples)
2411 tuple = updated_tuples + num_fields * up_count;
2412 memset(tuple, 0, sizeof(TupleField) * num_fields);
2414 res->up_count++;
2417 if (tuple)
2418 ReplaceCachedRows(tuple, tuple_updated, num_fields, 1);
2419 mylog("up_count=%d\n", res->up_count);
2422 static void RemoveUpdated(QResultClass * res, SQLLEN index)
2424 mylog("RemoveUpdated index=%d\n", index);
2425 RemoveUpdatedAfterTheKey(res, index, NULL);
2428 static void RemoveUpdatedAfterTheKey(QResultClass * res, SQLLEN index,
2429 const KeySet * keyset)
2431 SQLULEN *updated, num_read = QR_get_num_total_read(res);
2432 KeySet *updated_keyset;
2433 TupleField *updated_tuples = NULL;
2434 SQLLEN pidx, midx, mv_count;
2435 int i, num_fields = res->num_fields, rm_count = 0;
2437 mylog("RemoveUpdatedAfterTheKey %d,(%d,%d)\n", index,
2438 keyset ? keyset->blocknum : 0, keyset ? keyset->offset : 0);
2439 if (index < 0)
2441 midx = index;
2442 pidx = num_read - index - 1;
2443 } else
2445 pidx = index;
2446 if (index >= num_read)
2447 midx = num_read - index - 1;
2448 else
2449 midx = index;
2451 for (i = 0; i < res->up_count; i++)
2453 updated = res->updated + i;
2454 if (pidx == *updated || midx == *updated)
2456 updated_keyset = res->updated_keyset + i;
2457 if (keyset &&
2458 updated_keyset->blocknum == keyset->blocknum &&
2459 updated_keyset->offset == keyset->offset)
2460 break;
2461 updated_tuples = NULL;
2462 if (res->updated_tuples)
2464 updated_tuples = res->updated_tuples + i * num_fields;
2465 ClearCachedRows(updated_tuples, num_fields, 1);
2467 mv_count = res->up_count - i - 1;
2468 if (mv_count > 0)
2470 memmove(updated, updated + 1,
2471 sizeof(SQLULEN) * mv_count);
2472 memmove(updated_keyset, updated_keyset + 1,
2473 sizeof(KeySet) * mv_count);
2474 if (updated_tuples)
2475 memmove(updated_tuples, updated_tuples + num_fields,
2476 sizeof(TupleField) * num_fields * mv_count);
2478 res->up_count--;
2479 rm_count++;
2482 mylog("RemoveUpdatedAfter removed count=%d,%d\n", rm_count,
2483 res->up_count);
2486 static void CommitUpdated(QResultClass * res)
2488 KeySet *updated_keyset;
2489 int i;
2490 UWORD status;
2492 mylog("CommitUpdated res=%p\n", res);
2493 if (!res)
2494 return;
2495 if (!QR_get_cursor(res))
2496 return;
2497 if (res->up_count <= 0)
2498 return;
2499 if (updated_keyset = res->updated_keyset, !updated_keyset)
2500 return;
2501 for (i = res->up_count - 1; i >= 0; i--)
2503 status = updated_keyset[i].status;
2504 if (0 != (status & CURS_SELF_UPDATING))
2506 status &= ~CURS_SELF_UPDATING;
2507 status |= CURS_SELF_UPDATED;
2509 if (0 != (status & CURS_SELF_ADDING))
2511 status &= ~CURS_SELF_ADDING;
2512 status |= CURS_SELF_ADDED;
2514 if (0 != (status & CURS_SELF_DELETING))
2516 status &= ~CURS_SELF_DELETING;
2517 status |= CURS_SELF_DELETED;
2519 if (status != updated_keyset[i].status)
2521 inolog("!!Commit Updated=%d(%d)\n", res->updated[i], i);
2522 updated_keyset[i].status = status;
2528 static void DiscardRollback(StatementClass * stmt, QResultClass * res)
2530 int i;
2531 SQLLEN index, kres_ridx;
2532 UWORD status;
2533 Rollback *rollback;
2534 KeySet *keyset;
2535 BOOL kres_is_valid;
2537 inolog("DiscardRollback");
2538 if (QR_get_cursor(res))
2540 CommitAdded(res);
2541 CommitUpdated(res);
2542 CommitDeleted(res);
2543 return;
2546 if (0 == res->rb_count || NULL == res->rollback)
2547 return;
2548 rollback = res->rollback;
2549 keyset = res->keyset;
2550 for (i = 0; i < res->rb_count; i++)
2552 index = rollback[i].index;
2553 status = 0;
2554 kres_is_valid = FALSE;
2555 if (index >= 0)
2557 kres_ridx = GIdx2KResIdx(index, stmt, res);
2558 if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
2560 kres_is_valid = TRUE;
2561 status = keyset[kres_ridx].status;
2564 if (kres_is_valid)
2566 keyset[kres_ridx].status &=
2567 ~(CURS_SELF_DELETING | CURS_SELF_UPDATING |
2568 CURS_SELF_ADDING);
2569 keyset[kres_ridx].status |=
2570 ((status &
2571 (CURS_SELF_DELETING | CURS_SELF_UPDATING |
2572 CURS_SELF_ADDING)) << 3);
2575 free(rollback);
2576 res->rollback = NULL;
2577 res->rb_count = res->rb_alloc = 0;
2580 static QResultClass *positioned_load(StatementClass * stmt, UInt4 flag,
2581 const UInt4 * oidint,
2582 const char *tid);
2583 static void UndoRollback(StatementClass * stmt, QResultClass * res,
2584 BOOL partial)
2586 Int4 i, rollbp;
2587 SQLLEN index, ridx, kres_ridx;
2588 UWORD status;
2589 Rollback *rollback;
2590 KeySet *keyset, keys, *wkey = NULL;
2591 BOOL curs = (NULL != QR_get_cursor(res)), texist, kres_is_valid;
2593 if (0 == res->rb_count || NULL == res->rollback)
2594 return;
2595 rollback = res->rollback;
2596 keyset = res->keyset;
2598 rollbp = 0;
2599 if (partial)
2601 SQLLEN pidx, midx;
2602 Int2 doubtp, rollbps;
2603 int j;
2605 rollbps = rollbp = res->rb_count;
2606 for (i = 0, doubtp = 0; i < res->rb_count; i++)
2608 index = rollback[i].index;
2609 keys.blocknum = rollback[i].blocknum;
2610 keys.offset = rollback[i].offset;
2611 texist = tupleExists(stmt, &keys);
2612 inolog("texist[%d]=%d", i, texist);
2613 if (SQL_ADD == rollback[i].option)
2615 if (texist)
2616 doubtp = i + 1;
2617 } else if (SQL_REFRESH == rollback[i].option)
2619 if (texist || doubtp == i)
2620 doubtp = i + 1;
2621 } else
2623 if (texist)
2624 break;
2625 if (doubtp == i)
2626 doubtp = i + 1;
2628 inolog(" doubtp=%d\n", doubtp);
2630 rollbp = i;
2631 inolog(" doubtp=%d,rollbp=%d\n", doubtp, rollbp);
2632 if (doubtp < 0)
2633 doubtp = 0;
2636 rollbps = rollbp;
2637 for (i = doubtp; i < rollbp; i++)
2639 index = rollback[i].index;
2640 if (SQL_ADD == rollback[i].option)
2642 inolog("index[%d]=%d\n", i, index);
2643 if (index < 0)
2645 midx = index;
2646 pidx = res->num_total_read - index - 1;
2647 } else
2649 pidx = index;
2650 midx = res->num_total_read - index - 1;
2652 inolog("pidx=%d,midx=%d\n", pidx, midx);
2653 for (j = rollbp - 1; j > i; j--)
2655 if (rollback[j].index == midx ||
2656 rollback[j].index == pidx)
2658 if (SQL_DELETE == rollback[j].option)
2660 inolog("delete[%d].index=%d\n", j,
2661 rollback[j].index);
2662 break;
2666 if (j <= i)
2668 rollbp = i;
2669 break;
2674 while (rollbp < rollbps);
2676 inolog("rollbp=%d\n", rollbp);
2678 for (i = res->rb_count - 1; i >= rollbp; i--)
2680 inolog("UndoRollback %d(%d)\n", i, rollback[i].option);
2681 index = rollback[i].index;
2682 if (curs)
2684 if (SQL_ADD == rollback[i].option)
2685 RemoveAdded(res, index);
2686 RemoveDeleted(res, index);
2687 keys.blocknum = rollback[i].blocknum;
2688 keys.offset = rollback[i].offset;
2689 RemoveUpdatedAfterTheKey(res, index, &keys);
2691 status = 0;
2692 kres_is_valid = FALSE;
2693 if (index >= 0)
2695 kres_ridx = GIdx2KResIdx(index, stmt, res);
2696 if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
2698 kres_is_valid = TRUE;
2699 wkey = keyset + kres_ridx;
2700 status = wkey->status;
2703 inolog(" index=%d status=%hx", index, status);
2704 if (kres_is_valid)
2706 QResultClass *qres;
2707 Int2 num_fields = res->num_fields;
2709 ridx = GIdx2CacheIdx(index, stmt, res);
2710 if (SQL_ADD == rollback[i].option)
2712 if (ridx >= 0 && ridx < res->num_cached_rows)
2714 TupleField *tuple =
2715 res->backend_tuples + res->num_fields * ridx;
2716 ClearCachedRows(tuple, res->num_fields, 1);
2717 res->num_cached_rows--;
2719 res->num_cached_keys--;
2720 if (!curs)
2721 res->ad_count--;
2722 } else if (SQL_REFRESH == rollback[i].option)
2723 continue;
2724 else
2726 inolog(" (%u, %u)", wkey->blocknum, wkey->offset);
2727 wkey->blocknum = rollback[i].blocknum;
2728 wkey->offset = rollback[i].offset;
2729 inolog("->(%u, %u)\n", wkey->blocknum, wkey->offset);
2730 wkey->status &= ~KEYSET_INFO_PUBLIC;
2731 if (SQL_DELETE == rollback[i].option)
2732 wkey->status &= ~CURS_SELF_DELETING;
2733 else if (SQL_UPDATE == rollback[i].option)
2734 wkey->status &= ~CURS_SELF_UPDATING;
2735 wkey->status |= CURS_NEEDS_REREAD;
2736 if (ridx >= 0 && ridx < res->num_cached_rows)
2738 char tidval[32];
2740 sprintf(tidval, "(%d,%d)", wkey->blocknum,
2741 wkey->offset);
2742 qres = positioned_load(stmt, 0, NULL, tidval);
2743 if (QR_command_maybe_successful(qres) &&
2744 QR_get_num_cached_tuples(qres) == 1)
2746 MoveCachedRows(res->backend_tuples +
2747 num_fields * ridx,
2748 qres->backend_tuples, num_fields,
2750 wkey->status &= ~CURS_NEEDS_REREAD;
2752 QR_Destructor(qres);
2757 res->rb_count = rollbp;
2758 if (0 == rollbp)
2760 free(rollback);
2761 res->rollback = NULL;
2762 res->rb_alloc = 0;
2766 void ProcessRollback(ConnectionClass * conn, BOOL undo, BOOL partial)
2768 int i;
2769 StatementClass *stmt;
2770 QResultClass *res;
2772 for (i = 0; i < conn->num_stmts; i++)
2774 if (stmt = conn->stmts[i], !stmt)
2775 continue;
2776 for (res = SC_get_Result(stmt); res; res = res->next)
2778 if (undo)
2779 UndoRollback(stmt, res, partial);
2780 else
2781 DiscardRollback(stmt, res);
2787 #define LATEST_TUPLE_LOAD 1L
2788 #define USE_INSERTED_TID (1L << 1)
2789 static QResultClass *positioned_load(StatementClass * stmt, UInt4 flag,
2790 const UInt4 * oidint,
2791 const char *tidval)
2793 CSTR func = "positioned_load";
2794 CSTR andqual = " and ";
2795 QResultClass *qres = NULL;
2796 char *selstr, oideqstr[256];
2797 BOOL latest = ((flag & LATEST_TUPLE_LOAD) != 0);
2798 size_t len;
2799 TABLE_INFO *ti = stmt->ti[0];
2800 const char *bestitem = GET_NAME(ti->bestitem);
2801 const char *bestqual = GET_NAME(ti->bestqual);
2803 inolog("%s bestitem=%s bestqual=%s\n", func,
2804 SAFE_NAME(ti->bestitem), SAFE_NAME(ti->bestqual));
2805 if (!bestitem || !oidint)
2806 *oideqstr = '\0';
2807 else
2809 /*snprintf(oideqstr, sizeof(oideqstr), " and \"%s\" = %u", bestitem, oid); */
2810 strcpy(oideqstr, andqual);
2811 sprintf(oideqstr + strlen(andqual), bestqual, *oidint);
2813 len = strlen(stmt->load_statement);
2814 len += strlen(oideqstr);
2815 if (tidval)
2816 len += 100;
2817 else if ((flag & USE_INSERTED_TID) != 0)
2818 len += 50;
2819 else
2820 len += 20;
2821 selstr = (char *)malloc(len);
2822 if (tidval)
2824 if (latest)
2826 if (NAME_IS_VALID(ti->schema_name))
2827 snprintf(selstr, len,
2828 "%s where ctid = currtid2('\"%s\".\"%s\"', '%s') %s",
2829 stmt->load_statement,
2830 SAFE_NAME(ti->schema_name),
2831 SAFE_NAME(ti->table_name), tidval, oideqstr);
2832 else
2833 snprintf(selstr, len,
2834 "%s where ctid = currtid2('%s', '%s') %s",
2835 stmt->load_statement,
2836 SAFE_NAME(ti->table_name), tidval, oideqstr);
2837 } else
2838 snprintf(selstr, len, "%s where ctid = '%s' %s",
2839 stmt->load_statement, tidval, oideqstr);
2840 } else if ((flag & USE_INSERTED_TID) != 0)
2841 snprintf(selstr, len, "%s where ctid = currtid(0, '(0,0)') %s",
2842 stmt->load_statement, oideqstr);
2843 else if (bestitem && oidint)
2845 /*snprintf(selstr, len, "%s where \"%s\" = %u", stmt->load_statement, bestitem, *oid); */
2846 snprintf(selstr, len, "%s where ", stmt->load_statement);
2847 snprintf_add(selstr, len, bestqual, *oidint);
2848 } else
2850 SC_set_error(stmt, STMT_INTERNAL_ERROR,
2851 "can't find the add and updating row because of the lack of oid",
2852 func);
2853 goto cleanup;
2856 mylog("selstr=%s\n", selstr);
2857 qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, stmt);
2858 cleanup:
2859 free(selstr);
2860 return qres;
2863 static RETCODE
2864 SC_pos_reload_with_tid(StatementClass * stmt, SQLULEN global_ridx,
2865 UInt2 * count, Int4 logKind, const char *tid)
2867 CSTR func = "SC_pos_reload";
2868 int res_cols;
2869 UInt2 offset;
2870 UInt2 rcnt;
2871 SQLLEN res_ridx, kres_ridx;
2872 OID oidint;
2873 UInt4 blocknum;
2874 QResultClass *res, *qres;
2875 IRDFields *irdflds = SC_get_IRDF(stmt);
2876 RETCODE ret = SQL_ERROR;
2877 char tidval[32];
2878 BOOL use_ctid = TRUE, data_in_cache = TRUE, key_in_cache = TRUE;
2880 mylog("positioned load fi=%p ti=%p\n", irdflds->fi, stmt->ti);
2881 rcnt = 0;
2882 if (count)
2883 *count = 0;
2884 if (!(res = SC_get_Curres(stmt)))
2886 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
2887 "Null statement result in SC_pos_reload.", func);
2888 return SQL_ERROR;
2890 res_ridx = GIdx2CacheIdx(global_ridx, stmt, res);
2891 if (res_ridx < 0 || res_ridx >= QR_get_num_cached_tuples(res))
2893 data_in_cache = FALSE;
2894 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
2895 "the target rows is out of the rowset", func);
2896 return SQL_ERROR;
2898 kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
2899 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
2901 key_in_cache = FALSE;
2902 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
2903 "the target rows is out of the rowset", func);
2904 return SQL_ERROR;
2905 } else if (0 != (res->keyset[kres_ridx].status & CURS_SELF_ADDING))
2907 if (NULL == tid)
2909 use_ctid = FALSE;
2910 mylog
2911 ("The tuple is currently being added and can't use ctid\n");
2915 if (SC_update_not_ready(stmt))
2916 parse_statement(stmt, TRUE); /* not preferable */
2917 if (!stmt->updatable)
2919 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2920 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
2921 "the statement is read-only", func);
2922 return SQL_ERROR;
2924 if (!(oidint = getOid(res, kres_ridx)))
2926 if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
2928 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
2929 "the row was already deleted ?", func);
2930 return SQL_SUCCESS_WITH_INFO;
2933 getTid(res, kres_ridx, &blocknum, &offset);
2934 sprintf(tidval, "(%u, %u)", blocknum, offset);
2935 res_cols = getNumResultCols(res);
2936 if (tid)
2937 qres = positioned_load(stmt, 0, &oidint, tid);
2938 else
2939 qres =
2940 positioned_load(stmt, use_ctid ? LATEST_TUPLE_LOAD : 0,
2941 &oidint, use_ctid ? tidval : NULL);
2942 if (!QR_command_maybe_successful(qres))
2944 ret = SQL_ERROR;
2945 SC_replace_error_with_res(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
2946 "positioned_load failed", qres, TRUE);
2947 } else
2949 TupleField *tuple_old, *tuple_new;
2950 ConnectionClass *conn = SC_get_conn(stmt);
2952 rcnt = (UInt2) QR_get_num_cached_tuples(qres);
2953 tuple_old = res->backend_tuples + res->num_fields * res_ridx;
2954 // VX_CLEANUP: It might be possible to extend the carnage from here.
2955 if (rcnt == 1)
2957 int effective_fields = res_cols;
2959 QR_set_position(qres, 0);
2960 tuple_new = qres->tupleField;
2961 if (res->keyset && key_in_cache)
2963 if (SQL_CURSOR_KEYSET_DRIVEN ==
2964 stmt->options.cursor_type
2966 strcmp((const char *)tuple_new
2967 [qres->num_fields -
2968 res->num_key_fields].value, tidval))
2969 res->keyset[kres_ridx].status |= SQL_ROW_UPDATED;
2970 KeySetSet(tuple_new, qres->num_fields,
2971 res->num_key_fields, res->keyset + kres_ridx);
2973 if (data_in_cache)
2974 MoveCachedRows(tuple_old, tuple_new, effective_fields,
2976 ret = SQL_SUCCESS;
2977 } else
2979 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
2980 "the content was deleted after last fetch",
2981 func);
2982 ret = SQL_SUCCESS_WITH_INFO;
2983 if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
2985 res->keyset[kres_ridx].status |= SQL_ROW_DELETED;
2989 QR_Destructor(qres);
2990 if (count)
2991 *count = rcnt;
2992 return ret;
2995 RETCODE
2996 SC_pos_reload(StatementClass * stmt, SQLULEN global_ridx, UInt2 * count,
2997 Int4 logKind)
2999 return SC_pos_reload_with_tid(stmt, global_ridx, count, logKind,
3000 NULL);
3003 static const int pre_fetch_count = 32;
3004 static SQLLEN LoadFromKeyset(StatementClass * stmt, QResultClass * res,
3005 int rows_per_fetch, SQLLEN limitrow)
3007 CSTR func = "LoadFromKeyset";
3008 ConnectionClass *conn = SC_get_conn(stmt);
3009 SQLLEN i;
3010 int j, rowc, rcnt = 0;
3011 BOOL prepare;
3012 OID oid;
3013 UInt4 blocknum;
3014 SQLLEN kres_ridx;
3015 UInt2 offset;
3016 char *qval = NULL, *sval = NULL;
3017 int keys_per_fetch = 10;
3019 prepare = PG_VERSION_GE(conn, 7.3);
3020 for (i = SC_get_rowset_start(stmt), kres_ridx =
3021 GIdx2KResIdx(i, stmt, res), rowc = 0;; i++)
3023 if (i >= limitrow)
3025 if (!rowc)
3026 break;
3027 if (res->reload_count > 0)
3029 for (j = rowc; j < keys_per_fetch; j++)
3031 if (j)
3032 strcpy(sval, ",NULL");
3033 else
3034 strcpy(sval, "NULL");
3035 sval = strchr(sval, '\0');
3038 rowc = -1; /* end of loop */
3040 if (rowc < 0 || rowc >= keys_per_fetch)
3042 QResultClass *qres;
3044 strcpy(sval, ")");
3045 qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET, stmt);
3046 if (QR_command_maybe_successful(qres))
3048 SQLLEN j, k, l;
3049 Int2 m;
3050 TupleField *tuple, *tuplew;
3052 for (j = 0; j < QR_get_num_total_read(qres); j++)
3054 oid = getOid(qres, j);
3055 getTid(qres, j, &blocknum, &offset);
3056 for (k = SC_get_rowset_start(stmt); k < limitrow;
3057 k++)
3059 if (oid == getOid(res, k))
3061 l = GIdx2CacheIdx(k, stmt, res);
3062 tuple =
3063 res->backend_tuples +
3064 res->num_fields * l;
3065 tuplew =
3066 qres->backend_tuples +
3067 qres->num_fields * j;
3068 for (m = 0; m < res->num_fields;
3069 m++, tuple++, tuplew++)
3071 if (tuple->len > 0 && tuple->value)
3072 free(tuple->value);
3073 tuple->value = tuplew->value;
3074 tuple->len = tuplew->len;
3075 tuplew->value = NULL;
3076 tuplew->len = -1;
3078 res->keyset[k].status &= ~CURS_NEEDS_REREAD;
3079 break;
3083 } else
3085 SC_set_error(stmt, STMT_EXEC_ERROR, "Data Load Error",
3086 func);
3087 rcnt = -1;
3088 QR_Destructor(qres);
3089 break;
3091 QR_Destructor(qres);
3092 if (rowc < 0)
3093 break;
3094 rowc = 0;
3096 if (!rowc)
3098 size_t lodlen = 0;
3100 if (!qval)
3102 size_t allen;
3104 if (prepare)
3106 if (res->reload_count > 0)
3107 keys_per_fetch = res->reload_count;
3108 else
3110 char planname[32];
3111 int j;
3112 QResultClass *qres;
3114 if (rows_per_fetch >= pre_fetch_count * 2)
3115 keys_per_fetch = pre_fetch_count;
3116 else
3117 keys_per_fetch = rows_per_fetch;
3118 if (!keys_per_fetch)
3119 keys_per_fetch = 2;
3120 lodlen = strlen(stmt->load_statement);
3121 sprintf(planname, "_KEYSET_%p", res);
3122 allen = 8 + strlen(planname) +
3123 3 + 4 * keys_per_fetch + 1
3124 + 1 + 2 + lodlen + 20 +
3125 4 * keys_per_fetch + 1;
3126 SC_MALLOC_return_with_error(qval, char, allen,
3127 stmt,
3128 "Couldn't alloc qval",
3129 -1);
3130 sprintf(qval, "PREPARE \"%s\"", planname);
3131 sval = strchr(qval, '\0');
3132 for (j = 0; j < keys_per_fetch; j++)
3134 if (j == 0)
3135 strcpy(sval, "(tid");
3136 else
3137 strcpy(sval, ",tid");
3138 sval = strchr(sval, '\0');
3140 sprintf(sval, ") as %s where ctid in ",
3141 stmt->load_statement);
3142 sval = strchr(sval, '\0');
3143 for (j = 0; j < keys_per_fetch; j++)
3145 if (j == 0)
3146 strcpy(sval, "($1");
3147 else
3148 sprintf(sval, ",$%d", j + 1);
3149 sval = strchr(sval, '\0');
3151 strcpy(sval, ")");
3152 qres = CC_send_query(conn, qval, NULL, 0, stmt);
3153 if (QR_command_maybe_successful(qres))
3155 res->reload_count = keys_per_fetch;
3156 } else
3158 SC_set_error(stmt, STMT_EXEC_ERROR,
3159 "Prepare for Data Load Error",
3160 func);
3161 rcnt = -1;
3162 QR_Destructor(qres);
3163 break;
3165 QR_Destructor(qres);
3167 allen = 25 + 23 * keys_per_fetch;
3168 } else
3170 keys_per_fetch = pre_fetch_count;
3171 lodlen = strlen(stmt->load_statement);
3172 allen = lodlen + 20 + 23 * keys_per_fetch;
3174 SC_REALLOC_return_with_error(qval, char, allen,
3175 stmt,
3176 "Couldn't alloc qval", -1);
3178 if (res->reload_count > 0)
3180 sprintf(qval, "EXECUTE \"_KEYSET_%p\"(", res);
3181 sval = qval;
3182 } else
3184 memcpy(qval, stmt->load_statement, lodlen);
3185 sval = qval + lodlen;
3186 sval[0] = '\0';
3187 strcpy(sval, " where ctid in (");
3189 sval = strchr(sval, '\0');
3191 if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
3193 getTid(res, i, &blocknum, &offset);
3194 if (rowc)
3195 sprintf(sval, ",'(%u,%u)'", blocknum, offset);
3196 else
3197 sprintf(sval, "'(%u,%u)'", blocknum, offset);
3198 sval = strchr(sval, '\0');
3199 rowc++;
3200 rcnt++;
3203 if (qval)
3204 free(qval);
3205 return rcnt;
3208 static RETCODE SQL_API
3209 SC_pos_reload_needed(StatementClass * stmt, SQLULEN req_size,
3210 UDWORD flag)
3212 CSTR func = "SC_pos_reload_needed";
3213 Int4 req_rows_size;
3214 SQLLEN i, limitrow;
3215 UInt2 qcount;
3216 QResultClass *res;
3217 RETCODE ret = SQL_ERROR;
3218 SQLLEN kres_ridx, rowc;
3219 Int4 rows_per_fetch;
3220 BOOL create_from_scratch = (0 != flag);
3222 mylog("%s\n", func);
3223 if (!(res = SC_get_Curres(stmt)))
3225 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3226 "Null statement result in SC_pos_reload_needed.",
3227 func);
3228 return SQL_ERROR;
3230 if (SC_update_not_ready(stmt))
3231 parse_statement(stmt, TRUE); /* not preferable */
3232 if (!stmt->updatable)
3234 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3235 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3236 "the statement is read-only", func);
3237 return SQL_ERROR;
3239 rows_per_fetch = 0;
3240 req_rows_size = QR_get_reqsize(res);
3241 if (req_size > req_rows_size)
3242 req_rows_size = (UInt4) req_size;
3243 if (create_from_scratch)
3245 rows_per_fetch =
3246 ((pre_fetch_count - 1) / req_rows_size + 1) * req_rows_size;
3247 limitrow = RowIdx2GIdx(rows_per_fetch, stmt);
3248 } else
3250 limitrow = RowIdx2GIdx(req_rows_size, stmt);
3252 if (limitrow > res->num_cached_keys)
3253 limitrow = res->num_cached_keys;
3254 if (create_from_scratch)
3256 SQLLEN brows;
3258 ClearCachedRows(res->backend_tuples, res->num_fields,
3259 res->num_cached_rows);
3260 brows = GIdx2RowIdx(limitrow, stmt);
3261 if (brows > res->count_backend_allocated)
3263 res->backend_tuples = (TupleField *)
3264 realloc(res->backend_tuples,
3265 sizeof(TupleField) * res->num_fields * brows);
3266 res->count_backend_allocated = brows;
3268 if (brows > 0)
3269 memset(res->backend_tuples, 0,
3270 sizeof(TupleField) * res->num_fields * brows);
3271 QR_set_num_cached_rows(res, brows);
3272 QR_set_rowstart_in_cache(res, 0);
3273 if (SQL_RD_ON != stmt->options.retrieve_data)
3274 return SQL_SUCCESS;
3275 for (i = SC_get_rowset_start(stmt), kres_ridx =
3276 GIdx2KResIdx(i, stmt, res); i < limitrow; i++, kres_ridx++)
3278 if (0 ==
3279 (res->keyset[kres_ridx].
3280 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
3281 CURS_OTHER_DELETED)))
3282 res->keyset[kres_ridx].status |= CURS_NEEDS_REREAD;
3285 if (rowc =
3286 LoadFromKeyset(stmt, res, rows_per_fetch, limitrow), rowc < 0)
3288 return SQL_ERROR;
3290 for (i = SC_get_rowset_start(stmt), kres_ridx =
3291 GIdx2KResIdx(i, stmt, res); i < limitrow; i++)
3293 if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
3295 ret = SC_pos_reload(stmt, i, &qcount, 0);
3296 if (SQL_ERROR == ret)
3298 break;
3300 if (SQL_ROW_DELETED ==
3301 (res->keyset[kres_ridx].status & KEYSET_INFO_PUBLIC))
3303 res->keyset[kres_ridx].status |= CURS_OTHER_DELETED;
3305 res->keyset[kres_ridx].status &= ~CURS_NEEDS_REREAD;
3308 return ret;
3311 static RETCODE SQL_API
3312 SC_pos_newload(StatementClass * stmt, const UInt4 * oidint, BOOL tidRef,
3313 const char *tidval)
3315 CSTR func = "SC_pos_newload";
3316 int i;
3317 QResultClass *res, *qres;
3318 RETCODE ret = SQL_ERROR;
3320 mylog("positioned new ti=%p\n", stmt->ti);
3321 if (!(res = SC_get_Curres(stmt)))
3323 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3324 "Null statement result in SC_pos_newload.", func);
3325 return SQL_ERROR;
3327 if (SC_update_not_ready(stmt))
3328 parse_statement(stmt, TRUE); /* not preferable */
3329 if (!stmt->updatable)
3331 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3332 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3333 "the statement is read-only", func);
3334 return SQL_ERROR;
3336 qres =
3337 positioned_load(stmt,
3338 (tidRef
3339 && NULL == tidval) ? USE_INSERTED_TID : 0,
3340 oidint, tidRef ? tidval : NULL);
3341 if (!qres || !QR_command_maybe_successful(qres))
3343 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3344 "positioned_load in pos_newload failed", func);
3345 } else
3347 SQLLEN count = QR_get_num_cached_tuples(qres);
3349 QR_set_position(qres, 0);
3350 if (count == 1)
3352 int effective_fields = res->num_fields;
3353 ssize_t tuple_size;
3354 SQLLEN num_total_rows, num_cached_rows, kres_ridx;
3355 BOOL appendKey = FALSE, appendData = FALSE;
3356 TupleField *tuple_old, *tuple_new;
3358 tuple_new = qres->tupleField;
3359 num_total_rows = QR_get_num_total_tuples(res);
3361 AddAdded(stmt, res, num_total_rows, tuple_new);
3362 num_cached_rows = QR_get_num_cached_tuples(res);
3363 kres_ridx = GIdx2KResIdx(num_total_rows, stmt, res);
3364 if (QR_haskeyset(res))
3366 if (!QR_get_cursor(res))
3368 appendKey = TRUE;
3369 if (num_total_rows ==
3370 CacheIdx2GIdx(num_cached_rows, stmt, res))
3371 appendData = TRUE;
3372 else
3374 inolog
3375 ("total %d <> backend %d - base %d + start %d cursor_type=%d\n",
3376 num_total_rows, num_cached_rows,
3377 QR_get_rowstart_in_cache(res),
3378 SC_get_rowset_start(stmt),
3379 stmt->options.cursor_type);
3381 } else if (kres_ridx >= 0
3382 && kres_ridx < res->cache_size)
3384 appendKey = TRUE;
3385 appendData = TRUE;
3388 if (appendKey)
3390 if (res->num_cached_keys >= res->count_keyset_allocated)
3392 if (!res->count_keyset_allocated)
3393 tuple_size = TUPLE_MALLOC_INC;
3394 else
3395 tuple_size = res->count_keyset_allocated * 2;
3396 res->keyset =
3397 (KeySet *) realloc(res->keyset,
3398 sizeof(KeySet) * tuple_size);
3399 res->count_keyset_allocated = tuple_size;
3401 KeySetSet(tuple_new, qres->num_fields,
3402 res->num_key_fields, res->keyset + kres_ridx);
3403 res->num_cached_keys++;
3405 if (appendData)
3407 inolog
3408 ("total %d == backend %d - base %d + start %d cursor_type=%d\n",
3409 num_total_rows, num_cached_rows,
3410 QR_get_rowstart_in_cache(res),
3411 SC_get_rowset_start(stmt),
3412 stmt->options.cursor_type);
3413 if (num_cached_rows >= res->count_backend_allocated)
3415 if (!res->count_backend_allocated)
3416 tuple_size = TUPLE_MALLOC_INC;
3417 else
3418 tuple_size = res->count_backend_allocated * 2;
3419 res->backend_tuples =
3420 (TupleField *) realloc(res->backend_tuples,
3421 res->num_fields *
3422 sizeof(TupleField) *
3423 tuple_size);
3424 if (!res->backend_tuples)
3426 SC_set_error(stmt,
3427 QR_set_rstatus(res,
3428 PORES_FATAL_ERROR),
3429 "Out of memory while reading tuples.",
3430 func);
3431 QR_Destructor(qres);
3432 return SQL_ERROR;
3434 res->count_backend_allocated = tuple_size;
3436 tuple_old =
3437 res->backend_tuples +
3438 res->num_fields * num_cached_rows;
3439 for (i = 0; i < effective_fields; i++)
3441 tuple_old[i].len = tuple_new[i].len;
3442 tuple_new[i].len = -1;
3443 tuple_old[i].value = tuple_new[i].value;
3444 tuple_new[i].value = NULL;
3446 res->num_cached_rows++;
3448 ret = SQL_SUCCESS;
3449 } else if (0 == count)
3450 ret = SQL_NO_DATA_FOUND;
3451 else
3453 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3454 "the driver cound't identify inserted rows",
3455 func);
3456 ret = SQL_ERROR;
3458 /* stmt->currTuple = SC_get_rowset_start(stmt) + ridx; */
3460 QR_Destructor(qres);
3461 return ret;
3464 static RETCODE SQL_API
3465 irow_update(RETCODE ret, StatementClass * stmt, StatementClass * ustmt,
3466 SQLSETPOSIROW irow, SQLULEN global_ridx)
3468 CSTR func = "irow_update";
3470 if (ret != SQL_ERROR)
3472 int updcnt;
3473 QResultClass *tres = SC_get_Curres(ustmt);
3474 const char *cmdstr = QR_get_command(tres);
3476 if (cmdstr && sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
3478 if (updcnt == 1)
3480 const char *tidval = NULL;
3482 if (NULL != tres->backend_tuples &&
3483 1 == QR_get_num_cached_tuples(tres))
3484 tidval = QR_get_value_backend_text(tres, 0, 0);
3485 ret =
3486 SC_pos_reload_with_tid(stmt, global_ridx,
3487 (UInt2 *) 0, SQL_UPDATE,
3488 tidval);
3489 if (SQL_ERROR != ret)
3490 AddUpdated(stmt, global_ridx);
3491 } else if (updcnt == 0)
3493 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3494 "the content was changed before updation",
3495 func);
3496 ret = SQL_ERROR;
3497 if (stmt->options.cursor_type ==
3498 SQL_CURSOR_KEYSET_DRIVEN)
3499 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
3500 } else
3501 ret = SQL_ERROR;
3502 } else
3503 ret = SQL_ERROR;
3504 if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
3506 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3507 "SetPos update return error", func);
3510 return ret;
3513 /* SQL_NEED_DATA callback for SC_pos_update */
3514 typedef struct {
3515 BOOL updyes;
3516 QResultClass *res;
3517 StatementClass *stmt, *qstmt;
3518 IRDFields *irdflds;
3519 SQLSETPOSIROW irow;
3520 SQLULEN global_ridx;
3521 } pup_cdata;
3522 static RETCODE pos_update_callback(RETCODE retcode, void *para)
3524 CSTR func = "pos_update_callback";
3525 RETCODE ret = retcode;
3526 pup_cdata *s = (pup_cdata *) para;
3527 SQLLEN kres_ridx;
3529 if (s->updyes)
3531 mylog("pos_update_callback in\n");
3532 ret =
3533 irow_update(ret, s->stmt, s->qstmt, s->irow,
3534 s->global_ridx);
3535 inolog("irow_update ret=%d,%d\n", ret,
3536 SC_get_errornumber(s->qstmt));
3537 if (ret != SQL_SUCCESS)
3538 SC_error_copy(s->stmt, s->qstmt, TRUE);
3539 PGAPI_FreeStmt(s->qstmt, SQL_DROP);
3540 s->qstmt = NULL;
3542 s->updyes = FALSE;
3543 kres_ridx = GIdx2KResIdx(s->global_ridx, s->stmt, s->res);
3544 if (kres_ridx < 0 || kres_ridx >= s->res->num_cached_keys)
3546 SC_set_error(s->stmt, STMT_ROW_OUT_OF_RANGE,
3547 "the target rows is out of the rowset", func);
3548 inolog("gidx=%d num_keys=%d kresidx=%d\n", s->global_ridx,
3549 s->res->num_cached_keys, kres_ridx);
3550 return SQL_ERROR;
3552 if (SQL_SUCCESS == ret && s->res->keyset)
3554 ConnectionClass *conn = SC_get_conn(s->stmt);
3556 // VX_CLEANUP: It might be possible to extend the carnage from here.
3557 s->res->keyset[kres_ridx].status |=
3558 (SQL_ROW_UPDATED | CURS_SELF_UPDATED);
3560 if (s->irdflds->rowStatusArray)
3562 switch (ret)
3564 case SQL_SUCCESS:
3565 s->irdflds->rowStatusArray[s->irow] = SQL_ROW_UPDATED;
3566 break;
3567 default:
3568 s->irdflds->rowStatusArray[s->irow] = ret;
3572 return ret;
3575 RETCODE
3576 SC_pos_update(StatementClass * stmt,
3577 SQLSETPOSIROW irow, SQLULEN global_ridx)
3579 CSTR func = "SC_pos_update";
3580 int i, num_cols, upd_cols;
3581 pup_cdata s;
3582 ConnectionClass *conn;
3583 ARDFields *opts = SC_get_ARDF(stmt);
3584 BindInfoClass *bindings = opts->bindings;
3585 TABLE_INFO *ti;
3586 FIELD_INFO **fi;
3587 char updstr[4096];
3588 RETCODE ret;
3589 OID oid;
3590 UInt4 blocknum;
3591 UInt2 pgoffset;
3592 SQLLEN offset;
3593 SQLLEN *used, kres_ridx;
3594 Int4 bind_size = opts->bind_size;
3596 s.stmt = stmt;
3597 s.irow = irow;
3598 s.global_ridx = global_ridx;
3599 s.irdflds = SC_get_IRDF(s.stmt);
3600 fi = s.irdflds->fi;
3601 if (!(s.res = SC_get_Curres(s.stmt)))
3603 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3604 "Null statement result in SC_pos_update.", func);
3605 return SQL_ERROR;
3607 mylog("POS UPDATE %d+%d fi=%p ti=%p\n", s.irow,
3608 QR_get_rowstart_in_cache(s.res), fi, s.stmt->ti);
3609 if (SC_update_not_ready(stmt))
3610 parse_statement(s.stmt, TRUE); /* not preferable */
3611 if (!s.stmt->updatable)
3613 s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3614 SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER,
3615 "the statement is read-only", func);
3616 return SQL_ERROR;
3618 kres_ridx = GIdx2KResIdx(s.global_ridx, s.stmt, s.res);
3619 if (kres_ridx < 0 || kres_ridx >= s.res->num_cached_keys)
3621 SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE,
3622 "the target rows is out of the rowset", func);
3623 return SQL_ERROR;
3625 if (!(oid = getOid(s.res, kres_ridx)))
3627 if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
3629 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3630 "the row was already deleted ?", func);
3631 return SQL_ERROR;
3634 getTid(s.res, kres_ridx, &blocknum, &pgoffset);
3636 ti = s.stmt->ti[0];
3637 if (NAME_IS_VALID(ti->schema_name))
3638 sprintf(updstr, "update \"%s\".\"%s\" set",
3639 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name));
3640 else
3641 sprintf(updstr, "update \"%s\" set", SAFE_NAME(ti->table_name));
3642 num_cols = s.irdflds->nfields;
3643 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
3644 for (i = upd_cols = 0; i < num_cols; i++)
3646 if (used = bindings[i].used, used != NULL)
3648 used = LENADDR_SHIFT(used, offset);
3649 if (bind_size > 0)
3650 used = LENADDR_SHIFT(used, bind_size * s.irow);
3651 else
3652 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
3653 mylog("%d used=%d,%p\n", i, *used, used);
3654 if (*used != SQL_IGNORE && fi[i]->updatable)
3656 if (upd_cols)
3657 sprintf(updstr, "%s, \"%s\" = ?", updstr,
3658 GET_NAME(fi[i]->column_name));
3659 else
3660 sprintf(updstr, "%s \"%s\" = ?", updstr,
3661 GET_NAME(fi[i]->column_name));
3662 upd_cols++;
3664 } else
3665 mylog("%d null bind\n", i);
3667 conn = SC_get_conn(s.stmt);
3668 s.updyes = FALSE;
3669 if (upd_cols > 0)
3671 HSTMT hstmt;
3672 int j;
3673 APDFields *apdopts;
3674 OID fieldtype = 0;
3675 const char *bestitem = GET_NAME(ti->bestitem);
3676 const char *bestqual = GET_NAME(ti->bestqual);
3678 sprintf(updstr, "%s where ctid = '(%u, %u)'", updstr,
3679 blocknum, pgoffset);
3680 if (bestitem)
3682 /*sprintf(updstr, "%s and \"%s\" = %u", updstr, bestitem, oid); */
3683 strcat(updstr, " and ");
3684 sprintf(updstr + strlen(updstr), bestqual, oid);
3686 if (PG_VERSION_GE(conn, 8.2))
3687 strcat(updstr, " returning ctid");
3688 mylog("updstr=%s\n", updstr);
3689 if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
3691 SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR,
3692 "internal AllocStmt error", func);
3693 return SQL_ERROR;
3695 s.qstmt = (StatementClass *) hstmt;
3696 apdopts = SC_get_APDF(s.qstmt);
3697 apdopts->param_bind_type = opts->bind_size;
3698 apdopts->param_offset_ptr = opts->row_offset_ptr;
3699 SC_set_delegate(s.stmt, s.qstmt);
3700 for (i = j = 0; i < num_cols; i++)
3702 if (used = bindings[i].used, used != NULL)
3704 used = LENADDR_SHIFT(used, offset);
3705 if (bind_size > 0)
3706 used = LENADDR_SHIFT(used, bind_size * s.irow);
3707 else
3708 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
3709 mylog("%d used=%d\n", i, *used);
3710 if (*used != SQL_IGNORE && fi[i]->updatable)
3712 fieldtype = QR_get_field_type(s.res, i);
3713 PGAPI_BindParameter(hstmt,
3714 (SQLUSMALLINT)++ j,
3715 SQL_PARAM_INPUT,
3716 bindings[i].returntype,
3717 pgtype_to_concise_type(s.stmt,
3718 fieldtype,
3720 fi[i]->column_size >
3721 0 ? fi[i]->
3722 column_size :
3723 pgtype_column_size(s.stmt,
3724 fieldtype, i,
3725 UNKNOWNS_AS_MAX),
3726 (SQLSMALLINT) fi[i]->
3727 decimal_digits,
3728 bindings[i].buffer,
3729 bindings[i].buflen,
3730 bindings[i].used);
3734 s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
3735 s.updyes = TRUE;
3736 ret = PGAPI_ExecDirect(hstmt, (const UCHAR *)updstr, SQL_NTS, 0);
3737 if (ret == SQL_NEED_DATA)
3739 pup_cdata *cbdata = (pup_cdata *) malloc(sizeof(pup_cdata));
3740 memcpy(cbdata, &s, sizeof(pup_cdata));
3741 enqueueNeedDataCallback(s.stmt, pos_update_callback,
3742 cbdata);
3743 return ret;
3745 /* else if (ret != SQL_SUCCESS) this is unneccesary
3746 SC_error_copy(s.stmt, s.qstmt, TRUE); */
3747 } else
3749 ret = SQL_SUCCESS_WITH_INFO;
3750 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3751 "update list null", func);
3754 ret = pos_update_callback(ret, &s);
3755 return ret;
3758 RETCODE
3759 SC_pos_delete(StatementClass * stmt,
3760 SQLSETPOSIROW irow, SQLULEN global_ridx)
3762 CSTR func = "SC_pos_update";
3763 UWORD offset;
3764 QResultClass *res, *qres;
3765 ConnectionClass *conn = SC_get_conn(stmt);
3766 IRDFields *irdflds = SC_get_IRDF(stmt);
3767 char dltstr[4096];
3768 RETCODE ret;
3769 SQLLEN kres_ridx;
3770 OID oid;
3771 UInt4 blocknum, qflag;
3772 TABLE_INFO *ti;
3773 const char *bestitem;
3774 const char *bestqual;
3776 mylog("POS DELETE ti=%p\n", stmt->ti);
3777 if (!(res = SC_get_Curres(stmt)))
3779 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3780 "Null statement result in SC_pos_delete.", func);
3781 return SQL_ERROR;
3783 if (SC_update_not_ready(stmt))
3784 parse_statement(stmt, TRUE); /* not preferable */
3785 if (!stmt->updatable)
3787 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3788 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3789 "the statement is read-only", func);
3790 return SQL_ERROR;
3792 kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
3793 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
3795 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
3796 "the target rows is out of the rowset", func);
3797 return SQL_ERROR;
3799 ti = stmt->ti[0];
3800 bestitem = GET_NAME(ti->bestitem);
3801 if (!(oid = getOid(res, kres_ridx)))
3803 if (bestitem && !strcmp(bestitem, OID_NAME))
3805 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3806 "the row was already deleted ?", func);
3807 return SQL_ERROR;
3810 bestqual = GET_NAME(ti->bestqual);
3811 getTid(res, kres_ridx, &blocknum, &offset);
3812 /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s", */
3813 if (NAME_IS_VALID(ti->schema_name))
3814 sprintf(dltstr,
3815 "delete from \"%s\".\"%s\" where ctid = '(%u, %u)'",
3816 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name),
3817 blocknum, offset);
3818 else
3819 sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)'",
3820 SAFE_NAME(ti->table_name), blocknum, offset);
3821 if (bestitem)
3823 /*sprintf(dltstr, "%s and \"%s\" = %u", dltstr, bestitem, oid); */
3824 strcat(dltstr, " and ");
3825 sprintf(dltstr + strlen(dltstr), bestqual, oid);
3828 mylog("dltstr=%s\n", dltstr);
3829 qflag = 0;
3830 // VX_CLEANUP: GO_INTO_TRANSACTION doesn't seem to be used elsewhere
3831 qres = CC_send_query(conn, dltstr, NULL, qflag, stmt);
3832 ret = SQL_SUCCESS;
3833 if (QR_command_maybe_successful(qres))
3835 int dltcnt;
3836 const char *cmdstr = QR_get_command(qres);
3838 if (cmdstr && sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
3840 if (dltcnt == 1)
3842 RETCODE tret =
3843 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0,
3844 SQL_DELETE);
3845 if (!SQL_SUCCEEDED(tret))
3846 ret = tret;
3847 } else if (dltcnt == 0)
3849 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3850 "the content was changed before deletion",
3851 func);
3852 ret = SQL_ERROR;
3853 if (stmt->options.cursor_type ==
3854 SQL_CURSOR_KEYSET_DRIVEN)
3855 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
3856 } else
3857 ret = SQL_ERROR;
3858 } else
3859 ret = SQL_ERROR;
3860 } else
3861 ret = SQL_ERROR;
3862 if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
3864 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3865 "SetPos delete return error", func);
3867 if (qres)
3868 QR_Destructor(qres);
3869 if (SQL_SUCCESS == ret && res->keyset)
3871 AddDeleted(res, global_ridx, res->keyset + kres_ridx);
3872 res->keyset[kres_ridx].status &= (~KEYSET_INFO_PUBLIC);
3873 // VX_CLEANUP: It might be possible to extend the carnage from here.
3874 res->keyset[kres_ridx].status |=
3875 (SQL_ROW_DELETED | CURS_SELF_DELETED);
3876 inolog(".status[%d]=%x\n", global_ridx,
3877 res->keyset[kres_ridx].status);
3879 if (irdflds->rowStatusArray)
3881 switch (ret)
3883 case SQL_SUCCESS:
3884 irdflds->rowStatusArray[irow] = SQL_ROW_DELETED;
3885 break;
3886 default:
3887 irdflds->rowStatusArray[irow] = ret;
3890 return ret;
3893 static RETCODE SQL_API
3894 irow_insert(RETCODE ret, StatementClass * stmt, StatementClass * istmt,
3895 SQLLEN addpos)
3897 CSTR func = "irow_insert";
3899 if (ret != SQL_ERROR)
3901 int addcnt;
3902 OID oid, *poid = NULL;
3903 ARDFields *opts = SC_get_ARDF(stmt);
3904 QResultClass *ires = SC_get_Curres(istmt), *tres;
3905 const char *cmdstr;
3906 BindInfoClass *bookmark;
3908 tres = (ires->next ? ires->next : ires);
3909 cmdstr = QR_get_command(tres);
3910 if (cmdstr &&
3911 sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
3912 addcnt == 1)
3914 ConnectionClass *conn = SC_get_conn(stmt);
3915 RETCODE qret;
3917 if (0 != oid)
3918 poid = &oid;
3919 qret = SQL_NO_DATA_FOUND;
3920 if (PG_VERSION_GE(conn, 7.2))
3922 const char *tidval = NULL;
3924 if (NULL != tres->backend_tuples &&
3925 1 == QR_get_num_cached_tuples(tres))
3926 tidval = QR_get_value_backend_text(tres, 0, 0);
3927 qret = SC_pos_newload(stmt, poid, TRUE, tidval);
3928 if (SQL_ERROR == qret)
3929 return qret;
3931 if (SQL_NO_DATA_FOUND == qret)
3933 qret = SC_pos_newload(stmt, poid, FALSE, NULL);
3934 if (SQL_ERROR == qret)
3935 return qret;
3937 bookmark = opts->bookmark;
3938 if (bookmark && bookmark->buffer)
3940 char buf[32];
3941 SQLULEN offset =
3942 opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
3944 snprintf(buf, sizeof(buf), FORMAT_LEN,
3945 SC_make_bookmark(addpos));
3946 SC_set_current_col(stmt, -1);
3947 copy_and_convert_field(stmt,
3948 PG_TYPE_INT4,
3949 buf,
3950 bookmark->returntype,
3951 bookmark->buffer + offset,
3952 bookmark->buflen,
3953 LENADDR_SHIFT(bookmark->used,
3954 offset),
3955 LENADDR_SHIFT(bookmark->used,
3956 offset));
3958 } else
3960 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3961 "SetPos insert return error", func);
3964 return ret;
3967 /* SQL_NEED_DATA callback for SC_pos_add */
3968 typedef struct {
3969 BOOL updyes;
3970 QResultClass *res;
3971 StatementClass *stmt, *qstmt;
3972 IRDFields *irdflds;
3973 SQLSETPOSIROW irow;
3974 } padd_cdata;
3976 static RETCODE pos_add_callback(RETCODE retcode, void *para)
3978 RETCODE ret = retcode;
3979 padd_cdata *s = (padd_cdata *) para;
3980 SQLLEN addpos;
3982 if (s->updyes)
3984 SQLSETPOSIROW brow_save;
3986 mylog("pos_add_callback in ret=%d\n", ret);
3987 brow_save = s->stmt->bind_row;
3988 s->stmt->bind_row = s->irow;
3989 if (QR_get_cursor(s->res))
3990 addpos = -(SQLLEN) (s->res->ad_count + 1);
3991 else
3992 addpos = QR_get_num_total_tuples(s->res);
3993 ret = irow_insert(ret, s->stmt, s->qstmt, addpos);
3994 s->stmt->bind_row = brow_save;
3996 s->updyes = FALSE;
3997 SC_setInsertedTable(s->qstmt, ret);
3998 if (ret != SQL_SUCCESS)
3999 SC_error_copy(s->stmt, s->qstmt, TRUE);
4000 PGAPI_FreeStmt((HSTMT) s->qstmt, SQL_DROP);
4001 s->qstmt = NULL;
4002 if (SQL_SUCCESS == ret && s->res->keyset)
4004 SQLLEN global_ridx = QR_get_num_total_tuples(s->res) - 1;
4005 ConnectionClass *conn = SC_get_conn(s->stmt);
4006 SQLLEN kres_ridx;
4007 UWORD status = SQL_ROW_ADDED;
4009 // VX_CLEANUP: It might be possible to extend the carnage from here.
4010 status |= CURS_SELF_ADDED;
4011 kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, s->res);
4012 if (kres_ridx >= 0 || kres_ridx < s->res->num_cached_keys)
4014 s->res->keyset[kres_ridx].status = status;
4017 if (s->irdflds->rowStatusArray)
4019 switch (ret)
4021 case SQL_SUCCESS:
4022 s->irdflds->rowStatusArray[s->irow] = SQL_ROW_ADDED;
4023 break;
4024 default:
4025 s->irdflds->rowStatusArray[s->irow] = ret;
4029 return ret;
4032 RETCODE SC_pos_add(StatementClass * stmt, SQLSETPOSIROW irow)
4034 CSTR func = "SC_pos_add";
4035 int num_cols, add_cols, i;
4036 HSTMT hstmt;
4038 padd_cdata s;
4039 ConnectionClass *conn;
4040 ConnInfo *ci;
4041 ARDFields *opts = SC_get_ARDF(stmt);
4042 APDFields *apdopts;
4043 BindInfoClass *bindings = opts->bindings;
4044 FIELD_INFO **fi = SC_get_IRDF(stmt)->fi;
4045 char addstr[4096];
4046 RETCODE ret;
4047 SQLULEN offset;
4048 SQLLEN *used;
4049 Int4 bind_size = opts->bind_size;
4050 OID fieldtype;
4051 int func_cs_count = 0;
4053 mylog("POS ADD fi=%p ti=%p\n", fi, stmt->ti);
4054 s.stmt = stmt;
4055 s.irow = irow;
4056 if (!(s.res = SC_get_Curres(s.stmt)))
4058 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4059 "Null statement result in SC_pos_add.", func);
4060 return SQL_ERROR;
4062 if (SC_update_not_ready(stmt))
4063 parse_statement(s.stmt, TRUE); /* not preferable */
4064 if (!s.stmt->updatable)
4066 s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
4067 SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER,
4068 "the statement is read-only", func);
4069 return SQL_ERROR;
4071 s.irdflds = SC_get_IRDF(s.stmt);
4072 num_cols = s.irdflds->nfields;
4073 conn = SC_get_conn(s.stmt);
4074 if (NAME_IS_VALID(s.stmt->ti[0]->schema_name))
4075 sprintf(addstr, "insert into \"%s\".\"%s\" (",
4076 SAFE_NAME(s.stmt->ti[0]->schema_name),
4077 SAFE_NAME(s.stmt->ti[0]->table_name));
4078 else
4079 sprintf(addstr, "insert into \"%s\" (",
4080 SAFE_NAME(s.stmt->ti[0]->table_name));
4081 if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
4083 SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR,
4084 "internal AllocStmt error", func);
4085 return SQL_ERROR;
4087 if (opts->row_offset_ptr)
4088 offset = *opts->row_offset_ptr;
4089 else
4090 offset = 0;
4091 s.qstmt = (StatementClass *) hstmt;
4092 apdopts = SC_get_APDF(s.qstmt);
4093 apdopts->param_bind_type = opts->bind_size;
4094 apdopts->param_offset_ptr = opts->row_offset_ptr;
4095 SC_set_delegate(s.stmt, s.qstmt);
4096 ci = &(conn->connInfo);
4097 for (i = add_cols = 0; i < num_cols; i++)
4099 if (used = bindings[i].used, used != NULL)
4101 used = LENADDR_SHIFT(used, offset);
4102 if (bind_size > 0)
4103 used = LENADDR_SHIFT(used, bind_size * s.irow);
4104 else
4105 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
4106 mylog("%d used=%d\n", i, *used);
4107 if (*used != SQL_IGNORE && fi[i]->updatable)
4109 fieldtype = QR_get_field_type(s.res, i);
4110 if (add_cols)
4111 sprintf(addstr, "%s, \"%s\"", addstr,
4112 GET_NAME(fi[i]->column_name));
4113 else
4114 sprintf(addstr, "%s\"%s\"", addstr,
4115 GET_NAME(fi[i]->column_name));
4116 PGAPI_BindParameter(hstmt, (SQLUSMALLINT)++ add_cols,
4117 SQL_PARAM_INPUT,
4118 bindings[i].returntype,
4119 pgtype_to_concise_type(s.stmt,
4120 fieldtype,
4122 fi[i]->column_size >
4123 0 ? fi[i]->
4124 column_size : pgtype_column_size(s.
4125 stmt, fieldtype,
4126 i, UNKNOWNS_AS_MAX),
4127 (SQLSMALLINT) fi[i]->decimal_digits,
4128 bindings[i].buffer,
4129 bindings[i].buflen,
4130 bindings[i].used);
4132 } else
4133 mylog("%d null bind\n", i);
4135 s.updyes = FALSE;
4136 #define return DONT_CALL_RETURN_FROM_HERE???
4137 ENTER_INNER_CONN_CS(conn, func_cs_count);
4138 if (add_cols > 0)
4140 sprintf(addstr, "%s) values (", addstr);
4141 for (i = 0; i < add_cols; i++)
4143 if (i)
4144 strcat(addstr, ", ?");
4145 else
4146 strcat(addstr, "?");
4148 strcat(addstr, ")");
4149 if (PG_VERSION_GE(conn, 8.2))
4150 strcat(addstr, " returning ctid");
4151 mylog("addstr=%s\n", addstr);
4152 s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
4153 s.updyes = TRUE;
4154 ret = PGAPI_ExecDirect(hstmt, (const UCHAR *)addstr, SQL_NTS, 0);
4155 if (ret == SQL_NEED_DATA)
4157 padd_cdata *cbdata =
4158 (padd_cdata *) malloc(sizeof(padd_cdata));
4159 memcpy(cbdata, &s, sizeof(padd_cdata));
4160 enqueueNeedDataCallback(s.stmt, pos_add_callback, cbdata);
4161 goto cleanup;
4163 /* else if (ret != SQL_SUCCESS) this is unneccesary
4164 SC_error_copy(s.stmt, s.qstmt, TRUE); */
4165 } else
4167 ret = SQL_SUCCESS_WITH_INFO;
4168 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4169 "insert list null", func);
4172 ret = pos_add_callback(ret, &s);
4174 cleanup:
4175 #undef return
4176 CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
4177 return ret;
4181 * Stuff for updatable cursors end.
4184 RETCODE
4185 SC_pos_refresh(StatementClass * stmt, SQLSETPOSIROW irow,
4186 SQLULEN global_ridx)
4188 RETCODE ret;
4189 IRDFields *irdflds = SC_get_IRDF(stmt);
4190 /* save the last_fetch_count */
4191 SQLLEN last_fetch = stmt->last_fetch_count;
4192 SQLLEN last_fetch2 = stmt->last_fetch_count_include_ommitted;
4193 SQLSETPOSIROW bind_save = stmt->bind_row;
4194 BOOL tuple_reload = FALSE;
4196 if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
4197 tuple_reload = TRUE;
4198 else
4200 QResultClass *res = SC_get_Curres(stmt);
4201 if (res && res->keyset)
4203 SQLLEN kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
4204 if (kres_ridx >= 0
4205 && kres_ridx < QR_get_num_cached_tuples(res))
4207 if (0 !=
4208 (CURS_NEEDS_REREAD & res->keyset[kres_ridx].status))
4209 tuple_reload = TRUE;
4213 if (tuple_reload)
4214 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
4215 stmt->bind_row = irow;
4216 ret = SC_fetch(stmt);
4217 /* restore the last_fetch_count */
4218 stmt->last_fetch_count = last_fetch;
4219 stmt->last_fetch_count_include_ommitted = last_fetch2;
4220 stmt->bind_row = bind_save;
4221 if (irdflds->rowStatusArray)
4223 switch (ret)
4225 case SQL_ERROR:
4226 irdflds->rowStatusArray[irow] = SQL_ROW_ERROR;
4227 break;
4228 case SQL_SUCCESS:
4229 irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS;
4230 break;
4231 case SQL_SUCCESS_WITH_INFO:
4232 default:
4233 irdflds->rowStatusArray[irow] = ret;
4234 break;
4238 return SQL_SUCCESS;
4241 /* SQL_NEED_DATA callback for PGAPI_SetPos */
4242 typedef struct {
4243 BOOL need_data_callback, auto_commit_needed;
4244 QResultClass *res;
4245 StatementClass *stmt;
4246 ARDFields *opts;
4247 GetDataInfo *gdata;
4248 SQLLEN idx, start_row, end_row, ridx;
4249 UWORD fOption;
4250 SQLSETPOSIROW irow, nrow, processed;
4251 } spos_cdata;
4252 static
4253 RETCODE spos_callback(RETCODE retcode, void *para)
4255 CSTR func = "spos_callback";
4256 RETCODE ret;
4257 spos_cdata *s = (spos_cdata *) para;
4258 QResultClass *res;
4259 ARDFields *opts;
4260 ConnectionClass *conn;
4261 SQLULEN global_ridx;
4262 SQLLEN kres_ridx, pos_ridx = 0;
4264 ret = retcode;
4265 mylog("%s: %d in\n", func, s->need_data_callback);
4266 if (s->need_data_callback)
4268 s->processed++;
4269 if (SQL_ERROR != retcode)
4271 s->nrow++;
4272 s->idx++;
4274 } else
4276 s->ridx = -1;
4277 s->idx = s->nrow = s->processed = 0;
4279 res = s->res;
4280 opts = s->opts;
4281 if (!res || !opts)
4283 SC_set_error(s->stmt, STMT_SEQUENCE_ERROR,
4284 "Passed res or opts for spos_callback is NULL",
4285 func);
4286 return SQL_ERROR;
4288 s->need_data_callback = FALSE;
4289 for (; SQL_ERROR != ret && s->nrow <= s->end_row; s->idx++)
4291 global_ridx = RowIdx2GIdx(s->idx, s->stmt);
4292 if (SQL_ADD != s->fOption)
4294 if ((int) global_ridx >= QR_get_num_total_tuples(res))
4295 break;
4296 if (res->keyset)
4298 kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, res);
4299 if (kres_ridx >= res->num_cached_keys)
4300 break;
4301 if (kres_ridx >= 0) /* the row may be deleted and not in the rowset */
4303 if (0 ==
4304 (res->keyset[kres_ridx].
4305 status & CURS_IN_ROWSET))
4306 continue;
4310 if (s->nrow < s->start_row)
4312 s->nrow++;
4313 continue;
4315 s->ridx = s->nrow;
4316 pos_ridx = s->idx;
4317 if (0 != s->irow || !opts->row_operation_ptr
4318 || opts->row_operation_ptr[s->nrow] == SQL_ROW_PROCEED)
4320 switch (s->fOption)
4322 case SQL_UPDATE:
4323 ret = SC_pos_update(s->stmt, s->nrow, global_ridx);
4324 break;
4325 case SQL_DELETE:
4326 ret = SC_pos_delete(s->stmt, s->nrow, global_ridx);
4327 break;
4328 case SQL_ADD:
4329 ret = SC_pos_add(s->stmt, s->nrow);
4330 break;
4331 case SQL_REFRESH:
4332 ret = SC_pos_refresh(s->stmt, s->nrow, global_ridx);
4333 break;
4335 if (SQL_NEED_DATA == ret)
4337 spos_cdata *cbdata =
4338 (spos_cdata *) malloc(sizeof(spos_cdata));
4340 memcpy(cbdata, s, sizeof(spos_cdata));
4341 cbdata->need_data_callback = TRUE;
4342 enqueueNeedDataCallback(s->stmt, spos_callback, cbdata);
4343 return ret;
4345 s->processed++;
4347 if (SQL_ERROR != ret)
4348 s->nrow++;
4350 conn = SC_get_conn(s->stmt);
4351 if (s->auto_commit_needed)
4352 PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
4353 if (s->irow > 0)
4355 if (SQL_ADD != s->fOption && s->ridx >= 0) /* for SQLGetData */
4357 s->stmt->currTuple = RowIdx2GIdx(pos_ridx, s->stmt);
4358 QR_set_position(res, pos_ridx);
4360 } else if (SC_get_IRDF(s->stmt)->rowsFetched)
4361 *(SC_get_IRDF(s->stmt)->rowsFetched) = s->processed;
4362 res->recent_processed_row_count = s->stmt->diag_row_count =
4363 s->processed;
4364 if (opts)
4366 inolog("processed=%d ret=%d rowset=%d", s->processed, ret,
4367 opts->size_of_rowset_odbc2);
4368 inolog(",%d\n", opts->size_of_rowset);
4371 return ret;
4375 * This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
4376 * This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
4378 // VX_CLEANUP: In ODBC 3, SQLExtendedFetch has been replaced with
4379 // SQLFetchScroll. Need to check whether either of them works.
4380 RETCODE SQL_API
4381 PGAPI_SetPos(HSTMT hstmt,
4382 SQLSETPOSIROW irow,
4383 SQLUSMALLINT fOption, SQLUSMALLINT fLock)
4385 CSTR func = "PGAPI_SetPos";
4386 RETCODE ret;
4387 ConnectionClass *conn;
4388 SQLLEN rowsetSize;
4389 int i;
4390 UInt2 gdata_allocated;
4391 GetDataInfo *gdata_info;
4392 GetDataClass *gdata = NULL;
4393 spos_cdata s;
4395 s.stmt = (StatementClass *) hstmt;
4396 if (!s.stmt)
4398 SC_log_error(func, NULL_STRING, NULL);
4399 return SQL_INVALID_HANDLE;
4402 s.irow = irow;
4403 s.fOption = fOption;
4404 s.auto_commit_needed = FALSE;
4405 s.opts = SC_get_ARDF(s.stmt);
4406 gdata_info = SC_get_GDTI(s.stmt);
4407 gdata = gdata_info->gdata;
4408 mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, s.fOption,
4409 s.irow, fLock, s.stmt->currTuple);
4410 if (s.stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
4412 else if (s.fOption != SQL_POSITION && s.fOption != SQL_REFRESH)
4414 SC_set_error(s.stmt, STMT_NOT_IMPLEMENTED_ERROR,
4415 "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos",
4416 func);
4417 return SQL_ERROR;
4420 if (!(s.res = SC_get_Curres(s.stmt)))
4422 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4423 "Null statement result in PGAPI_SetPos.", func);
4424 return SQL_ERROR;
4427 rowsetSize =
4428 (s.stmt->transition_status ==
4429 7 ? s.opts->size_of_rowset_odbc2 : s.opts->size_of_rowset);
4430 if (s.irow == 0) /* bulk operation */
4432 if (SQL_POSITION == s.fOption)
4434 SC_set_error(s.stmt, STMT_INVALID_CURSOR_POSITION,
4435 "Bulk Position operations not allowed.", func);
4436 return SQL_ERROR;
4438 s.start_row = 0;
4439 s.end_row = rowsetSize - 1;
4440 } else
4442 if (SQL_ADD != s.fOption && s.irow > s.stmt->last_fetch_count)
4444 SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE,
4445 "Row value out of range", func);
4446 return SQL_ERROR;
4448 s.start_row = s.end_row = s.irow - 1;
4451 gdata_allocated = gdata_info->allocated;
4452 mylog("num_cols=%d gdatainfo=%d\n", QR_NumPublicResultCols(s.res),
4453 gdata_allocated);
4454 /* Reset for SQLGetData */
4455 if (gdata)
4457 for (i = 0; i < gdata_allocated; i++)
4458 gdata[i].data_left = -1;
4460 ret = SQL_SUCCESS;
4461 // VX_CLEANUP: It might be possible to extend the carnage from here.
4463 s.need_data_callback = FALSE;
4464 #define return DONT_CALL_RETURN_FROM_HERE???
4465 /* StartRollbackState(s.stmt); */
4466 ret = spos_callback(SQL_SUCCESS, &s);
4467 #undef return
4468 if (s.stmt->internal)
4469 ret = DiscardStatementSvp(s.stmt, ret, FALSE);
4470 mylog("%s returning %d\n", func, ret);
4471 return ret;
4475 /* Sets options that control the behavior of cursors. */
4476 RETCODE SQL_API
4477 PGAPI_SetScrollOptions(HSTMT hstmt,
4478 SQLUSMALLINT fConcurrency,
4479 SQLLEN crowKeyset, SQLUSMALLINT crowRowset)
4481 CSTR func = "PGAPI_SetScrollOptions";
4482 StatementClass *stmt = (StatementClass *) hstmt;
4484 mylog("%s: fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
4485 func, fConcurrency, crowKeyset, crowRowset);
4486 SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR,
4487 "SetScroll option not implemeted", func);
4489 return SQL_ERROR;
4493 /* Set the cursor name on a statement handle */
4494 RETCODE SQL_API
4495 PGAPI_SetCursorName(HSTMT hstmt,
4496 const SQLCHAR FAR * szCursor, SQLSMALLINT cbCursor)
4498 CSTR func = "PGAPI_SetCursorName";
4499 StatementClass *stmt = (StatementClass *) hstmt;
4501 mylog("%s: hstmt=%p, szCursor=%p, cbCursorMax=%d\n", func, hstmt,
4502 szCursor, cbCursor);
4504 if (!stmt)
4506 SC_log_error(func, NULL_STRING, NULL);
4507 return SQL_INVALID_HANDLE;
4510 SET_NAME(stmt->cursor_name,
4511 make_string(szCursor, cbCursor, NULL, 0));
4512 return SQL_SUCCESS;
4516 /* Return the cursor name for a statement handle */
4517 RETCODE SQL_API
4518 PGAPI_GetCursorName(HSTMT hstmt,
4519 SQLCHAR FAR * szCursor,
4520 SQLSMALLINT cbCursorMax,
4521 SQLSMALLINT FAR * pcbCursor)
4523 CSTR func = "PGAPI_GetCursorName";
4524 StatementClass *stmt = (StatementClass *) hstmt;
4525 size_t len = 0;
4526 RETCODE result;
4528 mylog("%s: hstmt=%p, szCursor=%p, cbCursorMax=%d, pcbCursor=%p\n",
4529 func, hstmt, szCursor, cbCursorMax, pcbCursor);
4531 if (!stmt)
4533 SC_log_error(func, NULL_STRING, NULL);
4534 return SQL_INVALID_HANDLE;
4536 result = SQL_SUCCESS;
4537 len = strlen(SC_cursor_name(stmt));
4539 if (szCursor)
4541 strncpy_null((char *)szCursor, SC_cursor_name(stmt), cbCursorMax);
4543 if (len >= cbCursorMax)
4545 result = SQL_SUCCESS_WITH_INFO;
4546 SC_set_error(stmt, STMT_TRUNCATED,
4547 "The buffer was too small for the GetCursorName.",
4548 func);
4552 if (pcbCursor)
4553 *pcbCursor = (SQLSMALLINT) len;
4556 * Because this function causes no db-access, there's
4557 * no need to call DiscardStatementSvp()
4560 return result;