vxodbc: PGAPI_DescribeCol was setting pcbColName incorrectly.
[versaplex.git] / vxodbc / results.cc
blobc1951e62c4b8b9e9cc534942c7b3be2891e9f7b7
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"
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 SC_set_error(stmt, STMT_EXEC_ERROR,
93 "No query has been executed with that handle",
94 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_INT4:
237 *pfSqlType = SQL_INTEGER;
238 *pcbColDef = pgtype_column_size(stmt, ft, icol, 10);
239 *pibScale = 1;
240 break;
241 case PG_TYPE_NUMERIC:
242 *pfSqlType = SQL_NUMERIC;
243 *pcbColDef = pgtype_column_size(stmt, ft, icol, 10);
244 *pibScale = pgtype_decimal_digits(stmt, ft, icol);
245 break;
246 case PG_TYPE_FLOAT8:
247 *pfSqlType = SQL_DOUBLE;
248 *pcbColDef = pgtype_column_size(stmt, ft, icol, 10);
249 *pibScale = pgtype_precision(stmt, ft, icol, 10);
250 break;
251 case VX_TYPE_DATETIME:
252 *pfSqlType = SQL_VARCHAR;
253 *pcbColDef = 19;
254 *pibScale = 1;
255 break;
256 default:
257 *pfSqlType = SQL_VARCHAR;
258 *pcbColDef = QR_get_fieldsize(res, icol);
259 if (*pcbColDef < 4)
260 *pcbColDef = 4;
261 *pibScale = 1;
262 break;
265 *pfNullable = 1;
267 return SQL_SUCCESS;
270 /* Returns result column descriptor information for a result set. */
271 RETCODE SQL_API
272 PGAPI_ColAttributes(HSTMT hstmt,
273 SQLUSMALLINT icol,
274 SQLUSMALLINT fDescType,
275 PTR rgbDesc,
276 SQLSMALLINT cbDescMax,
277 SQLSMALLINT FAR * pcbDesc, SQLLEN FAR * pfDesc)
279 CSTR func = "PGAPI_ColAttributes";
280 StatementClass *stmt = (StatementClass *) hstmt;
281 IRDFields *irdflds;
282 OID field_type = 0;
283 Int2 col_idx;
284 ConnectionClass *conn;
285 ConnInfo *ci;
286 int unknown_sizes;
287 int cols = 0;
288 RETCODE result;
289 const char *p = NULL;
290 SQLLEN value = 0;
291 const FIELD_INFO *fi = NULL;
292 const TABLE_INFO *ti = NULL;
293 QResultClass *res;
295 mylog("%s: entering..col=%d %d len=%d.\n", func, icol, fDescType,
296 cbDescMax);
298 if (!stmt)
300 SC_log_error(func, NULL_STRING, NULL);
301 return SQL_INVALID_HANDLE;
304 if (pcbDesc)
305 *pcbDesc = 0;
306 irdflds = SC_get_IRDF(stmt);
307 conn = SC_get_conn(stmt);
308 ci = &(conn->connInfo);
311 * Dont check for bookmark column. This is the responsibility of the
312 * driver manager. For certain types of arguments, the column number
313 * is ignored anyway, so it may be 0.
316 res = SC_get_Curres(stmt);
317 if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
319 inolog("answering bookmark info\n");
320 switch (fDescType)
322 case SQL_DESC_OCTET_LENGTH:
323 if (pfDesc)
324 *pfDesc = 4;
325 break;
326 case SQL_DESC_TYPE:
327 if (pfDesc)
328 *pfDesc =
329 stmt->options.use_bookmarks ==
330 SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
331 break;
333 return SQL_SUCCESS;
335 col_idx = icol - 1;
337 /* atoi(ci->unknown_sizes); */
338 unknown_sizes = UNKNOWNS_AS_MAX;
340 /* not appropriate for SQLColAttributes() */
341 if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
342 unknown_sizes = UNKNOWNS_AS_MAX;
344 if (!stmt->catalog_result && SC_is_parse_forced(stmt)
345 && stmt->statement_type == STMT_TYPE_SELECT)
347 if (SC_parsed_status(stmt) == STMT_PARSE_NONE)
349 mylog("%s: calling parse_statement\n", func);
350 parse_statement(stmt, FALSE);
353 cols = irdflds->nfields;
356 * Column Count is a special case. The Column number is ignored
357 * in this case.
359 if (fDescType == SQL_DESC_COUNT)
361 if (pfDesc)
362 *pfDesc = cols;
364 return SQL_SUCCESS;
367 if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi)
369 if (col_idx >= cols)
371 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
372 "Invalid column number in ColAttributes.",
373 func);
374 return SQL_ERROR;
379 if (col_idx < irdflds->nfields && irdflds->fi)
380 fi = irdflds->fi[col_idx];
381 if (FI_is_applicable(fi))
382 field_type = FI_type(fi);
383 else
385 BOOL build_fi = FALSE;
387 fi = NULL;
388 if (PROTOCOL_74(ci))
390 switch (fDescType)
392 case SQL_COLUMN_OWNER_NAME:
393 case SQL_COLUMN_TABLE_NAME:
394 case SQL_COLUMN_TYPE:
395 case SQL_COLUMN_TYPE_NAME:
396 case SQL_COLUMN_AUTO_INCREMENT:
397 case SQL_DESC_NULLABLE:
398 case SQL_DESC_BASE_TABLE_NAME:
399 case SQL_DESC_BASE_COLUMN_NAME:
400 case SQL_COLUMN_UPDATABLE:
401 build_fi = TRUE;
402 break;
405 if (!SC_pre_execute_ok(stmt, build_fi, col_idx, func))
406 return SQL_ERROR;
408 res = SC_get_Curres(stmt);
409 cols = QR_NumPublicResultCols(res);
412 * Column Count is a special case. The Column number is ignored
413 * in this case.
415 if (fDescType == SQL_DESC_COUNT)
417 if (pfDesc)
418 *pfDesc = cols;
420 return SQL_SUCCESS;
423 if (col_idx >= cols)
425 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
426 "Invalid column number in ColAttributes.",
427 func);
428 return SQL_ERROR;
431 field_type = QR_get_field_type(res, col_idx);
432 if (col_idx < irdflds->nfields && irdflds->fi)
433 fi = irdflds->fi[col_idx];
435 if (FI_is_applicable(fi))
437 ti = fi->ti;
438 field_type = FI_type(fi);
441 mylog("colAttr: col %d field_type=%d fi,ti=%p,%p\n", col_idx,
442 field_type, fi, ti);
444 switch (fDescType)
446 case SQL_COLUMN_AUTO_INCREMENT: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
447 if (fi && fi->auto_increment)
448 value = TRUE;
449 else
450 value = pgtype_auto_increment(stmt, field_type);
451 if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
452 value = FALSE;
453 mylog("AUTO_INCREMENT=%d\n", value);
455 break;
457 case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */
458 value = pgtype_case_sensitive(stmt, field_type);
459 break;
462 * This special case is handled above.
464 * case SQL_COLUMN_COUNT:
466 case SQL_COLUMN_DISPLAY_SIZE: /* == SQL_DESC_DISPLAY_SIZE */
467 value = (fi
468 && 0 !=
469 fi->display_size) ? fi->
470 display_size : pgtype_display_size(stmt, field_type,
471 col_idx, unknown_sizes);
473 mylog("%s: col %d, display_size= %d\n", func, col_idx, value);
475 break;
477 case SQL_COLUMN_LABEL: /* == SQL_DESC_LABEL */
478 if (fi && (NAME_IS_VALID(fi->column_alias)))
480 p = GET_NAME(fi->column_alias);
482 mylog("%s: COLUMN_LABEL = '%s'\n", func, p);
483 break;
485 /* otherwise same as column name -- FALL THROUGH!!! */
487 case SQL_DESC_NAME:
488 inolog("fi=%p", fi);
489 if (fi)
490 inolog(" (%s,%s)", PRINT_NAME(fi->column_alias),
491 PRINT_NAME(fi->column_name));
492 p = fi ? (NAME_IS_NULL(fi->column_alias) ?
493 SAFE_NAME(fi->column_name) : GET_NAME(fi->
494 column_alias)) :
495 QR_get_fieldname(res, col_idx);
497 mylog("%s: COLUMN_NAME = '%s'\n", func, p);
498 break;
500 case SQL_COLUMN_LENGTH:
501 value = (fi
502 && fi->length >
503 0) ? fi->length : pgtype_buffer_length(stmt,
504 field_type,
505 col_idx,
506 unknown_sizes);
507 if (0 > value)
508 /* if (-1 == value) I'm not sure which is right */
509 value = 0;
511 mylog("%s: col %d, column_length = %d\n", func, col_idx, value);
512 break;
514 case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
515 value = pgtype_money(stmt, field_type);
516 inolog("COLUMN_MONEY=%d\n", value);
517 break;
519 case SQL_DESC_NULLABLE:
520 if (SC_has_outer_join(stmt))
521 value = TRUE;
522 else
523 value =
524 fi ? fi->nullable : pgtype_nullable(stmt, field_type);
525 inolog("COLUMN_NULLABLE=%d\n", value);
526 break;
528 case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
529 p = ti ? SAFE_NAME(ti->schema_name) : NULL_STRING;
530 mylog("schema_name=%s\n", p);
531 break;
533 case SQL_COLUMN_PRECISION: /* in 2.x */
534 value = (fi
535 && fi->column_size >
536 0) ? fi->column_size : pgtype_column_size(stmt,
537 field_type,
538 col_idx,
539 unknown_sizes);
540 if (value < 0)
541 value = 0;
543 mylog("%s: col %d, column_size = %d\n", func, col_idx, value);
544 break;
546 case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */
547 p = ti ? CurrCatString(conn) : NULL_STRING; /* empty string means *not supported* */
548 break;
550 case SQL_COLUMN_SCALE: /* in 2.x */
551 value = pgtype_decimal_digits(stmt, field_type, col_idx);
552 inolog("COLUMN_SCALE=%d\n", value);
553 if (value < 0)
554 value = 0;
555 break;
557 case SQL_COLUMN_SEARCHABLE: /* == SQL_DESC_SEARCHABLE */
558 value = pgtype_searchable(stmt, field_type);
559 break;
561 case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */
562 p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
564 mylog("%s: TABLE_NAME = '%s'\n", func, p);
565 break;
567 case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
568 value = pgtype_to_concise_type(stmt, field_type, col_idx);
569 mylog("COLUMN_TYPE=%d\n", value);
570 break;
572 case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
573 p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment);
574 break;
576 case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */
577 value = pgtype_unsigned(stmt, field_type);
578 if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
579 value = SQL_TRUE;
581 break;
583 case SQL_COLUMN_UPDATABLE: /* == SQL_DESC_UPDATABLE */
586 * Neither Access or Borland care about this.
588 * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
589 * else
591 value =
592 fi ? (fi->
593 updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY)
594 : (QR_get_attid(res, col_idx) >
595 0 ? SQL_ATTR_WRITE : (PROTOCOL_74(ci) ? SQL_ATTR_READONLY
596 : SQL_ATTR_READWRITE_UNKNOWN));
597 if (SQL_ATTR_READONLY != value)
599 const char *name =
600 fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res,
601 col_idx);
602 if (stricmp(name, OID_NAME) == 0
603 || stricmp(name, "ctid") == 0
604 || stricmp(name, "xmin") == 0)
605 value = SQL_ATTR_READONLY;
606 else if (conn->ms_jet && fi && fi->auto_increment)
607 value = SQL_ATTR_READONLY;
610 mylog("%s: UPDATEABLE = %d\n", func, value);
611 break;
612 case SQL_DESC_BASE_COLUMN_NAME:
614 p = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res,
615 col_idx);
617 mylog("%s: BASE_COLUMN_NAME = '%s'\n", func, p);
618 break;
619 case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */
620 p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
622 mylog("%s: BASE_TABLE_NAME = '%s'\n", func, p);
623 break;
624 case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */
625 value = (fi
626 && fi->length >
627 0) ? fi->length : pgtype_desclength(stmt, field_type,
628 col_idx,
629 unknown_sizes);
630 if (-1 == value)
631 value = 0;
633 mylog("%s: col %d, desc_length = %d\n", func, col_idx, value);
634 break;
635 case SQL_DESC_OCTET_LENGTH:
636 value = (fi
637 && fi->length >
638 0) ? fi->length : pgtype_transfer_octet_length(stmt,
639 field_type,
640 col_idx,
641 unknown_sizes);
642 if (-1 == value)
643 value = 0;
644 mylog("%s: col %d, octet_length = %d\n", func, col_idx, value);
645 break;
646 case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */
647 if (value = FI_precision(fi), value <= 0)
648 value =
649 pgtype_precision(stmt, field_type, col_idx,
650 unknown_sizes);
651 if (value < 0)
652 value = 0;
654 mylog("%s: col %d, desc_precision = %d\n", func, col_idx,
655 value);
656 break;
657 case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */
658 value = pgtype_scale(stmt, field_type, col_idx);
659 if (value < 0)
660 value = 0;
661 break;
662 case SQL_DESC_LOCAL_TYPE_NAME:
663 p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment);
664 break;
665 case SQL_DESC_TYPE:
666 value = pgtype_to_sqldesctype(stmt, field_type, col_idx);
667 break;
668 case SQL_DESC_NUM_PREC_RADIX:
669 value = pgtype_radix(stmt, field_type);
670 break;
671 case SQL_DESC_LITERAL_PREFIX:
672 p = pgtype_literal_prefix(stmt, field_type);
673 break;
674 case SQL_DESC_LITERAL_SUFFIX:
675 p = pgtype_literal_suffix(stmt, field_type);
676 break;
677 case SQL_DESC_UNNAMED:
678 value = (fi && NAME_IS_NULL(fi->column_name)
679 && NAME_IS_NULL(fi->
680 column_alias)) ? SQL_UNNAMED :
681 SQL_NAMED;
682 break;
683 case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
684 SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER,
685 "this request may be for MS SQL Server", func);
686 return SQL_ERROR;
687 default:
688 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
689 "ColAttribute for this type not implemented yet",
690 func);
691 return SQL_ERROR;
694 result = SQL_SUCCESS;
696 if (p)
697 { /* char/binary data */
698 size_t len = strlen(p);
700 if (rgbDesc)
702 strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
704 if (len >= cbDescMax)
706 result = SQL_SUCCESS_WITH_INFO;
707 SC_set_error(stmt, STMT_TRUNCATED,
708 "The buffer was too small for the rgbDesc.",
709 func);
713 if (pcbDesc)
714 *pcbDesc = (SQLSMALLINT) len;
715 } else
717 /* numeric data */
718 if (pfDesc)
719 *pfDesc = value;
722 return result;
726 /* Returns result data for a single column in the current row. */
727 RETCODE SQL_API
728 PGAPI_GetData(HSTMT hstmt,
729 SQLUSMALLINT icol,
730 SQLSMALLINT fCType,
731 PTR rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue)
733 CSTR func = "PGAPI_GetData";
734 QResultClass *res;
735 StatementClass *stmt = (StatementClass *) hstmt;
736 UInt2 num_cols;
737 SQLLEN num_rows;
738 OID field_type;
739 void *value = NULL;
740 RETCODE result = SQL_SUCCESS;
741 char get_bookmark = FALSE;
742 ConnInfo *ci;
743 SQLSMALLINT target_type;
745 mylog("%s: enter, stmt=%p icol=%d\n", func, stmt, icol);
747 if (!stmt)
749 SC_log_error(func, NULL_STRING, NULL);
750 return SQL_INVALID_HANDLE;
752 ci = &(SC_get_conn(stmt)->connInfo);
753 res = SC_get_Curres(stmt);
755 if (STMT_EXECUTING == stmt->status)
757 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
758 "Can't get data while statement is still executing.",
759 func);
760 return SQL_ERROR;
763 if (stmt->status != STMT_FINISHED)
765 SC_set_error(stmt, STMT_STATUS_ERROR,
766 "GetData can only be called after the successful execution on a SQL statement",
767 func);
768 return SQL_ERROR;
771 if (SQL_ARD_TYPE == fCType)
773 ARDFields *opts;
774 BindInfoClass *binfo = NULL;
776 opts = SC_get_ARDF(stmt);
777 if (0 == icol)
778 binfo = opts->bookmark;
779 else if (icol <= opts->allocated && opts->bindings)
780 binfo = &opts->bindings[icol - 1];
781 if (binfo)
783 target_type = binfo->returntype;
784 mylog("SQL_ARD_TYPE=%d\n", target_type);
785 } else
787 SC_set_error(stmt, STMT_STATUS_ERROR,
788 "GetData can't determine the type via ARD",
789 func);
790 return SQL_ERROR;
792 } else
793 target_type = fCType;
794 if (icol == 0)
796 if (stmt->options.use_bookmarks == SQL_UB_OFF)
798 SC_set_error(stmt, STMT_COLNUM_ERROR,
799 "Attempt to retrieve bookmark with bookmark usage disabled",
800 func);
801 return SQL_ERROR;
804 /* Make sure it is the bookmark data type */
805 switch (target_type)
807 case SQL_C_BOOKMARK:
808 case SQL_C_VARBOOKMARK:
809 break;
810 default:
811 inolog
812 ("GetData Column 0 is type %d not of type SQL_C_BOOKMARK",
813 target_type);
814 SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE,
815 "Column 0 is not of type SQL_C_BOOKMARK",
816 func);
817 return SQL_ERROR;
820 get_bookmark = TRUE;
821 } else
823 /* use zero-based column numbers */
824 icol--;
826 /* make sure the column number is valid */
827 num_cols = QR_NumPublicResultCols(res);
828 if (icol >= num_cols)
830 SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
831 "Invalid column number.", func);
832 return SQL_ERROR;
836 #define return DONT_CALL_RETURN_FROM_HERE???
837 /* StartRollbackState(stmt); */
839 /* make sure we're positioned on a valid row */
840 num_rows = QR_get_num_total_tuples(res);
841 if ((stmt->currTuple < 0) || (stmt->currTuple >= num_rows))
843 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
844 "Not positioned on a valid row for GetData.",
845 func);
846 result = SQL_ERROR;
847 goto cleanup;
849 mylog(" num_rows = %d\n", num_rows);
851 if (!get_bookmark)
853 SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
854 value = QR_get_value_backend_row(res, curt, icol);
855 inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple,
856 QR_get_rowstart_in_cache(res),
857 SC_get_rowset_start(stmt));
858 mylog(" value = '%s'\n", value ? value : "(null)");
862 if (get_bookmark)
864 BOOL contents_get = FALSE;
866 if (rgbValue)
868 if (SQL_C_BOOKMARK == target_type || 4 <= cbValueMax)
870 contents_get = TRUE;
871 *((SQLULEN *) rgbValue) = SC_get_bookmark(stmt);
874 if (pcbValue)
875 *pcbValue = sizeof(SQLULEN);
877 if (contents_get)
878 result = SQL_SUCCESS;
879 else
881 SC_set_error(stmt, STMT_TRUNCATED,
882 "The buffer was too small for the GetData.",
883 func);
884 result = SQL_SUCCESS_WITH_INFO;
886 goto cleanup;
889 field_type = QR_get_field_type(res, icol);
891 mylog
892 ("**** %s: icol = %d, target_type = %d, field_type = %d, value = '%s'\n",
893 func, icol, target_type, field_type, value ? value : "(null)");
895 SC_set_current_col(stmt, icol);
897 result = copy_and_convert_field(stmt, field_type, value,
898 target_type, rgbValue, cbValueMax,
899 pcbValue, pcbValue);
901 switch (result)
903 case COPY_OK:
904 result = SQL_SUCCESS;
905 break;
907 case COPY_UNSUPPORTED_TYPE:
908 SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
909 "Received an unsupported type from Postgres.",
910 func);
911 result = SQL_ERROR;
912 break;
914 case COPY_UNSUPPORTED_CONVERSION:
915 SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
916 "Couldn't handle the necessary data type conversion.",
917 func);
918 result = SQL_ERROR;
919 break;
921 case COPY_RESULT_TRUNCATED:
922 SC_set_error(stmt, STMT_TRUNCATED,
923 "The buffer was too small for the GetData.", func);
924 result = SQL_SUCCESS_WITH_INFO;
925 break;
927 case COPY_GENERAL_ERROR: /* error msg already filled in */
928 result = SQL_ERROR;
929 break;
931 case COPY_NO_DATA_FOUND:
932 /* SC_log_error(func, "no data found", stmt); */
933 result = SQL_NO_DATA_FOUND;
934 break;
936 default:
937 SC_set_error(stmt, STMT_INTERNAL_ERROR,
938 "Unrecognized return value from copy_and_convert_field.",
939 func);
940 result = SQL_ERROR;
941 break;
944 cleanup:
945 #undef return
946 if (stmt->internal)
947 result = DiscardStatementSvp(stmt, result, FALSE);
948 return result;
953 * Returns data for bound columns in the current row ("hstmt->iCursor"),
954 * advances the cursor.
956 RETCODE SQL_API PGAPI_Fetch(HSTMT hstmt)
958 CSTR func = "PGAPI_Fetch";
959 StatementClass *stmt = (StatementClass *) hstmt;
960 ARDFields *opts;
961 QResultClass *res;
962 BindInfoClass *bookmark;
963 RETCODE retval = SQL_SUCCESS;
965 mylog("%s: stmt = %p, stmt->result= %p\n", func, stmt,
966 stmt ? SC_get_Curres(stmt) : NULL);
968 if (!stmt)
970 SC_log_error(func, NULL_STRING, NULL);
971 return SQL_INVALID_HANDLE;
974 SC_clear_error(stmt);
976 if (!(res = SC_get_Curres(stmt)))
978 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
979 "Null statement result in PGAPI_Fetch.", func);
980 return SQL_ERROR;
983 /* Not allowed to bind a bookmark column when using SQLFetch. */
984 opts = SC_get_ARDF(stmt);
985 if ((bookmark = opts->bookmark) && bookmark->buffer)
987 SC_set_error(stmt, STMT_COLNUM_ERROR,
988 "Not allowed to bind a bookmark column when using PGAPI_Fetch",
989 func);
990 return SQL_ERROR;
993 if (stmt->status == STMT_EXECUTING)
995 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
996 "Can't fetch while statement is still executing.",
997 func);
998 return SQL_ERROR;
1001 if (stmt->status != STMT_FINISHED)
1003 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
1004 "Fetch can only be called after the successful execution on a SQL statement",
1005 func);
1006 return SQL_ERROR;
1009 if (opts->bindings == NULL)
1011 if (stmt->statement_type != STMT_TYPE_SELECT)
1012 return SQL_NO_DATA_FOUND;
1013 /* just to avoid a crash if the user insists on calling this */
1014 /* function even if SQL_ExecDirect has reported an Error */
1015 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1016 "Bindings were not allocated properly.", func);
1017 return SQL_ERROR;
1019 #define return DONT_CALL_RETURN_FROM_HERE???
1020 /* StartRollbackState(stmt); */
1021 if (stmt->rowset_start < 0)
1022 SC_set_rowset_start(stmt, 0, TRUE);
1023 QR_set_rowset_size(res, 1);
1024 /* QR_inc_rowstart_in_cache(res, stmt->last_fetch_count_include_ommitted); */
1025 SC_inc_rowset_start(stmt, stmt->last_fetch_count_include_ommitted);
1027 retval = SC_fetch(stmt);
1028 #undef return
1029 if (stmt->internal)
1030 retval = DiscardStatementSvp(stmt, retval, FALSE);
1031 return retval;
1034 static RETCODE SQL_API
1035 SC_pos_reload_needed(StatementClass * stmt, SQLULEN req_size,
1036 UDWORD flag);
1037 SQLLEN getNthValid(const QResultClass * res, SQLLEN sta,
1038 UWORD orientation, SQLULEN nth, SQLLEN * nearest)
1040 SQLLEN i, num_tuples = QR_get_num_total_tuples(res), nearp;
1041 SQLULEN count;
1042 KeySet *keyset;
1044 if (!QR_once_reached_eof(res))
1045 num_tuples = INT_MAX;
1046 /* Note that the parameter nth is 1-based */
1047 inolog("get %dth Valid data from %d to %s [dlt=%d]", nth, sta,
1048 orientation == SQL_FETCH_PRIOR ? "backward" : "forward",
1049 res->dl_count);
1050 if (0 == res->dl_count)
1052 if (SQL_FETCH_PRIOR == orientation)
1054 if (sta + 1 >= (SQLLEN) nth)
1056 *nearest = sta + 1 - nth;
1057 return nth;
1059 *nearest = -1;
1060 return -(SQLLEN) (sta + 1);
1061 } else
1063 nearp = sta - 1 + nth;
1064 if (nearp < num_tuples)
1066 *nearest = nearp;
1067 return nth;
1069 *nearest = num_tuples;
1070 return -(SQLLEN) (num_tuples - sta);
1073 count = 0;
1074 if (QR_get_cursor(res))
1076 SQLLEN *deleted = (SQLLEN *)res->deleted;
1078 *nearest = sta - 1 + nth;
1079 if (SQL_FETCH_PRIOR == orientation)
1081 for (i = res->dl_count - 1;
1082 i >= 0 && *nearest <= (SQLLEN) deleted[i]; i--)
1084 inolog("deleted[%d]=%d\n", i, deleted[i]);
1085 if (sta >= (SQLLEN) deleted[i])
1086 (*nearest)--;
1088 inolog("nearest=%d\n", *nearest);
1089 if (*nearest < 0)
1091 *nearest = -1;
1092 count = sta + 1;
1093 } else
1094 return nth;
1095 } else
1097 if (!QR_once_reached_eof(res))
1098 num_tuples = INT_MAX;
1099 for (i = 0;
1100 i < res->dl_count && *nearest >= (SQLLEN) deleted[i];
1101 i++)
1103 if (sta <= (SQLLEN) deleted[i])
1104 (*nearest)++;
1106 if (*nearest >= num_tuples)
1108 *nearest = num_tuples;
1109 count = *nearest - sta;
1110 } else
1111 return nth;
1113 } else if (SQL_FETCH_PRIOR == orientation)
1115 for (i = sta, keyset = res->keyset + sta; i >= 0; i--, keyset--)
1117 if (0 ==
1118 (keyset->
1119 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
1120 CURS_OTHER_DELETED)))
1122 *nearest = i;
1123 inolog(" nearest=%d\n", *nearest);
1124 if (++count == nth)
1125 return count;
1128 *nearest = -1;
1129 } else
1131 for (i = sta, keyset = res->keyset + sta;
1132 i < num_tuples; i++, keyset++)
1134 if (0 ==
1135 (keyset->
1136 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
1137 CURS_OTHER_DELETED)))
1139 *nearest = i;
1140 inolog(" nearest=%d\n", *nearest);
1141 if (++count == nth)
1142 return count;
1145 *nearest = num_tuples;
1147 inolog(" nearest not found\n");
1148 return -(SQLLEN) count;
1151 static void
1152 move_cursor_position_if_needed(StatementClass * self,
1153 QResultClass * res)
1155 SQLLEN move_offset;
1158 * The move direction must be initialized to is_not_moving or
1159 * is_moving_from_the_last in advance.
1161 if (!QR_get_cursor(res))
1163 QR_stop_movement(res); /* for safety */
1164 res->move_offset = 0;
1165 return;
1167 inolog("BASE=%d numb=%d curr=%d cursT=%d\n",
1168 QR_get_rowstart_in_cache(res), res->num_cached_rows,
1169 self->currTuple, res->cursTuple);
1171 /* retrieve "move from the last" case first */
1172 if (QR_is_moving_from_the_last(res))
1174 mylog("must MOVE from the last\n");
1175 if (QR_once_reached_eof(res) || self->rowset_start <= QR_get_num_total_tuples(res)) /* this shouldn't happen */
1176 mylog("strange situation in move from the last\n");
1177 if (0 == res->move_offset)
1178 res->move_offset = INT_MAX - self->rowset_start;
1179 else
1181 inolog("!!move_offset=%d calc=%d\n", res->move_offset,
1182 INT_MAX - self->rowset_start);
1184 return;
1187 /* normal case */
1188 res->move_offset = 0;
1189 move_offset = self->currTuple - res->cursTuple;
1190 if (QR_get_rowstart_in_cache(res) >= 0 &&
1191 QR_get_rowstart_in_cache(res) <= res->num_cached_rows)
1193 QR_set_next_in_cache(res,
1194 (QR_get_rowstart_in_cache(res) <
1195 0) ? 0 : QR_get_rowstart_in_cache(res));
1196 return;
1198 if (0 == move_offset)
1199 return;
1200 if (move_offset > 0)
1202 QR_set_move_forward(res);
1203 res->move_offset = move_offset;
1204 } else
1206 QR_set_move_backward(res);
1207 res->move_offset = -move_offset;
1212 * return NO_DATA_FOUND macros
1213 * save_rowset_start or num_tuples must be defined
1215 #define EXTFETCH_RETURN_BOF(stmt, res) \
1217 inolog("RETURN_BOF\n"); \
1218 SC_set_rowset_start(stmt, -1, TRUE); \
1219 stmt->currTuple = -1; \
1220 /* move_cursor_position_if_needed(stmt, res); */ \
1221 return SQL_NO_DATA_FOUND; \
1223 #define EXTFETCH_RETURN_EOF(stmt, res) \
1225 inolog("RETURN_EOF\n"); \
1226 SC_set_rowset_start(stmt, num_tuples, TRUE); \
1227 stmt->currTuple = -1; \
1228 /* move_cursor_position_if_needed(stmt, res); */ \
1229 return SQL_NO_DATA_FOUND; \
1232 /* This fetchs a block of data (rowset). */
1233 RETCODE SQL_API
1234 PGAPI_ExtendedFetch(HSTMT hstmt,
1235 SQLUSMALLINT fFetchType,
1236 SQLLEN irow,
1237 SQLULEN FAR * pcrow,
1238 SQLUSMALLINT FAR * rgfRowStatus,
1239 SQLLEN bookmark_offset, SQLLEN rowsetSize)
1241 CSTR func = "PGAPI_ExtendedFetch";
1242 StatementClass *stmt = (StatementClass *) hstmt;
1243 ARDFields *opts;
1244 QResultClass *res;
1245 BindInfoClass *bookmark;
1246 SQLLEN num_tuples, i, fc_io;
1247 SQLLEN save_rowset_size, progress_size;
1248 SQLLEN save_rowset_start, rowset_start;
1249 RETCODE result = SQL_SUCCESS;
1250 char truncated, error, should_set_rowset_start = FALSE;
1251 ConnInfo *ci;
1252 SQLLEN currp;
1253 UWORD pstatus;
1254 BOOL currp_is_valid, reached_eof;
1256 mylog("stmt=%p rowsetSize=%d\n", stmt, rowsetSize);
1258 if (!stmt)
1260 SC_log_error(func, NULL_STRING, NULL);
1261 return SQL_INVALID_HANDLE;
1263 ci = &(SC_get_conn(stmt)->connInfo);
1265 /* if (SC_is_fetchcursor(stmt) && !stmt->manual_result) */
1266 if (SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type)
1268 if (fFetchType != SQL_FETCH_NEXT)
1270 SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE,
1271 "The fetch type for PGAPI_ExtendedFetch isn't allowed with ForwardOnly cursor.",
1272 func);
1273 return SQL_ERROR;
1277 SC_clear_error(stmt);
1279 if (!(res = SC_get_Curres(stmt)))
1281 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1282 "Null statement result in PGAPI_ExtendedFetch.",
1283 func);
1284 return SQL_ERROR;
1287 opts = SC_get_ARDF(stmt);
1289 * If a bookmark colunmn is bound but bookmark usage is off, then
1290 * error
1292 if ((bookmark = opts->bookmark) && bookmark->buffer
1293 && stmt->options.use_bookmarks == SQL_UB_OFF)
1295 SC_set_error(stmt, STMT_COLNUM_ERROR,
1296 "Attempt to retrieve bookmark with bookmark usage disabled",
1297 func);
1298 return SQL_ERROR;
1301 if (stmt->status == STMT_EXECUTING)
1303 SC_set_error(stmt, STMT_SEQUENCE_ERROR,
1304 "Can't fetch while statement is still executing.",
1305 func);
1306 return SQL_ERROR;
1309 if (stmt->status != STMT_FINISHED)
1311 SC_set_error(stmt, STMT_STATUS_ERROR,
1312 "ExtendedFetch can only be called after the successful execution on a SQL statement",
1313 func);
1314 return SQL_ERROR;
1317 if (opts->bindings == NULL)
1319 if (stmt->statement_type != STMT_TYPE_SELECT)
1320 return SQL_NO_DATA_FOUND;
1321 /* just to avoid a crash if the user insists on calling this */
1322 /* function even if SQL_ExecDirect has reported an Error */
1323 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
1324 "Bindings were not allocated properly.", func);
1325 return SQL_ERROR;
1328 /* Initialize to no rows fetched */
1329 if (rgfRowStatus)
1330 for (i = 0; i < rowsetSize; i++)
1331 *(rgfRowStatus + i) = SQL_ROW_NOROW;
1333 if (pcrow)
1334 *pcrow = 0;
1336 num_tuples = QR_get_num_total_tuples(res);
1337 reached_eof = QR_once_reached_eof(res) && QR_get_cursor(res);
1339 inolog("num_tuples=%d\n", num_tuples);
1340 /* Save and discard the saved rowset size */
1341 save_rowset_start = SC_get_rowset_start(stmt);
1342 save_rowset_size = stmt->save_rowset_size;
1343 stmt->save_rowset_size = -1;
1344 rowset_start = SC_get_rowset_start(stmt);
1346 QR_stop_movement(res);
1347 res->move_offset = 0;
1348 switch (fFetchType)
1350 case SQL_FETCH_NEXT:
1353 * From the odbc spec... If positioned before the start of the
1354 * RESULT SET, then this should be equivalent to
1355 * SQL_FETCH_FIRST.
1358 progress_size =
1359 (save_rowset_size > 0 ? save_rowset_size : rowsetSize);
1360 if (rowset_start < 0)
1361 SC_set_rowset_start(stmt, 0, TRUE);
1362 else if (res->keyset)
1364 if (stmt->last_fetch_count <= progress_size)
1366 SC_inc_rowset_start(stmt,
1367 stmt->
1368 last_fetch_count_include_ommitted);
1369 progress_size -= stmt->last_fetch_count;
1371 if (progress_size > 0)
1373 if (getNthValid(res, SC_get_rowset_start(stmt),
1374 SQL_FETCH_NEXT, progress_size + 1,
1375 &rowset_start) <= 0)
1377 EXTFETCH_RETURN_EOF(stmt, res)} else
1378 should_set_rowset_start = TRUE;
1380 } else
1381 SC_inc_rowset_start(stmt, progress_size);
1382 mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d, rowst=%d\n",
1383 num_tuples, stmt->currTuple, rowset_start);
1384 break;
1386 case SQL_FETCH_PRIOR:
1387 mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n",
1388 num_tuples, stmt->currTuple);
1391 * From the odbc spec... If positioned after the end of the
1392 * RESULT SET, then this should be equivalent to
1393 * SQL_FETCH_LAST.
1395 if (SC_get_rowset_start(stmt) <= 0)
1397 EXTFETCH_RETURN_BOF(stmt, res)}
1398 if (SC_get_rowset_start(stmt) >= num_tuples)
1400 if (rowsetSize > num_tuples)
1402 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1403 "fetch prior from eof and before the beginning",
1404 func);
1406 SC_set_rowset_start(stmt,
1407 num_tuples <=
1408 0 ? 0 : (num_tuples - rowsetSize),
1409 TRUE);
1410 } else if (QR_haskeyset(res))
1412 if (i =
1413 getNthValid(res, SC_get_rowset_start(stmt) - 1,
1414 SQL_FETCH_PRIOR, rowsetSize, &rowset_start),
1415 i < -1)
1417 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1418 "fetch prior and before the beggining",
1419 func);
1420 SC_set_rowset_start(stmt, 0, TRUE);
1421 } else if (i <= 0)
1423 EXTFETCH_RETURN_BOF(stmt, res)} else
1424 should_set_rowset_start = TRUE;
1425 } else if (SC_get_rowset_start(stmt) < rowsetSize)
1427 SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET,
1428 "fetch prior from eof and before the beggining",
1429 func);
1430 SC_set_rowset_start(stmt, 0, TRUE);
1431 } else
1432 SC_inc_rowset_start(stmt, -rowsetSize);
1433 break;
1435 case SQL_FETCH_FIRST:
1436 mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n",
1437 num_tuples, stmt->currTuple);
1439 SC_set_rowset_start(stmt, 0, TRUE);
1440 break;
1442 case SQL_FETCH_LAST:
1443 mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n",
1444 num_tuples, stmt->currTuple);
1446 if (!reached_eof)
1448 QR_set_move_from_the_last(res);
1449 res->move_offset = rowsetSize;
1451 SC_set_rowset_start(stmt,
1452 num_tuples <=
1453 0 ? 0 : (num_tuples - rowsetSize), TRUE);
1454 break;
1456 case SQL_FETCH_ABSOLUTE:
1457 mylog
1458 ("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n",
1459 num_tuples, stmt->currTuple, irow);
1461 /* Position before result set, but dont fetch anything */
1462 if (irow == 0)
1464 EXTFETCH_RETURN_BOF(stmt, res)}
1465 /* Position before the desired row */
1466 else if (irow > 0)
1468 if (getNthValid(res, 0, SQL_FETCH_NEXT, irow, &rowset_start)
1469 <= 0)
1471 EXTFETCH_RETURN_EOF(stmt, res)} else
1472 should_set_rowset_start = TRUE;
1474 /* Position with respect to the end of the result set */
1475 else
1477 if (getNthValid
1478 (res, num_tuples - 1, SQL_FETCH_PRIOR, -irow,
1479 &rowset_start) <= 0)
1481 EXTFETCH_RETURN_BOF(stmt, res)} else
1483 if (!reached_eof)
1485 QR_set_move_from_the_last(res);
1486 res->move_offset = -irow;
1488 should_set_rowset_start = TRUE;
1491 break;
1493 case SQL_FETCH_RELATIVE:
1496 * Refresh the current rowset -- not currently implemented,
1497 * but lie anyway
1499 if (irow == 0)
1500 break;
1502 if (irow > 0)
1504 if (getNthValid
1505 (res, SC_get_rowset_start(stmt) + 1, SQL_FETCH_NEXT,
1506 irow, &rowset_start) <= 0)
1508 EXTFETCH_RETURN_EOF(stmt, res)} else
1509 should_set_rowset_start = TRUE;
1510 } else
1512 if (getNthValid
1513 (res, SC_get_rowset_start(stmt) - 1, SQL_FETCH_PRIOR,
1514 -irow, &rowset_start) <= 0)
1516 EXTFETCH_RETURN_BOF(stmt, res)} else
1517 should_set_rowset_start = TRUE;
1519 break;
1521 case SQL_FETCH_BOOKMARK:
1523 SQLLEN bidx = SC_resolve_bookmark(irow);
1525 if (bidx < 0)
1527 if (!reached_eof)
1529 QR_set_move_from_the_last(res);
1530 res->move_offset = 1 + res->ad_count + bidx;
1532 bidx = num_tuples - 1 - res->ad_count - bidx;
1535 rowset_start = bidx;
1536 if (bookmark_offset >= 0)
1538 if (getNthValid
1539 (res, bidx, SQL_FETCH_NEXT, bookmark_offset + 1,
1540 &rowset_start) <= 0)
1542 EXTFETCH_RETURN_EOF(stmt, res)} else
1543 should_set_rowset_start = TRUE;
1544 } else
1545 if (getNthValid
1546 (res, bidx, SQL_FETCH_PRIOR, 1 - bookmark_offset,
1547 &rowset_start) <= 0)
1549 stmt->currTuple = -1;
1550 EXTFETCH_RETURN_BOF(stmt, res)} else
1551 should_set_rowset_start = TRUE;
1553 break;
1555 default:
1556 SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE,
1557 "Unsupported PGAPI_ExtendedFetch Direction", func);
1558 return SQL_ERROR;
1562 * CHECK FOR PROPER CURSOR STATE
1566 * Handle Declare Fetch style specially because the end is not really
1567 * the end...
1569 if (!should_set_rowset_start)
1570 rowset_start = SC_get_rowset_start(stmt);
1572 /* If *new* rowset is after the result_set, return no data found */
1573 if (rowset_start >= num_tuples)
1575 EXTFETCH_RETURN_EOF(stmt, res)}
1577 /* If *new* rowset is prior to result_set, return no data found */
1578 if (rowset_start < 0)
1580 if (rowset_start + rowsetSize <= 0)
1582 EXTFETCH_RETURN_BOF(stmt, res)} else
1583 { /* overlap with beginning of result set,
1584 * so get first rowset */
1585 SC_set_rowset_start(stmt, 0, TRUE);
1587 should_set_rowset_start = FALSE;
1589 #define return DONT_CALL_RETURN_FROM_HERE???
1590 /* increment the base row in the tuple cache */
1591 QR_set_rowset_size(res, (Int4) rowsetSize);
1592 /* set the rowset_start if needed */
1593 if (should_set_rowset_start)
1594 SC_set_rowset_start(stmt, rowset_start, TRUE);
1595 /* currTuple is always 1 row prior to the rowset start */
1596 stmt->currTuple = RowIdx2GIdx(-1, stmt);
1598 QR_set_rowstart_in_cache(res, SC_get_rowset_start(stmt));
1600 if (res->keyset && !QR_get_cursor(res))
1602 UDWORD flag = 0;
1603 SQLLEN rowset_end, req_size;
1605 getNthValid(res, rowset_start, SQL_FETCH_NEXT, rowsetSize,
1606 &rowset_end);
1607 req_size = rowset_end - rowset_start + 1;
1608 if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
1610 if (fFetchType != SQL_FETCH_NEXT ||
1611 QR_get_rowstart_in_cache(res) + req_size >
1612 QR_get_num_cached_tuples(res))
1613 flag = 1;
1615 if (SQL_RD_ON == stmt->options.retrieve_data || flag != 0)
1617 SC_pos_reload_needed(stmt, req_size, flag);
1620 /* Physical Row advancement occurs for each row fetched below */
1622 mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
1624 truncated = error = FALSE;
1626 currp = -1;
1627 stmt->bind_row = 0; /* set the binding location */
1628 result = SC_fetch(stmt);
1629 if (SQL_ERROR == result)
1630 goto cleanup;
1631 if (SQL_NO_DATA_FOUND != result && res->keyset)
1633 currp = GIdx2KResIdx(SC_get_rowset_start(stmt), stmt, res);
1634 inolog("currp=%d\n", currp);
1635 if (currp < 0)
1637 result = SQL_ERROR;
1638 mylog("rowset_start=%d but currp=%d\n",
1639 SC_get_rowset_start(stmt), currp);
1640 SC_set_error(stmt, STMT_INTERNAL_ERROR,
1641 "rowset_start not in the keyset", func);
1642 goto cleanup;
1645 for (i = 0, fc_io = 0;
1646 SQL_NO_DATA_FOUND != result && SQL_ERROR != result; currp++)
1648 fc_io++;
1649 currp_is_valid = FALSE;
1650 if (res->keyset)
1652 if (currp < res->num_cached_keys)
1654 currp_is_valid = TRUE;
1655 res->keyset[currp].status &= ~CURS_IN_ROWSET; /* Off the flag first */
1656 } else
1658 mylog("Umm current row is out of keyset\n");
1659 break;
1662 inolog("ExtFetch result=%d\n", result);
1663 if (currp_is_valid && SQL_SUCCESS_WITH_INFO == result
1664 && 0 == stmt->last_fetch_count)
1666 inolog("just skipping deleted row %d\n", currp);
1667 QR_set_rowset_size(res, (Int4) (rowsetSize - i + fc_io));
1668 result = SC_fetch(stmt);
1669 if (SQL_ERROR == result)
1670 break;
1671 continue;
1674 /* Determine Function status */
1675 if (result == SQL_SUCCESS_WITH_INFO)
1676 truncated = TRUE;
1677 else if (result == SQL_ERROR)
1678 error = TRUE;
1680 /* Determine Row Status */
1681 if (rgfRowStatus)
1683 if (result == SQL_ERROR)
1684 *(rgfRowStatus + i) = SQL_ROW_ERROR;
1685 else if (currp_is_valid)
1687 pstatus =
1688 (res->keyset[currp].status & KEYSET_INFO_PUBLIC);
1689 if (pstatus != 0 && pstatus != SQL_ROW_ADDED)
1691 rgfRowStatus[i] = pstatus;
1692 } else
1693 rgfRowStatus[i] = SQL_ROW_SUCCESS;
1694 /* refresh the status */
1695 /* if (SQL_ROW_DELETED != pstatus) */
1696 res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC);
1697 } else
1698 *(rgfRowStatus + i) = SQL_ROW_SUCCESS;
1700 if (SQL_ERROR != result && currp_is_valid)
1701 res->keyset[currp].status |= CURS_IN_ROWSET; /* This is the unique place where the CURS_IN_ROWSET bit is turned on */
1702 i++;
1703 if (i >= rowsetSize)
1704 break;
1705 stmt->bind_row = (SQLSETPOSIROW) i; /* set the binding location */
1706 result = SC_fetch(stmt);
1708 if (SQL_ERROR == result)
1709 goto cleanup;
1711 /* Save the fetch count for SQLSetPos */
1712 stmt->last_fetch_count = i;
1714 currp = KResIdx2GIdx(currp, stmt, res);
1715 stmt->last_fetch_count_include_ommitted = GIdx2RowIdx(currp, stmt);
1717 stmt->last_fetch_count_include_ommitted = fc_io;
1719 /* Reset next binding row */
1720 stmt->bind_row = 0;
1722 /* Move the cursor position to the first row in the result set. */
1723 stmt->currTuple = RowIdx2GIdx(0, stmt);
1725 /* Set the number of rows retrieved */
1726 if (pcrow)
1727 *pcrow = i;
1728 inolog("pcrow=%d\n", i);
1730 if (i == 0)
1731 /* Only DeclareFetch should wind up here */
1732 result = SQL_NO_DATA_FOUND;
1733 else if (error)
1734 result = SQL_ERROR;
1735 else if (truncated)
1736 result = SQL_SUCCESS_WITH_INFO;
1737 else if (SC_get_errornumber(stmt) == STMT_POS_BEFORE_RECORDSET)
1738 result = SQL_SUCCESS_WITH_INFO;
1739 else
1740 result = SQL_SUCCESS;
1742 cleanup:
1743 #undef return
1744 if (stmt->internal)
1745 result = DiscardStatementSvp(stmt, result, FALSE);
1746 return result;
1751 * This determines whether there are more results sets available for
1752 * the "hstmt".
1754 /* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
1755 RETCODE SQL_API PGAPI_MoreResults(HSTMT hstmt)
1757 CSTR func = "PGAPI_MoreResults";
1758 StatementClass *stmt = (StatementClass *) hstmt;
1759 QResultClass *res;
1760 RETCODE ret = SQL_SUCCESS;
1762 mylog("%s: entering...\n", func);
1763 if (stmt && (res = SC_get_Curres(stmt)))
1764 SC_set_Curres(stmt, res->next);
1765 if (res = SC_get_Curres(stmt), res)
1767 SQLSMALLINT num_p;
1769 if (stmt->multi_statement < 0)
1770 PGAPI_NumParams(stmt, &num_p);
1771 if (stmt->multi_statement > 0)
1773 const char *cmdstr;
1775 SC_initialize_cols_info(stmt, FALSE, TRUE);
1776 stmt->statement_type = STMT_TYPE_UNKNOWN;
1777 if (cmdstr = QR_get_command(res), NULL != cmdstr)
1778 stmt->statement_type = statement_type(cmdstr);
1779 stmt->join_info = 0;
1780 SC_clear_parse_method(stmt);
1782 stmt->diag_row_count = res->recent_processed_row_count;
1783 SC_set_rowset_start(stmt, -1, FALSE);
1784 stmt->currTuple = -1;
1785 } else
1787 PGAPI_FreeStmt(hstmt, SQL_CLOSE);
1788 ret = SQL_NO_DATA_FOUND;
1790 mylog("%s: returning %d\n", func, ret);
1791 return ret;
1796 * Stuff for updatable cursors.
1798 static Int2 getNumResultCols(const QResultClass * res)
1800 Int2 res_cols = QR_NumPublicResultCols(res);
1801 return res_cols;
1803 static OID getOid(const QResultClass * res, SQLLEN index)
1805 return res->keyset[index].oid;
1807 static void getTid(const QResultClass * res, SQLLEN index,
1808 UInt4 * blocknum, UInt2 * offset)
1810 *blocknum = res->keyset[index].blocknum;
1811 *offset = res->keyset[index].offset;
1813 static void KeySetSet(const TupleField * tuple, int num_fields,
1814 int num_key_fields, KeySet * keyset)
1816 sscanf((const char *)tuple[num_fields - num_key_fields].value, "(%u,%hu)",
1817 &keyset->blocknum, &keyset->offset);
1818 if (num_key_fields > 1)
1819 sscanf((const char *)tuple[num_fields - 1].value, "%u", &keyset->oid);
1820 else
1821 keyset->oid = 0;
1825 SQLLEN ClearCachedRows(TupleField * tuple, int num_fields,
1826 SQLLEN num_rows)
1828 SQLLEN i;
1830 for (i = 0; i < num_fields * num_rows; i++, tuple++)
1832 if (tuple->value)
1834 inolog("freeing tuple[%d][%d].value=%p\n", i / num_fields,
1835 i % num_fields, tuple->value);
1836 free(tuple->value);
1837 tuple->value = NULL;
1839 tuple->len = -1;
1841 return i;
1844 SQLLEN ReplaceCachedRows(TupleField * otuple, const TupleField * ituple,
1845 int num_fields, SQLLEN num_rows)
1847 SQLLEN i;
1849 inolog("ReplaceCachedRows %p num_fields=%d num_rows=%d\n", otuple,
1850 num_fields, num_rows);
1851 for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
1853 if (otuple->value)
1855 free(otuple->value);
1856 otuple->value = NULL;
1858 if (ituple->value)
1860 otuple->value = strdup((const char *)ituple->value);
1861 inolog("[%d,%d] %s copied\n", i / num_fields,
1862 i % num_fields, otuple->value);
1864 otuple->len = ituple->len;
1866 return i;
1869 static
1870 int MoveCachedRows(TupleField * otuple, TupleField * ituple,
1871 Int2 num_fields, SQLLEN num_rows)
1873 int i;
1875 inolog("MoveCachedRows %p num_fields=%d num_rows=%d\n", otuple,
1876 num_fields, num_rows);
1877 for (i = 0; i < num_fields * num_rows; i++, ituple++, otuple++)
1879 if (otuple->value)
1881 free(otuple->value);
1882 otuple->value = NULL;
1884 if (ituple->value)
1886 otuple->value = ituple->value;
1887 ituple->value = NULL;
1888 inolog("[%d,%d] %s copied\n", i / num_fields,
1889 i % num_fields, otuple->value);
1891 otuple->len = ituple->len;
1892 ituple->len = -1;
1894 return i;
1897 static BOOL tupleExists(const StatementClass * stmt,
1898 const KeySet * keyset)
1900 char selstr[256];
1901 const TABLE_INFO *ti = stmt->ti[0];
1902 QResultClass *res;
1903 RETCODE ret = FALSE;
1905 if (NAME_IS_VALID(ti->schema_name))
1906 snprintf(selstr, sizeof(selstr),
1907 "select 1 from \"%s\".\"%s\" where ctid = '(%d,%d)'",
1908 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name),
1909 keyset->blocknum, keyset->offset);
1910 else
1911 snprintf(selstr, sizeof(selstr),
1912 "select 1 from \"%s\" where ctid = '(%d,%d)'",
1913 SAFE_NAME(ti->table_name), keyset->blocknum,
1914 keyset->offset);
1915 res = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, NULL);
1916 if (QR_command_maybe_successful(res) && 1 == res->num_cached_rows)
1917 ret = TRUE;
1918 QR_Destructor(res);
1919 return ret;
1922 static BOOL enlargeAdded(QResultClass * res, UInt4 number,
1923 const StatementClass * stmt)
1925 UInt4 alloc;
1926 KeySet *added_keyset;
1927 TupleField *added_tuples;
1928 int num_fields = res->num_fields;
1930 alloc = res->ad_alloc;
1931 if (0 == alloc)
1932 alloc = number > 10 ? number : 10;
1933 else
1934 while (alloc < number)
1936 alloc *= 2;
1939 if (alloc <= res->ad_alloc)
1940 return TRUE;
1941 if (added_keyset = (KeySet *)
1942 realloc(res->added_keyset, sizeof(KeySet) * alloc),
1943 !added_keyset)
1945 res->ad_alloc = 0;
1946 return FALSE;
1948 added_tuples = res->added_tuples;
1949 if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
1950 if (added_tuples = (TupleField *)
1951 realloc(res->added_tuples,
1952 sizeof(TupleField) * num_fields * alloc),
1953 !added_tuples)
1955 if (added_keyset)
1956 free(added_keyset);
1957 added_keyset = NULL;
1959 res->added_keyset = added_keyset;
1960 res->added_tuples = added_tuples;
1961 if (!added_keyset)
1963 res->ad_alloc = 0;
1964 return FALSE;
1966 res->ad_alloc = alloc;
1967 return TRUE;
1969 static void AddAdded(StatementClass * stmt, QResultClass * res,
1970 SQLLEN index, const TupleField * tuple_added)
1972 KeySet *added_keyset, *keyset, keys;
1973 TupleField *added_tuples = NULL, *tuple;
1974 UInt4 ad_count;
1975 Int2 num_fields;
1977 if (!res)
1978 return;
1979 num_fields = res->num_fields;
1980 inolog("AddAdded index=%d, tuple=%p, num_fields=%d\n", index,
1981 tuple_added, num_fields);
1982 ad_count = res->ad_count;
1983 res->ad_count++;
1984 if (QR_get_cursor(res))
1985 index = -(SQLLEN) res->ad_count;
1986 if (!tuple_added)
1987 return;
1988 KeySetSet(tuple_added, num_fields + res->num_key_fields,
1989 res->num_key_fields, &keys);
1990 keys.status = SQL_ROW_ADDED;
1991 // VX_CLEANUP: It might be possible to extend the carnage from here.
1992 keys.status |= CURS_SELF_ADDED;
1994 if (!QR_get_cursor(res))
1995 return;
1996 if (ad_count > 0 && 0 == res->ad_alloc)
1997 return;
1998 if (!enlargeAdded(res, ad_count + 1, stmt))
1999 return;
2000 added_keyset = res->added_keyset;
2001 added_tuples = res->added_tuples;
2003 keyset = added_keyset + ad_count;
2004 *keyset = keys;
2005 if (added_tuples)
2007 tuple = added_tuples + num_fields * ad_count;
2008 memset(tuple, 0, sizeof(TupleField) * num_fields);
2009 ReplaceCachedRows(tuple, tuple_added, num_fields, 1);
2013 static void RemoveAdded(QResultClass *, SQLLEN);
2014 static void RemoveUpdated(QResultClass *, SQLLEN);
2015 static void RemoveUpdatedAfterTheKey(QResultClass *, SQLLEN,
2016 const KeySet *);
2017 static void RemoveDeleted(QResultClass *, SQLLEN);
2018 static void RemoveAdded(QResultClass * res, SQLLEN index)
2020 SQLLEN rmidx, mv_count;
2021 Int2 num_fields = res->num_fields;
2022 KeySet *added_keyset;
2023 TupleField *added_tuples;
2025 mylog("RemoveAdded index=%d\n", index);
2026 if (index < 0)
2027 rmidx = -index - 1;
2028 else
2029 rmidx = index - res->num_total_read;
2030 if (rmidx >= res->ad_count)
2031 return;
2032 added_keyset = res->added_keyset + rmidx;
2033 added_tuples = res->added_tuples + num_fields * rmidx;
2034 ClearCachedRows(added_tuples, num_fields, 1);
2035 mv_count = res->ad_count - rmidx - 1;
2036 if (mv_count > 0)
2038 memmove(added_keyset, added_keyset + 1,
2039 mv_count * sizeof(KeySet));
2040 memmove(added_tuples, added_tuples + num_fields,
2041 mv_count * num_fields * sizeof(TupleField));
2043 RemoveDeleted(res, index);
2044 RemoveUpdated(res, index);
2045 res->ad_count--;
2046 mylog("RemoveAdded removed=1 count=%d\n", res->ad_count);
2049 static void CommitAdded(QResultClass * res)
2051 KeySet *added_keyset;
2052 int i;
2053 UWORD status;
2055 mylog("CommitAdded res=%p\n", res);
2056 if (!res || !res->added_keyset)
2057 return;
2058 added_keyset = res->added_keyset;
2059 for (i = res->ad_count - 1; i >= 0; i--)
2061 status = added_keyset[i].status;
2062 if (0 != (status & CURS_SELF_ADDING))
2064 status |= CURS_SELF_ADDED;
2065 status &= ~CURS_SELF_ADDING;
2067 if (0 != (status & CURS_SELF_UPDATING))
2069 status |= CURS_SELF_UPDATED;
2070 status &= ~CURS_SELF_UPDATING;
2072 if (0 != (status & CURS_SELF_DELETING))
2074 status |= CURS_SELF_DELETED;
2075 status &= ~CURS_SELF_DELETING;
2077 if (status != added_keyset[i].status)
2079 inolog("!!Commit Added=%d(%d)\n",
2080 QR_get_num_total_read(res) + i, i);
2081 added_keyset[i].status = status;
2087 int AddDeleted(QResultClass * res, SQLULEN index, KeySet * keyset)
2089 int i;
2090 Int2 dl_count, new_alloc;
2091 SQLULEN *deleted;
2092 KeySet *deleted_keyset;
2093 UWORD status;
2094 Int2 num_fields = res->num_fields;
2096 inolog("AddDeleted %d\n", index);
2097 if (!res)
2098 return FALSE;
2099 dl_count = res->dl_count;
2100 res->dl_count++;
2101 if (!QR_get_cursor(res))
2102 return TRUE;
2103 if (!res->deleted)
2105 dl_count = 0;
2106 new_alloc = 10;
2107 QR_MALLOC_return_with_error(res->deleted, SQLULEN,
2108 sizeof(SQLULEN) * new_alloc, res,
2109 "Deleted index malloc error",
2110 FALSE);
2111 QR_MALLOC_return_with_error(res->deleted_keyset, KeySet,
2112 sizeof(KeySet) * new_alloc, res,
2113 "Deleted keyset malloc error",
2114 FALSE);
2115 deleted = res->deleted;
2116 deleted_keyset = res->deleted_keyset;
2117 res->dl_alloc = new_alloc;
2118 } else
2120 if (dl_count >= res->dl_alloc)
2122 new_alloc = res->dl_alloc * 2;
2123 res->dl_alloc = 0;
2124 QR_REALLOC_return_with_error(res->deleted, SQLULEN,
2125 sizeof(SQLULEN) * new_alloc,
2126 res,
2127 "Dleted index realloc error",
2128 FALSE);
2129 deleted = res->deleted;
2130 QR_REALLOC_return_with_error(res->deleted_keyset, KeySet,
2131 sizeof(KeySet) * new_alloc,
2132 res,
2133 "Dleted KeySet realloc error",
2134 FALSE);
2135 deleted_keyset = res->deleted_keyset;
2136 res->dl_alloc = new_alloc;
2138 /* sort deleted indexes in ascending order */
2139 for (i = 0, deleted = res->deleted, deleted_keyset =
2140 res->deleted_keyset; i < dl_count;
2141 i++, deleted++, deleted_keyset += num_fields)
2143 if (index < *deleted)
2144 break;
2146 memmove(deleted + 1, deleted, sizeof(SQLLEN) * (dl_count - i));
2147 memmove(deleted_keyset + 1, deleted_keyset,
2148 sizeof(KeySet) * (dl_count - i));
2150 *deleted = index;
2151 *deleted_keyset = *keyset;
2152 status = keyset->status;
2153 status &= (~KEYSET_INFO_PUBLIC);
2154 status |= SQL_ROW_DELETED;
2155 // VX_CLEANUP: It might be possible to extend the carnage from here.
2157 status &=
2158 ~(CURS_SELF_ADDING | CURS_SELF_UPDATING |
2159 CURS_SELF_DELETING);
2160 status |= CURS_SELF_DELETED;
2162 deleted_keyset->status = status;
2163 res->dl_count = dl_count + 1;
2165 return TRUE;
2168 static void RemoveDeleted(QResultClass * res, SQLLEN index)
2170 int i, mv_count, rm_count = 0;
2171 SQLLEN pidx, midx;
2172 SQLULEN *deleted, num_read = QR_get_num_total_read(res);
2173 KeySet *deleted_keyset;
2175 mylog("RemoveDeleted index=%d\n", index);
2176 if (index < 0)
2178 midx = index;
2179 pidx = num_read - index - 1;
2180 } else
2182 pidx = index;
2183 if (index >= num_read)
2184 midx = num_read - index - 1;
2185 else
2186 midx = index;
2188 for (i = 0; i < res->dl_count; i++)
2190 if (pidx == res->deleted[i] || midx == res->deleted[i])
2192 mv_count = res->dl_count - i - 1;
2193 if (mv_count > 0)
2195 deleted = res->deleted + i;
2196 deleted_keyset = res->deleted_keyset + i;
2197 memmove(deleted, deleted + 1,
2198 mv_count * sizeof(SQLULEN));
2199 memmove(deleted_keyset, deleted_keyset + 1,
2200 mv_count * sizeof(KeySet));
2202 res->dl_count--;
2203 rm_count++;
2206 mylog("RemoveDeleted removed count=%d,%d\n", rm_count,
2207 res->dl_count);
2210 static void CommitDeleted(QResultClass * res)
2212 int i;
2213 SQLULEN *deleted;
2214 KeySet *deleted_keyset;
2215 UWORD status;
2217 if (!res->deleted)
2218 return;
2220 for (i = 0, deleted = res->deleted, deleted_keyset =
2221 res->deleted_keyset; i < res->dl_count;
2222 i++, deleted++, deleted_keyset++)
2224 status = deleted_keyset->status;
2225 if (0 != (status & CURS_SELF_ADDING))
2227 status |= CURS_SELF_ADDED;
2228 status &= ~CURS_SELF_ADDING;
2230 if (0 != (status & CURS_SELF_UPDATING))
2232 status |= CURS_SELF_UPDATED;
2233 status &= ~CURS_SELF_UPDATING;
2235 if (0 != (status & CURS_SELF_DELETING))
2237 status |= CURS_SELF_DELETED;
2238 status &= ~CURS_SELF_DELETING;
2240 if (status != deleted_keyset->status)
2242 inolog("!!Commit Deleted=%d(%d)\n", *deleted, i);
2243 deleted_keyset->status = status;
2248 static BOOL enlargeUpdated(QResultClass * res, Int4 number,
2249 const StatementClass * stmt)
2251 Int2 alloc;
2252 SQLULEN *updated;
2253 KeySet *updated_keyset;
2254 TupleField *updated_tuples = NULL;
2256 alloc = res->up_alloc;
2257 if (0 == alloc)
2258 alloc = number > 10 ? number : 10;
2259 else
2260 while (alloc < number)
2262 alloc *= 2;
2264 if (alloc <= res->up_alloc)
2265 return TRUE;
2267 if (updated = (SQLUINTEGER *)
2268 realloc(res->updated, sizeof(UInt4) * alloc), !updated)
2270 if (res->updated_keyset)
2272 free(res->updated_keyset);
2273 res->updated_keyset = NULL;
2275 res->up_alloc = 0;
2276 return FALSE;
2278 if (updated_keyset = (KeySet *)
2279 realloc(res->updated_keyset, sizeof(KeySet) * alloc),
2280 !updated_keyset)
2282 free(res->updated);
2283 res->updated = NULL;
2284 res->up_alloc = 0;
2285 return FALSE;
2287 if (SQL_CURSOR_KEYSET_DRIVEN != stmt->options.cursor_type)
2288 if (updated_tuples = (TupleField *)
2289 realloc(res->updated_tuples,
2290 sizeof(TupleField) * res->num_fields * alloc),
2291 !updated_tuples)
2293 free(res->updated);
2294 res->updated = NULL;
2295 free(res->updated_keyset);
2296 res->updated_keyset = NULL;
2297 res->up_alloc = 0;
2298 return FALSE;
2300 res->updated = updated;
2301 res->updated_keyset = updated_keyset;
2302 res->updated_tuples = updated_tuples;
2303 res->up_alloc = alloc;
2305 return TRUE;
2308 static void AddUpdated(StatementClass * stmt, SQLLEN index)
2310 QResultClass *res;
2311 SQLULEN *updated;
2312 KeySet *updated_keyset, *keyset;
2313 TupleField *updated_tuples = NULL, *tuple_updated, *tuple;
2314 SQLULEN kres_ridx;
2315 UInt2 up_count;
2316 SQLLEN upd_idx, upd_add_idx;
2317 Int2 num_fields;
2318 int i;
2319 UWORD status;
2321 inolog("AddUpdated index=%d\n", index);
2322 if (!stmt)
2323 return;
2324 if (res = SC_get_Curres(stmt), !res)
2325 return;
2326 if (!res->keyset)
2327 return;
2328 kres_ridx = GIdx2KResIdx(index, stmt, res);
2329 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
2330 return;
2331 keyset = res->keyset + kres_ridx;
2332 if (!QR_get_cursor(res))
2333 return;
2334 up_count = res->up_count;
2335 if (up_count > 0 && 0 == res->up_alloc)
2336 return;
2337 num_fields = res->num_fields;
2338 tuple_updated = res->backend_tuples + kres_ridx * num_fields;
2339 if (!tuple_updated)
2340 return;
2341 upd_idx = -1;
2342 upd_add_idx = -1;
2343 updated = res->updated;
2344 updated_keyset = res->updated_keyset;
2345 status = keyset->status;
2346 status &= (~KEYSET_INFO_PUBLIC);
2347 status |= SQL_ROW_UPDATED;
2349 for (i = up_count - 1; i >= 0; i--)
2351 if (updated[i] == index)
2352 break;
2354 if (i >= 0)
2355 upd_idx = i;
2356 else
2358 SQLLEN num_totals = QR_get_num_total_tuples(res);
2359 if (index >= num_totals)
2360 upd_add_idx = num_totals - index;
2362 status |= CURS_SELF_UPDATED;
2363 status &=
2364 ~(CURS_SELF_ADDING | CURS_SELF_UPDATING |
2365 CURS_SELF_DELETING);
2367 tuple = NULL;
2368 /* update the corresponding add(updat)ed info */
2369 if (upd_add_idx >= 0)
2371 res->added_keyset[upd_add_idx].status = status;
2372 if (res->added_tuples)
2374 tuple = res->added_tuples + num_fields * upd_add_idx;
2375 ClearCachedRows(tuple, num_fields, 1);
2377 } else if (upd_idx >= 0)
2379 res->updated_keyset[upd_idx].status = status;
2380 if (res->updated_tuples)
2382 tuple = res->added_tuples + num_fields * upd_add_idx;
2383 ClearCachedRows(tuple, num_fields, 1);
2385 } else
2387 if (!enlargeUpdated(res, res->up_count + 1, stmt))
2388 return;
2389 updated = res->updated;
2390 updated_keyset = res->updated_keyset;
2391 updated_tuples = res->updated_tuples;
2392 upd_idx = up_count;
2393 updated[up_count] = index;
2394 updated_keyset[up_count] = *keyset;
2395 updated_keyset[up_count].status = status;
2396 if (updated_tuples)
2398 tuple = updated_tuples + num_fields * up_count;
2399 memset(tuple, 0, sizeof(TupleField) * num_fields);
2401 res->up_count++;
2404 if (tuple)
2405 ReplaceCachedRows(tuple, tuple_updated, num_fields, 1);
2406 mylog("up_count=%d\n", res->up_count);
2409 static void RemoveUpdated(QResultClass * res, SQLLEN index)
2411 mylog("RemoveUpdated index=%d\n", index);
2412 RemoveUpdatedAfterTheKey(res, index, NULL);
2415 static void RemoveUpdatedAfterTheKey(QResultClass * res, SQLLEN index,
2416 const KeySet * keyset)
2418 SQLULEN *updated, num_read = QR_get_num_total_read(res);
2419 KeySet *updated_keyset;
2420 TupleField *updated_tuples = NULL;
2421 SQLLEN pidx, midx, mv_count;
2422 int i, num_fields = res->num_fields, rm_count = 0;
2424 mylog("RemoveUpdatedAfterTheKey %d,(%d,%d)\n", index,
2425 keyset ? keyset->blocknum : 0, keyset ? keyset->offset : 0);
2426 if (index < 0)
2428 midx = index;
2429 pidx = num_read - index - 1;
2430 } else
2432 pidx = index;
2433 if (index >= num_read)
2434 midx = num_read - index - 1;
2435 else
2436 midx = index;
2438 for (i = 0; i < res->up_count; i++)
2440 updated = res->updated + i;
2441 if (pidx == *updated || midx == *updated)
2443 updated_keyset = res->updated_keyset + i;
2444 if (keyset &&
2445 updated_keyset->blocknum == keyset->blocknum &&
2446 updated_keyset->offset == keyset->offset)
2447 break;
2448 updated_tuples = NULL;
2449 if (res->updated_tuples)
2451 updated_tuples = res->updated_tuples + i * num_fields;
2452 ClearCachedRows(updated_tuples, num_fields, 1);
2454 mv_count = res->up_count - i - 1;
2455 if (mv_count > 0)
2457 memmove(updated, updated + 1,
2458 sizeof(SQLULEN) * mv_count);
2459 memmove(updated_keyset, updated_keyset + 1,
2460 sizeof(KeySet) * mv_count);
2461 if (updated_tuples)
2462 memmove(updated_tuples, updated_tuples + num_fields,
2463 sizeof(TupleField) * num_fields * mv_count);
2465 res->up_count--;
2466 rm_count++;
2469 mylog("RemoveUpdatedAfter removed count=%d,%d\n", rm_count,
2470 res->up_count);
2473 static void CommitUpdated(QResultClass * res)
2475 KeySet *updated_keyset;
2476 int i;
2477 UWORD status;
2479 mylog("CommitUpdated res=%p\n", res);
2480 if (!res)
2481 return;
2482 if (!QR_get_cursor(res))
2483 return;
2484 if (res->up_count <= 0)
2485 return;
2486 if (updated_keyset = res->updated_keyset, !updated_keyset)
2487 return;
2488 for (i = res->up_count - 1; i >= 0; i--)
2490 status = updated_keyset[i].status;
2491 if (0 != (status & CURS_SELF_UPDATING))
2493 status &= ~CURS_SELF_UPDATING;
2494 status |= CURS_SELF_UPDATED;
2496 if (0 != (status & CURS_SELF_ADDING))
2498 status &= ~CURS_SELF_ADDING;
2499 status |= CURS_SELF_ADDED;
2501 if (0 != (status & CURS_SELF_DELETING))
2503 status &= ~CURS_SELF_DELETING;
2504 status |= CURS_SELF_DELETED;
2506 if (status != updated_keyset[i].status)
2508 inolog("!!Commit Updated=%d(%d)\n", res->updated[i], i);
2509 updated_keyset[i].status = status;
2515 static void DiscardRollback(StatementClass * stmt, QResultClass * res)
2517 int i;
2518 SQLLEN index, kres_ridx;
2519 UWORD status;
2520 Rollback *rollback;
2521 KeySet *keyset;
2522 BOOL kres_is_valid;
2524 inolog("DiscardRollback");
2525 if (QR_get_cursor(res))
2527 CommitAdded(res);
2528 CommitUpdated(res);
2529 CommitDeleted(res);
2530 return;
2533 if (0 == res->rb_count || NULL == res->rollback)
2534 return;
2535 rollback = res->rollback;
2536 keyset = res->keyset;
2537 for (i = 0; i < res->rb_count; i++)
2539 index = rollback[i].index;
2540 status = 0;
2541 kres_is_valid = FALSE;
2542 if (index >= 0)
2544 kres_ridx = GIdx2KResIdx(index, stmt, res);
2545 if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
2547 kres_is_valid = TRUE;
2548 status = keyset[kres_ridx].status;
2551 if (kres_is_valid)
2553 keyset[kres_ridx].status &=
2554 ~(CURS_SELF_DELETING | CURS_SELF_UPDATING |
2555 CURS_SELF_ADDING);
2556 keyset[kres_ridx].status |=
2557 ((status &
2558 (CURS_SELF_DELETING | CURS_SELF_UPDATING |
2559 CURS_SELF_ADDING)) << 3);
2562 free(rollback);
2563 res->rollback = NULL;
2564 res->rb_count = res->rb_alloc = 0;
2567 static QResultClass *positioned_load(StatementClass * stmt, UInt4 flag,
2568 const UInt4 * oidint,
2569 const char *tid);
2570 static void UndoRollback(StatementClass * stmt, QResultClass * res,
2571 BOOL partial)
2573 Int4 i, rollbp;
2574 SQLLEN index, ridx, kres_ridx;
2575 UWORD status;
2576 Rollback *rollback;
2577 KeySet *keyset, keys, *wkey = NULL;
2578 BOOL curs = (NULL != QR_get_cursor(res)), texist, kres_is_valid;
2580 if (0 == res->rb_count || NULL == res->rollback)
2581 return;
2582 rollback = res->rollback;
2583 keyset = res->keyset;
2585 rollbp = 0;
2586 if (partial)
2588 SQLLEN pidx, midx;
2589 Int2 doubtp, rollbps;
2590 int j;
2592 rollbps = rollbp = res->rb_count;
2593 for (i = 0, doubtp = 0; i < res->rb_count; i++)
2595 index = rollback[i].index;
2596 keys.blocknum = rollback[i].blocknum;
2597 keys.offset = rollback[i].offset;
2598 texist = tupleExists(stmt, &keys);
2599 inolog("texist[%d]=%d", i, texist);
2600 if (SQL_ADD == rollback[i].option)
2602 if (texist)
2603 doubtp = i + 1;
2604 } else if (SQL_REFRESH == rollback[i].option)
2606 if (texist || doubtp == i)
2607 doubtp = i + 1;
2608 } else
2610 if (texist)
2611 break;
2612 if (doubtp == i)
2613 doubtp = i + 1;
2615 inolog(" doubtp=%d\n", doubtp);
2617 rollbp = i;
2618 inolog(" doubtp=%d,rollbp=%d\n", doubtp, rollbp);
2619 if (doubtp < 0)
2620 doubtp = 0;
2623 rollbps = rollbp;
2624 for (i = doubtp; i < rollbp; i++)
2626 index = rollback[i].index;
2627 if (SQL_ADD == rollback[i].option)
2629 inolog("index[%d]=%d\n", i, index);
2630 if (index < 0)
2632 midx = index;
2633 pidx = res->num_total_read - index - 1;
2634 } else
2636 pidx = index;
2637 midx = res->num_total_read - index - 1;
2639 inolog("pidx=%d,midx=%d\n", pidx, midx);
2640 for (j = rollbp - 1; j > i; j--)
2642 if (rollback[j].index == midx ||
2643 rollback[j].index == pidx)
2645 if (SQL_DELETE == rollback[j].option)
2647 inolog("delete[%d].index=%d\n", j,
2648 rollback[j].index);
2649 break;
2653 if (j <= i)
2655 rollbp = i;
2656 break;
2661 while (rollbp < rollbps);
2663 inolog("rollbp=%d\n", rollbp);
2665 for (i = res->rb_count - 1; i >= rollbp; i--)
2667 inolog("UndoRollback %d(%d)\n", i, rollback[i].option);
2668 index = rollback[i].index;
2669 if (curs)
2671 if (SQL_ADD == rollback[i].option)
2672 RemoveAdded(res, index);
2673 RemoveDeleted(res, index);
2674 keys.blocknum = rollback[i].blocknum;
2675 keys.offset = rollback[i].offset;
2676 RemoveUpdatedAfterTheKey(res, index, &keys);
2678 status = 0;
2679 kres_is_valid = FALSE;
2680 if (index >= 0)
2682 kres_ridx = GIdx2KResIdx(index, stmt, res);
2683 if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
2685 kres_is_valid = TRUE;
2686 wkey = keyset + kres_ridx;
2687 status = wkey->status;
2690 inolog(" index=%d status=%hx", index, status);
2691 if (kres_is_valid)
2693 QResultClass *qres;
2694 Int2 num_fields = res->num_fields;
2696 ridx = GIdx2CacheIdx(index, stmt, res);
2697 if (SQL_ADD == rollback[i].option)
2699 if (ridx >= 0 && ridx < res->num_cached_rows)
2701 TupleField *tuple =
2702 res->backend_tuples + res->num_fields * ridx;
2703 ClearCachedRows(tuple, res->num_fields, 1);
2704 res->num_cached_rows--;
2706 res->num_cached_keys--;
2707 if (!curs)
2708 res->ad_count--;
2709 } else if (SQL_REFRESH == rollback[i].option)
2710 continue;
2711 else
2713 inolog(" (%u, %u)", wkey->blocknum, wkey->offset);
2714 wkey->blocknum = rollback[i].blocknum;
2715 wkey->offset = rollback[i].offset;
2716 inolog("->(%u, %u)\n", wkey->blocknum, wkey->offset);
2717 wkey->status &= ~KEYSET_INFO_PUBLIC;
2718 if (SQL_DELETE == rollback[i].option)
2719 wkey->status &= ~CURS_SELF_DELETING;
2720 else if (SQL_UPDATE == rollback[i].option)
2721 wkey->status &= ~CURS_SELF_UPDATING;
2722 wkey->status |= CURS_NEEDS_REREAD;
2723 if (ridx >= 0 && ridx < res->num_cached_rows)
2725 char tidval[32];
2727 sprintf(tidval, "(%d,%d)", wkey->blocknum,
2728 wkey->offset);
2729 qres = positioned_load(stmt, 0, NULL, tidval);
2730 if (QR_command_maybe_successful(qres) &&
2731 QR_get_num_cached_tuples(qres) == 1)
2733 MoveCachedRows(res->backend_tuples +
2734 num_fields * ridx,
2735 qres->backend_tuples, num_fields,
2737 wkey->status &= ~CURS_NEEDS_REREAD;
2739 QR_Destructor(qres);
2744 res->rb_count = rollbp;
2745 if (0 == rollbp)
2747 free(rollback);
2748 res->rollback = NULL;
2749 res->rb_alloc = 0;
2753 void ProcessRollback(ConnectionClass * conn, BOOL undo, BOOL partial)
2755 int i;
2756 StatementClass *stmt;
2757 QResultClass *res;
2759 for (i = 0; i < conn->num_stmts; i++)
2761 if (stmt = conn->stmts[i], !stmt)
2762 continue;
2763 for (res = SC_get_Result(stmt); res; res = res->next)
2765 if (undo)
2766 UndoRollback(stmt, res, partial);
2767 else
2768 DiscardRollback(stmt, res);
2774 #define LATEST_TUPLE_LOAD 1L
2775 #define USE_INSERTED_TID (1L << 1)
2776 static QResultClass *positioned_load(StatementClass * stmt, UInt4 flag,
2777 const UInt4 * oidint,
2778 const char *tidval)
2780 CSTR func = "positioned_load";
2781 CSTR andqual = " and ";
2782 QResultClass *qres = NULL;
2783 char *selstr, oideqstr[256];
2784 BOOL latest = ((flag & LATEST_TUPLE_LOAD) != 0);
2785 size_t len;
2786 TABLE_INFO *ti = stmt->ti[0];
2787 const char *bestitem = GET_NAME(ti->bestitem);
2788 const char *bestqual = GET_NAME(ti->bestqual);
2790 inolog("%s bestitem=%s bestqual=%s\n", func,
2791 SAFE_NAME(ti->bestitem), SAFE_NAME(ti->bestqual));
2792 if (!bestitem || !oidint)
2793 *oideqstr = '\0';
2794 else
2796 /*snprintf(oideqstr, sizeof(oideqstr), " and \"%s\" = %u", bestitem, oid); */
2797 strcpy(oideqstr, andqual);
2798 sprintf(oideqstr + strlen(andqual), bestqual, *oidint);
2800 len = strlen(stmt->load_statement);
2801 len += strlen(oideqstr);
2802 if (tidval)
2803 len += 100;
2804 else if ((flag & USE_INSERTED_TID) != 0)
2805 len += 50;
2806 else
2807 len += 20;
2808 selstr = (char *)malloc(len);
2809 if (tidval)
2811 if (latest)
2813 if (NAME_IS_VALID(ti->schema_name))
2814 snprintf(selstr, len,
2815 "%s where ctid = currtid2('\"%s\".\"%s\"', '%s') %s",
2816 stmt->load_statement,
2817 SAFE_NAME(ti->schema_name),
2818 SAFE_NAME(ti->table_name), tidval, oideqstr);
2819 else
2820 snprintf(selstr, len,
2821 "%s where ctid = currtid2('%s', '%s') %s",
2822 stmt->load_statement,
2823 SAFE_NAME(ti->table_name), tidval, oideqstr);
2824 } else
2825 snprintf(selstr, len, "%s where ctid = '%s' %s",
2826 stmt->load_statement, tidval, oideqstr);
2827 } else if ((flag & USE_INSERTED_TID) != 0)
2828 snprintf(selstr, len, "%s where ctid = currtid(0, '(0,0)') %s",
2829 stmt->load_statement, oideqstr);
2830 else if (bestitem && oidint)
2832 /*snprintf(selstr, len, "%s where \"%s\" = %u", stmt->load_statement, bestitem, *oid); */
2833 snprintf(selstr, len, "%s where ", stmt->load_statement);
2834 snprintf_add(selstr, len, bestqual, *oidint);
2835 } else
2837 SC_set_error(stmt, STMT_INTERNAL_ERROR,
2838 "can't find the add and updating row because of the lack of oid",
2839 func);
2840 goto cleanup;
2843 mylog("selstr=%s\n", selstr);
2844 qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, 0, stmt);
2845 cleanup:
2846 free(selstr);
2847 return qres;
2850 static RETCODE
2851 SC_pos_reload_with_tid(StatementClass * stmt, SQLULEN global_ridx,
2852 UInt2 * count, Int4 logKind, const char *tid)
2854 CSTR func = "SC_pos_reload";
2855 int res_cols;
2856 UInt2 offset;
2857 UInt2 rcnt;
2858 SQLLEN res_ridx, kres_ridx;
2859 OID oidint;
2860 UInt4 blocknum;
2861 QResultClass *res, *qres;
2862 IRDFields *irdflds = SC_get_IRDF(stmt);
2863 RETCODE ret = SQL_ERROR;
2864 char tidval[32];
2865 BOOL use_ctid = TRUE, data_in_cache = TRUE, key_in_cache = TRUE;
2867 mylog("positioned load fi=%p ti=%p\n", irdflds->fi, stmt->ti);
2868 rcnt = 0;
2869 if (count)
2870 *count = 0;
2871 if (!(res = SC_get_Curres(stmt)))
2873 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
2874 "Null statement result in SC_pos_reload.", func);
2875 return SQL_ERROR;
2877 res_ridx = GIdx2CacheIdx(global_ridx, stmt, res);
2878 if (res_ridx < 0 || res_ridx >= QR_get_num_cached_tuples(res))
2880 data_in_cache = FALSE;
2881 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
2882 "the target rows is out of the rowset", func);
2883 return SQL_ERROR;
2885 kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
2886 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
2888 key_in_cache = FALSE;
2889 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
2890 "the target rows is out of the rowset", func);
2891 return SQL_ERROR;
2892 } else if (0 != (res->keyset[kres_ridx].status & CURS_SELF_ADDING))
2894 if (NULL == tid)
2896 use_ctid = FALSE;
2897 mylog
2898 ("The tuple is currently being added and can't use ctid\n");
2902 if (SC_update_not_ready(stmt))
2903 parse_statement(stmt, TRUE); /* not preferable */
2904 if (!stmt->updatable)
2906 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2907 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
2908 "the statement is read-only", func);
2909 return SQL_ERROR;
2911 if (!(oidint = getOid(res, kres_ridx)))
2913 if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
2915 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
2916 "the row was already deleted ?", func);
2917 return SQL_SUCCESS_WITH_INFO;
2920 getTid(res, kres_ridx, &blocknum, &offset);
2921 sprintf(tidval, "(%u, %u)", blocknum, offset);
2922 res_cols = getNumResultCols(res);
2923 if (tid)
2924 qres = positioned_load(stmt, 0, &oidint, tid);
2925 else
2926 qres =
2927 positioned_load(stmt, use_ctid ? LATEST_TUPLE_LOAD : 0,
2928 &oidint, use_ctid ? tidval : NULL);
2929 if (!QR_command_maybe_successful(qres))
2931 ret = SQL_ERROR;
2932 SC_replace_error_with_res(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
2933 "positioned_load failed", qres, TRUE);
2934 } else
2936 TupleField *tuple_old, *tuple_new;
2937 ConnectionClass *conn = SC_get_conn(stmt);
2939 rcnt = (UInt2) QR_get_num_cached_tuples(qres);
2940 tuple_old = res->backend_tuples + res->num_fields * res_ridx;
2941 // VX_CLEANUP: It might be possible to extend the carnage from here.
2942 if (rcnt == 1)
2944 int effective_fields = res_cols;
2946 QR_set_position(qres, 0);
2947 tuple_new = qres->tupleField;
2948 if (res->keyset && key_in_cache)
2950 if (SQL_CURSOR_KEYSET_DRIVEN ==
2951 stmt->options.cursor_type
2953 strcmp((const char *)tuple_new
2954 [qres->num_fields -
2955 res->num_key_fields].value, tidval))
2956 res->keyset[kres_ridx].status |= SQL_ROW_UPDATED;
2957 KeySetSet(tuple_new, qres->num_fields,
2958 res->num_key_fields, res->keyset + kres_ridx);
2960 if (data_in_cache)
2961 MoveCachedRows(tuple_old, tuple_new, effective_fields,
2963 ret = SQL_SUCCESS;
2964 } else
2966 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
2967 "the content was deleted after last fetch",
2968 func);
2969 ret = SQL_SUCCESS_WITH_INFO;
2970 if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
2972 res->keyset[kres_ridx].status |= SQL_ROW_DELETED;
2976 QR_Destructor(qres);
2977 if (count)
2978 *count = rcnt;
2979 return ret;
2982 RETCODE
2983 SC_pos_reload(StatementClass * stmt, SQLULEN global_ridx, UInt2 * count,
2984 Int4 logKind)
2986 return SC_pos_reload_with_tid(stmt, global_ridx, count, logKind,
2987 NULL);
2990 static const int pre_fetch_count = 32;
2991 static SQLLEN LoadFromKeyset(StatementClass * stmt, QResultClass * res,
2992 int rows_per_fetch, SQLLEN limitrow)
2994 CSTR func = "LoadFromKeyset";
2995 ConnectionClass *conn = SC_get_conn(stmt);
2996 SQLLEN i;
2997 int j, rowc, rcnt = 0;
2998 BOOL prepare;
2999 OID oid;
3000 UInt4 blocknum;
3001 SQLLEN kres_ridx;
3002 UInt2 offset;
3003 char *qval = NULL, *sval = NULL;
3004 int keys_per_fetch = 10;
3006 prepare = PG_VERSION_GE(conn, 7.3);
3007 for (i = SC_get_rowset_start(stmt), kres_ridx =
3008 GIdx2KResIdx(i, stmt, res), rowc = 0;; i++)
3010 if (i >= limitrow)
3012 if (!rowc)
3013 break;
3014 if (res->reload_count > 0)
3016 for (j = rowc; j < keys_per_fetch; j++)
3018 if (j)
3019 strcpy(sval, ",NULL");
3020 else
3021 strcpy(sval, "NULL");
3022 sval = strchr(sval, '\0');
3025 rowc = -1; /* end of loop */
3027 if (rowc < 0 || rowc >= keys_per_fetch)
3029 QResultClass *qres;
3031 strcpy(sval, ")");
3032 qres = CC_send_query(conn, qval, NULL, CREATE_KEYSET, stmt);
3033 if (QR_command_maybe_successful(qres))
3035 SQLLEN j, k, l;
3036 Int2 m;
3037 TupleField *tuple, *tuplew;
3039 for (j = 0; j < QR_get_num_total_read(qres); j++)
3041 oid = getOid(qres, j);
3042 getTid(qres, j, &blocknum, &offset);
3043 for (k = SC_get_rowset_start(stmt); k < limitrow;
3044 k++)
3046 if (oid == getOid(res, k))
3048 l = GIdx2CacheIdx(k, stmt, res);
3049 tuple =
3050 res->backend_tuples +
3051 res->num_fields * l;
3052 tuplew =
3053 qres->backend_tuples +
3054 qres->num_fields * j;
3055 for (m = 0; m < res->num_fields;
3056 m++, tuple++, tuplew++)
3058 if (tuple->len > 0 && tuple->value)
3059 free(tuple->value);
3060 tuple->value = tuplew->value;
3061 tuple->len = tuplew->len;
3062 tuplew->value = NULL;
3063 tuplew->len = -1;
3065 res->keyset[k].status &= ~CURS_NEEDS_REREAD;
3066 break;
3070 } else
3072 SC_set_error(stmt, STMT_EXEC_ERROR, "Data Load Error",
3073 func);
3074 rcnt = -1;
3075 QR_Destructor(qres);
3076 break;
3078 QR_Destructor(qres);
3079 if (rowc < 0)
3080 break;
3081 rowc = 0;
3083 if (!rowc)
3085 size_t lodlen = 0;
3087 if (!qval)
3089 size_t allen;
3091 if (prepare)
3093 if (res->reload_count > 0)
3094 keys_per_fetch = res->reload_count;
3095 else
3097 char planname[32];
3098 int j;
3099 QResultClass *qres;
3101 if (rows_per_fetch >= pre_fetch_count * 2)
3102 keys_per_fetch = pre_fetch_count;
3103 else
3104 keys_per_fetch = rows_per_fetch;
3105 if (!keys_per_fetch)
3106 keys_per_fetch = 2;
3107 lodlen = strlen(stmt->load_statement);
3108 sprintf(planname, "_KEYSET_%p", res);
3109 allen = 8 + strlen(planname) +
3110 3 + 4 * keys_per_fetch + 1
3111 + 1 + 2 + lodlen + 20 +
3112 4 * keys_per_fetch + 1;
3113 SC_MALLOC_return_with_error(qval, char, allen,
3114 stmt,
3115 "Couldn't alloc qval",
3116 -1);
3117 sprintf(qval, "PREPARE \"%s\"", planname);
3118 sval = strchr(qval, '\0');
3119 for (j = 0; j < keys_per_fetch; j++)
3121 if (j == 0)
3122 strcpy(sval, "(tid");
3123 else
3124 strcpy(sval, ",tid");
3125 sval = strchr(sval, '\0');
3127 sprintf(sval, ") as %s where ctid in ",
3128 stmt->load_statement);
3129 sval = strchr(sval, '\0');
3130 for (j = 0; j < keys_per_fetch; j++)
3132 if (j == 0)
3133 strcpy(sval, "($1");
3134 else
3135 sprintf(sval, ",$%d", j + 1);
3136 sval = strchr(sval, '\0');
3138 strcpy(sval, ")");
3139 qres = CC_send_query(conn, qval, NULL, 0, stmt);
3140 if (QR_command_maybe_successful(qres))
3142 res->reload_count = keys_per_fetch;
3143 } else
3145 SC_set_error(stmt, STMT_EXEC_ERROR,
3146 "Prepare for Data Load Error",
3147 func);
3148 rcnt = -1;
3149 QR_Destructor(qres);
3150 break;
3152 QR_Destructor(qres);
3154 allen = 25 + 23 * keys_per_fetch;
3155 } else
3157 keys_per_fetch = pre_fetch_count;
3158 lodlen = strlen(stmt->load_statement);
3159 allen = lodlen + 20 + 23 * keys_per_fetch;
3161 SC_REALLOC_return_with_error(qval, char, allen,
3162 stmt,
3163 "Couldn't alloc qval", -1);
3165 if (res->reload_count > 0)
3167 sprintf(qval, "EXECUTE \"_KEYSET_%p\"(", res);
3168 sval = qval;
3169 } else
3171 memcpy(qval, stmt->load_statement, lodlen);
3172 sval = qval + lodlen;
3173 sval[0] = '\0';
3174 strcpy(sval, " where ctid in (");
3176 sval = strchr(sval, '\0');
3178 if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
3180 getTid(res, i, &blocknum, &offset);
3181 if (rowc)
3182 sprintf(sval, ",'(%u,%u)'", blocknum, offset);
3183 else
3184 sprintf(sval, "'(%u,%u)'", blocknum, offset);
3185 sval = strchr(sval, '\0');
3186 rowc++;
3187 rcnt++;
3190 if (qval)
3191 free(qval);
3192 return rcnt;
3195 static RETCODE SQL_API
3196 SC_pos_reload_needed(StatementClass * stmt, SQLULEN req_size,
3197 UDWORD flag)
3199 CSTR func = "SC_pos_reload_needed";
3200 Int4 req_rows_size;
3201 SQLLEN i, limitrow;
3202 UInt2 qcount;
3203 QResultClass *res;
3204 RETCODE ret = SQL_ERROR;
3205 SQLLEN kres_ridx, rowc;
3206 Int4 rows_per_fetch;
3207 BOOL create_from_scratch = (0 != flag);
3209 mylog("%s\n", func);
3210 if (!(res = SC_get_Curres(stmt)))
3212 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3213 "Null statement result in SC_pos_reload_needed.",
3214 func);
3215 return SQL_ERROR;
3217 if (SC_update_not_ready(stmt))
3218 parse_statement(stmt, TRUE); /* not preferable */
3219 if (!stmt->updatable)
3221 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3222 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3223 "the statement is read-only", func);
3224 return SQL_ERROR;
3226 rows_per_fetch = 0;
3227 req_rows_size = QR_get_reqsize(res);
3228 if (req_size > req_rows_size)
3229 req_rows_size = (UInt4) req_size;
3230 if (create_from_scratch)
3232 rows_per_fetch =
3233 ((pre_fetch_count - 1) / req_rows_size + 1) * req_rows_size;
3234 limitrow = RowIdx2GIdx(rows_per_fetch, stmt);
3235 } else
3237 limitrow = RowIdx2GIdx(req_rows_size, stmt);
3239 if (limitrow > res->num_cached_keys)
3240 limitrow = res->num_cached_keys;
3241 if (create_from_scratch)
3243 SQLLEN brows;
3245 ClearCachedRows(res->backend_tuples, res->num_fields,
3246 res->num_cached_rows);
3247 brows = GIdx2RowIdx(limitrow, stmt);
3248 if (brows > res->count_backend_allocated)
3250 res->backend_tuples = (TupleField *)
3251 realloc(res->backend_tuples,
3252 sizeof(TupleField) * res->num_fields * brows);
3253 res->count_backend_allocated = brows;
3255 if (brows > 0)
3256 memset(res->backend_tuples, 0,
3257 sizeof(TupleField) * res->num_fields * brows);
3258 QR_set_num_cached_rows(res, brows);
3259 QR_set_rowstart_in_cache(res, 0);
3260 if (SQL_RD_ON != stmt->options.retrieve_data)
3261 return SQL_SUCCESS;
3262 for (i = SC_get_rowset_start(stmt), kres_ridx =
3263 GIdx2KResIdx(i, stmt, res); i < limitrow; i++, kres_ridx++)
3265 if (0 ==
3266 (res->keyset[kres_ridx].
3267 status & (CURS_SELF_DELETING | CURS_SELF_DELETED |
3268 CURS_OTHER_DELETED)))
3269 res->keyset[kres_ridx].status |= CURS_NEEDS_REREAD;
3272 if (rowc =
3273 LoadFromKeyset(stmt, res, rows_per_fetch, limitrow), rowc < 0)
3275 return SQL_ERROR;
3277 for (i = SC_get_rowset_start(stmt), kres_ridx =
3278 GIdx2KResIdx(i, stmt, res); i < limitrow; i++)
3280 if (0 != (res->keyset[kres_ridx].status & CURS_NEEDS_REREAD))
3282 ret = SC_pos_reload(stmt, i, &qcount, 0);
3283 if (SQL_ERROR == ret)
3285 break;
3287 if (SQL_ROW_DELETED ==
3288 (res->keyset[kres_ridx].status & KEYSET_INFO_PUBLIC))
3290 res->keyset[kres_ridx].status |= CURS_OTHER_DELETED;
3292 res->keyset[kres_ridx].status &= ~CURS_NEEDS_REREAD;
3295 return ret;
3298 static RETCODE SQL_API
3299 SC_pos_newload(StatementClass * stmt, const UInt4 * oidint, BOOL tidRef,
3300 const char *tidval)
3302 CSTR func = "SC_pos_newload";
3303 int i;
3304 QResultClass *res, *qres;
3305 RETCODE ret = SQL_ERROR;
3307 mylog("positioned new ti=%p\n", stmt->ti);
3308 if (!(res = SC_get_Curres(stmt)))
3310 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3311 "Null statement result in SC_pos_newload.", func);
3312 return SQL_ERROR;
3314 if (SC_update_not_ready(stmt))
3315 parse_statement(stmt, TRUE); /* not preferable */
3316 if (!stmt->updatable)
3318 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3319 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3320 "the statement is read-only", func);
3321 return SQL_ERROR;
3323 qres =
3324 positioned_load(stmt,
3325 (tidRef
3326 && NULL == tidval) ? USE_INSERTED_TID : 0,
3327 oidint, tidRef ? tidval : NULL);
3328 if (!qres || !QR_command_maybe_successful(qres))
3330 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3331 "positioned_load in pos_newload failed", func);
3332 } else
3334 SQLLEN count = QR_get_num_cached_tuples(qres);
3336 QR_set_position(qres, 0);
3337 if (count == 1)
3339 int effective_fields = res->num_fields;
3340 ssize_t tuple_size;
3341 SQLLEN num_total_rows, num_cached_rows, kres_ridx;
3342 BOOL appendKey = FALSE, appendData = FALSE;
3343 TupleField *tuple_old, *tuple_new;
3345 tuple_new = qres->tupleField;
3346 num_total_rows = QR_get_num_total_tuples(res);
3348 AddAdded(stmt, res, num_total_rows, tuple_new);
3349 num_cached_rows = QR_get_num_cached_tuples(res);
3350 kres_ridx = GIdx2KResIdx(num_total_rows, stmt, res);
3351 if (QR_haskeyset(res))
3353 if (!QR_get_cursor(res))
3355 appendKey = TRUE;
3356 if (num_total_rows ==
3357 CacheIdx2GIdx(num_cached_rows, stmt, res))
3358 appendData = TRUE;
3359 else
3361 inolog
3362 ("total %d <> backend %d - base %d + start %d cursor_type=%d\n",
3363 num_total_rows, num_cached_rows,
3364 QR_get_rowstart_in_cache(res),
3365 SC_get_rowset_start(stmt),
3366 stmt->options.cursor_type);
3368 } else if (kres_ridx >= 0
3369 && kres_ridx < res->cache_size)
3371 appendKey = TRUE;
3372 appendData = TRUE;
3375 if (appendKey)
3377 if (res->num_cached_keys >= res->count_keyset_allocated)
3379 if (!res->count_keyset_allocated)
3380 tuple_size = TUPLE_MALLOC_INC;
3381 else
3382 tuple_size = res->count_keyset_allocated * 2;
3383 res->keyset =
3384 (KeySet *) realloc(res->keyset,
3385 sizeof(KeySet) * tuple_size);
3386 res->count_keyset_allocated = tuple_size;
3388 KeySetSet(tuple_new, qres->num_fields,
3389 res->num_key_fields, res->keyset + kres_ridx);
3390 res->num_cached_keys++;
3392 if (appendData)
3394 inolog
3395 ("total %d == backend %d - base %d + start %d cursor_type=%d\n",
3396 num_total_rows, num_cached_rows,
3397 QR_get_rowstart_in_cache(res),
3398 SC_get_rowset_start(stmt),
3399 stmt->options.cursor_type);
3400 if (num_cached_rows >= res->count_backend_allocated)
3402 if (!res->count_backend_allocated)
3403 tuple_size = TUPLE_MALLOC_INC;
3404 else
3405 tuple_size = res->count_backend_allocated * 2;
3406 res->backend_tuples =
3407 (TupleField *) realloc(res->backend_tuples,
3408 res->num_fields *
3409 sizeof(TupleField) *
3410 tuple_size);
3411 if (!res->backend_tuples)
3413 SC_set_error(stmt,
3414 QR_set_rstatus(res,
3415 PORES_FATAL_ERROR),
3416 "Out of memory while reading tuples.",
3417 func);
3418 QR_Destructor(qres);
3419 return SQL_ERROR;
3421 res->count_backend_allocated = tuple_size;
3423 tuple_old =
3424 res->backend_tuples +
3425 res->num_fields * num_cached_rows;
3426 for (i = 0; i < effective_fields; i++)
3428 tuple_old[i].len = tuple_new[i].len;
3429 tuple_new[i].len = -1;
3430 tuple_old[i].value = tuple_new[i].value;
3431 tuple_new[i].value = NULL;
3433 res->num_cached_rows++;
3435 ret = SQL_SUCCESS;
3436 } else if (0 == count)
3437 ret = SQL_NO_DATA_FOUND;
3438 else
3440 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3441 "the driver cound't identify inserted rows",
3442 func);
3443 ret = SQL_ERROR;
3445 /* stmt->currTuple = SC_get_rowset_start(stmt) + ridx; */
3447 QR_Destructor(qres);
3448 return ret;
3451 static RETCODE SQL_API
3452 irow_update(RETCODE ret, StatementClass * stmt, StatementClass * ustmt,
3453 SQLSETPOSIROW irow, SQLULEN global_ridx)
3455 CSTR func = "irow_update";
3457 if (ret != SQL_ERROR)
3459 int updcnt;
3460 QResultClass *tres = SC_get_Curres(ustmt);
3461 const char *cmdstr = QR_get_command(tres);
3463 if (cmdstr && sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
3465 if (updcnt == 1)
3467 const char *tidval = NULL;
3469 if (NULL != tres->backend_tuples &&
3470 1 == QR_get_num_cached_tuples(tres))
3471 tidval = QR_get_value_backend_text(tres, 0, 0);
3472 ret =
3473 SC_pos_reload_with_tid(stmt, global_ridx,
3474 (UInt2 *) 0, SQL_UPDATE,
3475 tidval);
3476 if (SQL_ERROR != ret)
3477 AddUpdated(stmt, global_ridx);
3478 } else if (updcnt == 0)
3480 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3481 "the content was changed before updation",
3482 func);
3483 ret = SQL_ERROR;
3484 if (stmt->options.cursor_type ==
3485 SQL_CURSOR_KEYSET_DRIVEN)
3486 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
3487 } else
3488 ret = SQL_ERROR;
3489 } else
3490 ret = SQL_ERROR;
3491 if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
3493 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3494 "SetPos update return error", func);
3497 return ret;
3500 /* SQL_NEED_DATA callback for SC_pos_update */
3501 typedef struct {
3502 BOOL updyes;
3503 QResultClass *res;
3504 StatementClass *stmt, *qstmt;
3505 IRDFields *irdflds;
3506 SQLSETPOSIROW irow;
3507 SQLULEN global_ridx;
3508 } pup_cdata;
3509 static RETCODE pos_update_callback(RETCODE retcode, void *para)
3511 CSTR func = "pos_update_callback";
3512 RETCODE ret = retcode;
3513 pup_cdata *s = (pup_cdata *) para;
3514 SQLLEN kres_ridx;
3516 if (s->updyes)
3518 mylog("pos_update_callback in\n");
3519 ret =
3520 irow_update(ret, s->stmt, s->qstmt, s->irow,
3521 s->global_ridx);
3522 inolog("irow_update ret=%d,%d\n", ret,
3523 SC_get_errornumber(s->qstmt));
3524 if (ret != SQL_SUCCESS)
3525 SC_error_copy(s->stmt, s->qstmt, TRUE);
3526 PGAPI_FreeStmt(s->qstmt, SQL_DROP);
3527 s->qstmt = NULL;
3529 s->updyes = FALSE;
3530 kres_ridx = GIdx2KResIdx(s->global_ridx, s->stmt, s->res);
3531 if (kres_ridx < 0 || kres_ridx >= s->res->num_cached_keys)
3533 SC_set_error(s->stmt, STMT_ROW_OUT_OF_RANGE,
3534 "the target rows is out of the rowset", func);
3535 inolog("gidx=%d num_keys=%d kresidx=%d\n", s->global_ridx,
3536 s->res->num_cached_keys, kres_ridx);
3537 return SQL_ERROR;
3539 if (SQL_SUCCESS == ret && s->res->keyset)
3541 ConnectionClass *conn = SC_get_conn(s->stmt);
3543 // VX_CLEANUP: It might be possible to extend the carnage from here.
3544 s->res->keyset[kres_ridx].status |=
3545 (SQL_ROW_UPDATED | CURS_SELF_UPDATED);
3547 if (s->irdflds->rowStatusArray)
3549 switch (ret)
3551 case SQL_SUCCESS:
3552 s->irdflds->rowStatusArray[s->irow] = SQL_ROW_UPDATED;
3553 break;
3554 default:
3555 s->irdflds->rowStatusArray[s->irow] = ret;
3559 return ret;
3562 RETCODE
3563 SC_pos_update(StatementClass * stmt,
3564 SQLSETPOSIROW irow, SQLULEN global_ridx)
3566 CSTR func = "SC_pos_update";
3567 int i, num_cols, upd_cols;
3568 pup_cdata s;
3569 ConnectionClass *conn;
3570 ARDFields *opts = SC_get_ARDF(stmt);
3571 BindInfoClass *bindings = opts->bindings;
3572 TABLE_INFO *ti;
3573 FIELD_INFO **fi;
3574 char updstr[4096];
3575 RETCODE ret;
3576 OID oid;
3577 UInt4 blocknum;
3578 UInt2 pgoffset;
3579 SQLLEN offset;
3580 SQLLEN *used, kres_ridx;
3581 Int4 bind_size = opts->bind_size;
3583 s.stmt = stmt;
3584 s.irow = irow;
3585 s.global_ridx = global_ridx;
3586 s.irdflds = SC_get_IRDF(s.stmt);
3587 fi = s.irdflds->fi;
3588 if (!(s.res = SC_get_Curres(s.stmt)))
3590 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3591 "Null statement result in SC_pos_update.", func);
3592 return SQL_ERROR;
3594 mylog("POS UPDATE %d+%d fi=%p ti=%p\n", s.irow,
3595 QR_get_rowstart_in_cache(s.res), fi, s.stmt->ti);
3596 if (SC_update_not_ready(stmt))
3597 parse_statement(s.stmt, TRUE); /* not preferable */
3598 if (!s.stmt->updatable)
3600 s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3601 SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER,
3602 "the statement is read-only", func);
3603 return SQL_ERROR;
3605 kres_ridx = GIdx2KResIdx(s.global_ridx, s.stmt, s.res);
3606 if (kres_ridx < 0 || kres_ridx >= s.res->num_cached_keys)
3608 SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE,
3609 "the target rows is out of the rowset", func);
3610 return SQL_ERROR;
3612 if (!(oid = getOid(s.res, kres_ridx)))
3614 if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
3616 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3617 "the row was already deleted ?", func);
3618 return SQL_ERROR;
3621 getTid(s.res, kres_ridx, &blocknum, &pgoffset);
3623 ti = s.stmt->ti[0];
3624 if (NAME_IS_VALID(ti->schema_name))
3625 sprintf(updstr, "update \"%s\".\"%s\" set",
3626 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name));
3627 else
3628 sprintf(updstr, "update \"%s\" set", SAFE_NAME(ti->table_name));
3629 num_cols = s.irdflds->nfields;
3630 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
3631 for (i = upd_cols = 0; i < num_cols; i++)
3633 if (used = bindings[i].used, used != NULL)
3635 used = LENADDR_SHIFT(used, offset);
3636 if (bind_size > 0)
3637 used = LENADDR_SHIFT(used, bind_size * s.irow);
3638 else
3639 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
3640 mylog("%d used=%d,%p\n", i, *used, used);
3641 if (*used != SQL_IGNORE && fi[i]->updatable)
3643 if (upd_cols)
3644 sprintf(updstr, "%s, \"%s\" = ?", updstr,
3645 GET_NAME(fi[i]->column_name));
3646 else
3647 sprintf(updstr, "%s \"%s\" = ?", updstr,
3648 GET_NAME(fi[i]->column_name));
3649 upd_cols++;
3651 } else
3652 mylog("%d null bind\n", i);
3654 conn = SC_get_conn(s.stmt);
3655 s.updyes = FALSE;
3656 if (upd_cols > 0)
3658 HSTMT hstmt;
3659 int j;
3660 APDFields *apdopts;
3661 OID fieldtype = 0;
3662 const char *bestitem = GET_NAME(ti->bestitem);
3663 const char *bestqual = GET_NAME(ti->bestqual);
3665 sprintf(updstr, "%s where ctid = '(%u, %u)'", updstr,
3666 blocknum, pgoffset);
3667 if (bestitem)
3669 /*sprintf(updstr, "%s and \"%s\" = %u", updstr, bestitem, oid); */
3670 strcat(updstr, " and ");
3671 sprintf(updstr + strlen(updstr), bestqual, oid);
3673 if (PG_VERSION_GE(conn, 8.2))
3674 strcat(updstr, " returning ctid");
3675 mylog("updstr=%s\n", updstr);
3676 if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
3678 SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR,
3679 "internal AllocStmt error", func);
3680 return SQL_ERROR;
3682 s.qstmt = (StatementClass *) hstmt;
3683 apdopts = SC_get_APDF(s.qstmt);
3684 apdopts->param_bind_type = opts->bind_size;
3685 apdopts->param_offset_ptr = opts->row_offset_ptr;
3686 SC_set_delegate(s.stmt, s.qstmt);
3687 for (i = j = 0; i < num_cols; i++)
3689 if (used = bindings[i].used, used != NULL)
3691 used = LENADDR_SHIFT(used, offset);
3692 if (bind_size > 0)
3693 used = LENADDR_SHIFT(used, bind_size * s.irow);
3694 else
3695 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
3696 mylog("%d used=%d\n", i, *used);
3697 if (*used != SQL_IGNORE && fi[i]->updatable)
3699 fieldtype = QR_get_field_type(s.res, i);
3700 PGAPI_BindParameter(hstmt,
3701 (SQLUSMALLINT)++ j,
3702 SQL_PARAM_INPUT,
3703 bindings[i].returntype,
3704 pgtype_to_concise_type(s.stmt,
3705 fieldtype,
3707 fi[i]->column_size >
3708 0 ? fi[i]->
3709 column_size :
3710 pgtype_column_size(s.stmt,
3711 fieldtype, i,
3712 UNKNOWNS_AS_MAX),
3713 (SQLSMALLINT) fi[i]->
3714 decimal_digits,
3715 bindings[i].buffer,
3716 bindings[i].buflen,
3717 bindings[i].used);
3721 s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
3722 s.updyes = TRUE;
3723 ret = PGAPI_ExecDirect(hstmt, (const UCHAR *)updstr, SQL_NTS, 0);
3724 if (ret == SQL_NEED_DATA)
3726 pup_cdata *cbdata = (pup_cdata *) malloc(sizeof(pup_cdata));
3727 memcpy(cbdata, &s, sizeof(pup_cdata));
3728 enqueueNeedDataCallback(s.stmt, pos_update_callback,
3729 cbdata);
3730 return ret;
3732 /* else if (ret != SQL_SUCCESS) this is unneccesary
3733 SC_error_copy(s.stmt, s.qstmt, TRUE); */
3734 } else
3736 ret = SQL_SUCCESS_WITH_INFO;
3737 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3738 "update list null", func);
3741 ret = pos_update_callback(ret, &s);
3742 return ret;
3745 RETCODE
3746 SC_pos_delete(StatementClass * stmt,
3747 SQLSETPOSIROW irow, SQLULEN global_ridx)
3749 CSTR func = "SC_pos_update";
3750 UWORD offset;
3751 QResultClass *res, *qres;
3752 ConnectionClass *conn = SC_get_conn(stmt);
3753 IRDFields *irdflds = SC_get_IRDF(stmt);
3754 char dltstr[4096];
3755 RETCODE ret;
3756 SQLLEN kres_ridx;
3757 OID oid;
3758 UInt4 blocknum, qflag;
3759 TABLE_INFO *ti;
3760 const char *bestitem;
3761 const char *bestqual;
3763 mylog("POS DELETE ti=%p\n", stmt->ti);
3764 if (!(res = SC_get_Curres(stmt)))
3766 SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
3767 "Null statement result in SC_pos_delete.", func);
3768 return SQL_ERROR;
3770 if (SC_update_not_ready(stmt))
3771 parse_statement(stmt, TRUE); /* not preferable */
3772 if (!stmt->updatable)
3774 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3775 SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
3776 "the statement is read-only", func);
3777 return SQL_ERROR;
3779 kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
3780 if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
3782 SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE,
3783 "the target rows is out of the rowset", func);
3784 return SQL_ERROR;
3786 ti = stmt->ti[0];
3787 bestitem = GET_NAME(ti->bestitem);
3788 if (!(oid = getOid(res, kres_ridx)))
3790 if (bestitem && !strcmp(bestitem, OID_NAME))
3792 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3793 "the row was already deleted ?", func);
3794 return SQL_ERROR;
3797 bestqual = GET_NAME(ti->bestqual);
3798 getTid(res, kres_ridx, &blocknum, &offset);
3799 /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s", */
3800 if (NAME_IS_VALID(ti->schema_name))
3801 sprintf(dltstr,
3802 "delete from \"%s\".\"%s\" where ctid = '(%u, %u)'",
3803 SAFE_NAME(ti->schema_name), SAFE_NAME(ti->table_name),
3804 blocknum, offset);
3805 else
3806 sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)'",
3807 SAFE_NAME(ti->table_name), blocknum, offset);
3808 if (bestitem)
3810 /*sprintf(dltstr, "%s and \"%s\" = %u", dltstr, bestitem, oid); */
3811 strcat(dltstr, " and ");
3812 sprintf(dltstr + strlen(dltstr), bestqual, oid);
3815 mylog("dltstr=%s\n", dltstr);
3816 qflag = 0;
3817 // VX_CLEANUP: GO_INTO_TRANSACTION doesn't seem to be used elsewhere
3818 qres = CC_send_query(conn, dltstr, NULL, qflag, stmt);
3819 ret = SQL_SUCCESS;
3820 if (QR_command_maybe_successful(qres))
3822 int dltcnt;
3823 const char *cmdstr = QR_get_command(qres);
3825 if (cmdstr && sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
3827 if (dltcnt == 1)
3829 RETCODE tret =
3830 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0,
3831 SQL_DELETE);
3832 if (!SQL_SUCCEEDED(tret))
3833 ret = tret;
3834 } else if (dltcnt == 0)
3836 SC_set_error(stmt, STMT_ROW_VERSION_CHANGED,
3837 "the content was changed before deletion",
3838 func);
3839 ret = SQL_ERROR;
3840 if (stmt->options.cursor_type ==
3841 SQL_CURSOR_KEYSET_DRIVEN)
3842 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
3843 } else
3844 ret = SQL_ERROR;
3845 } else
3846 ret = SQL_ERROR;
3847 } else
3848 ret = SQL_ERROR;
3849 if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
3851 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3852 "SetPos delete return error", func);
3854 if (qres)
3855 QR_Destructor(qres);
3856 if (SQL_SUCCESS == ret && res->keyset)
3858 AddDeleted(res, global_ridx, res->keyset + kres_ridx);
3859 res->keyset[kres_ridx].status &= (~KEYSET_INFO_PUBLIC);
3860 // VX_CLEANUP: It might be possible to extend the carnage from here.
3861 res->keyset[kres_ridx].status |=
3862 (SQL_ROW_DELETED | CURS_SELF_DELETED);
3863 inolog(".status[%d]=%x\n", global_ridx,
3864 res->keyset[kres_ridx].status);
3866 if (irdflds->rowStatusArray)
3868 switch (ret)
3870 case SQL_SUCCESS:
3871 irdflds->rowStatusArray[irow] = SQL_ROW_DELETED;
3872 break;
3873 default:
3874 irdflds->rowStatusArray[irow] = ret;
3877 return ret;
3880 static RETCODE SQL_API
3881 irow_insert(RETCODE ret, StatementClass * stmt, StatementClass * istmt,
3882 SQLLEN addpos)
3884 CSTR func = "irow_insert";
3886 if (ret != SQL_ERROR)
3888 int addcnt;
3889 OID oid, *poid = NULL;
3890 ARDFields *opts = SC_get_ARDF(stmt);
3891 QResultClass *ires = SC_get_Curres(istmt), *tres;
3892 const char *cmdstr;
3893 BindInfoClass *bookmark;
3895 tres = (ires->next ? ires->next : ires);
3896 cmdstr = QR_get_command(tres);
3897 if (cmdstr &&
3898 sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
3899 addcnt == 1)
3901 ConnectionClass *conn = SC_get_conn(stmt);
3902 RETCODE qret;
3904 if (0 != oid)
3905 poid = &oid;
3906 qret = SQL_NO_DATA_FOUND;
3907 if (PG_VERSION_GE(conn, 7.2))
3909 const char *tidval = NULL;
3911 if (NULL != tres->backend_tuples &&
3912 1 == QR_get_num_cached_tuples(tres))
3913 tidval = QR_get_value_backend_text(tres, 0, 0);
3914 qret = SC_pos_newload(stmt, poid, TRUE, tidval);
3915 if (SQL_ERROR == qret)
3916 return qret;
3918 if (SQL_NO_DATA_FOUND == qret)
3920 qret = SC_pos_newload(stmt, poid, FALSE, NULL);
3921 if (SQL_ERROR == qret)
3922 return qret;
3924 bookmark = opts->bookmark;
3925 if (bookmark && bookmark->buffer)
3927 char buf[32];
3928 SQLULEN offset =
3929 opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
3931 snprintf(buf, sizeof(buf), FORMAT_LEN,
3932 SC_make_bookmark(addpos));
3933 SC_set_current_col(stmt, -1);
3934 copy_and_convert_field(stmt,
3935 PG_TYPE_INT4,
3936 buf,
3937 bookmark->returntype,
3938 bookmark->buffer + offset,
3939 bookmark->buflen,
3940 LENADDR_SHIFT(bookmark->used,
3941 offset),
3942 LENADDR_SHIFT(bookmark->used,
3943 offset));
3945 } else
3947 SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND,
3948 "SetPos insert return error", func);
3951 return ret;
3954 /* SQL_NEED_DATA callback for SC_pos_add */
3955 typedef struct {
3956 BOOL updyes;
3957 QResultClass *res;
3958 StatementClass *stmt, *qstmt;
3959 IRDFields *irdflds;
3960 SQLSETPOSIROW irow;
3961 } padd_cdata;
3963 static RETCODE pos_add_callback(RETCODE retcode, void *para)
3965 RETCODE ret = retcode;
3966 padd_cdata *s = (padd_cdata *) para;
3967 SQLLEN addpos;
3969 if (s->updyes)
3971 SQLSETPOSIROW brow_save;
3973 mylog("pos_add_callback in ret=%d\n", ret);
3974 brow_save = s->stmt->bind_row;
3975 s->stmt->bind_row = s->irow;
3976 if (QR_get_cursor(s->res))
3977 addpos = -(SQLLEN) (s->res->ad_count + 1);
3978 else
3979 addpos = QR_get_num_total_tuples(s->res);
3980 ret = irow_insert(ret, s->stmt, s->qstmt, addpos);
3981 s->stmt->bind_row = brow_save;
3983 s->updyes = FALSE;
3984 SC_setInsertedTable(s->qstmt, ret);
3985 if (ret != SQL_SUCCESS)
3986 SC_error_copy(s->stmt, s->qstmt, TRUE);
3987 PGAPI_FreeStmt((HSTMT) s->qstmt, SQL_DROP);
3988 s->qstmt = NULL;
3989 if (SQL_SUCCESS == ret && s->res->keyset)
3991 SQLLEN global_ridx = QR_get_num_total_tuples(s->res) - 1;
3992 ConnectionClass *conn = SC_get_conn(s->stmt);
3993 SQLLEN kres_ridx;
3994 UWORD status = SQL_ROW_ADDED;
3996 // VX_CLEANUP: It might be possible to extend the carnage from here.
3997 status |= CURS_SELF_ADDED;
3998 kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, s->res);
3999 if (kres_ridx >= 0 || kres_ridx < s->res->num_cached_keys)
4001 s->res->keyset[kres_ridx].status = status;
4004 if (s->irdflds->rowStatusArray)
4006 switch (ret)
4008 case SQL_SUCCESS:
4009 s->irdflds->rowStatusArray[s->irow] = SQL_ROW_ADDED;
4010 break;
4011 default:
4012 s->irdflds->rowStatusArray[s->irow] = ret;
4016 return ret;
4019 RETCODE SC_pos_add(StatementClass * stmt, SQLSETPOSIROW irow)
4021 CSTR func = "SC_pos_add";
4022 int num_cols, add_cols, i;
4023 HSTMT hstmt;
4025 padd_cdata s;
4026 ConnectionClass *conn;
4027 ConnInfo *ci;
4028 ARDFields *opts = SC_get_ARDF(stmt);
4029 APDFields *apdopts;
4030 BindInfoClass *bindings = opts->bindings;
4031 FIELD_INFO **fi = SC_get_IRDF(stmt)->fi;
4032 char addstr[4096];
4033 RETCODE ret;
4034 SQLULEN offset;
4035 SQLLEN *used;
4036 Int4 bind_size = opts->bind_size;
4037 OID fieldtype;
4038 int func_cs_count = 0;
4040 mylog("POS ADD fi=%p ti=%p\n", fi, stmt->ti);
4041 s.stmt = stmt;
4042 s.irow = irow;
4043 if (!(s.res = SC_get_Curres(s.stmt)))
4045 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4046 "Null statement result in SC_pos_add.", func);
4047 return SQL_ERROR;
4049 if (SC_update_not_ready(stmt))
4050 parse_statement(s.stmt, TRUE); /* not preferable */
4051 if (!s.stmt->updatable)
4053 s.stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
4054 SC_set_error(s.stmt, STMT_INVALID_OPTION_IDENTIFIER,
4055 "the statement is read-only", func);
4056 return SQL_ERROR;
4058 s.irdflds = SC_get_IRDF(s.stmt);
4059 num_cols = s.irdflds->nfields;
4060 conn = SC_get_conn(s.stmt);
4061 if (NAME_IS_VALID(s.stmt->ti[0]->schema_name))
4062 sprintf(addstr, "insert into \"%s\".\"%s\" (",
4063 SAFE_NAME(s.stmt->ti[0]->schema_name),
4064 SAFE_NAME(s.stmt->ti[0]->table_name));
4065 else
4066 sprintf(addstr, "insert into \"%s\" (",
4067 SAFE_NAME(s.stmt->ti[0]->table_name));
4068 if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
4070 SC_set_error(s.stmt, STMT_NO_MEMORY_ERROR,
4071 "internal AllocStmt error", func);
4072 return SQL_ERROR;
4074 if (opts->row_offset_ptr)
4075 offset = *opts->row_offset_ptr;
4076 else
4077 offset = 0;
4078 s.qstmt = (StatementClass *) hstmt;
4079 apdopts = SC_get_APDF(s.qstmt);
4080 apdopts->param_bind_type = opts->bind_size;
4081 apdopts->param_offset_ptr = opts->row_offset_ptr;
4082 SC_set_delegate(s.stmt, s.qstmt);
4083 ci = &(conn->connInfo);
4084 for (i = add_cols = 0; i < num_cols; i++)
4086 if (used = bindings[i].used, used != NULL)
4088 used = LENADDR_SHIFT(used, offset);
4089 if (bind_size > 0)
4090 used = LENADDR_SHIFT(used, bind_size * s.irow);
4091 else
4092 used = LENADDR_SHIFT(used, s.irow * sizeof(SQLLEN));
4093 mylog("%d used=%d\n", i, *used);
4094 if (*used != SQL_IGNORE && fi[i]->updatable)
4096 fieldtype = QR_get_field_type(s.res, i);
4097 if (add_cols)
4098 sprintf(addstr, "%s, \"%s\"", addstr,
4099 GET_NAME(fi[i]->column_name));
4100 else
4101 sprintf(addstr, "%s\"%s\"", addstr,
4102 GET_NAME(fi[i]->column_name));
4103 PGAPI_BindParameter(hstmt, (SQLUSMALLINT)++ add_cols,
4104 SQL_PARAM_INPUT,
4105 bindings[i].returntype,
4106 pgtype_to_concise_type(s.stmt,
4107 fieldtype,
4109 fi[i]->column_size >
4110 0 ? fi[i]->
4111 column_size : pgtype_column_size(s.
4112 stmt, fieldtype,
4113 i, UNKNOWNS_AS_MAX),
4114 (SQLSMALLINT) fi[i]->decimal_digits,
4115 bindings[i].buffer,
4116 bindings[i].buflen,
4117 bindings[i].used);
4119 } else
4120 mylog("%d null bind\n", i);
4122 s.updyes = FALSE;
4123 #define return DONT_CALL_RETURN_FROM_HERE???
4124 ENTER_INNER_CONN_CS(conn, func_cs_count);
4125 if (add_cols > 0)
4127 sprintf(addstr, "%s) values (", addstr);
4128 for (i = 0; i < add_cols; i++)
4130 if (i)
4131 strcat(addstr, ", ?");
4132 else
4133 strcat(addstr, "?");
4135 strcat(addstr, ")");
4136 if (PG_VERSION_GE(conn, 8.2))
4137 strcat(addstr, " returning ctid");
4138 mylog("addstr=%s\n", addstr);
4139 s.qstmt->exec_start_row = s.qstmt->exec_end_row = s.irow;
4140 s.updyes = TRUE;
4141 ret = PGAPI_ExecDirect(hstmt, (const UCHAR *)addstr, SQL_NTS, 0);
4142 if (ret == SQL_NEED_DATA)
4144 padd_cdata *cbdata =
4145 (padd_cdata *) malloc(sizeof(padd_cdata));
4146 memcpy(cbdata, &s, sizeof(padd_cdata));
4147 enqueueNeedDataCallback(s.stmt, pos_add_callback, cbdata);
4148 goto cleanup;
4150 /* else if (ret != SQL_SUCCESS) this is unneccesary
4151 SC_error_copy(s.stmt, s.qstmt, TRUE); */
4152 } else
4154 ret = SQL_SUCCESS_WITH_INFO;
4155 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4156 "insert list null", func);
4159 ret = pos_add_callback(ret, &s);
4161 cleanup:
4162 #undef return
4163 CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
4164 return ret;
4168 * Stuff for updatable cursors end.
4171 RETCODE
4172 SC_pos_refresh(StatementClass * stmt, SQLSETPOSIROW irow,
4173 SQLULEN global_ridx)
4175 RETCODE ret;
4176 IRDFields *irdflds = SC_get_IRDF(stmt);
4177 /* save the last_fetch_count */
4178 SQLLEN last_fetch = stmt->last_fetch_count;
4179 SQLLEN last_fetch2 = stmt->last_fetch_count_include_ommitted;
4180 SQLSETPOSIROW bind_save = stmt->bind_row;
4181 BOOL tuple_reload = FALSE;
4183 if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
4184 tuple_reload = TRUE;
4185 else
4187 QResultClass *res = SC_get_Curres(stmt);
4188 if (res && res->keyset)
4190 SQLLEN kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
4191 if (kres_ridx >= 0
4192 && kres_ridx < QR_get_num_cached_tuples(res))
4194 if (0 !=
4195 (CURS_NEEDS_REREAD & res->keyset[kres_ridx].status))
4196 tuple_reload = TRUE;
4200 if (tuple_reload)
4201 SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
4202 stmt->bind_row = irow;
4203 ret = SC_fetch(stmt);
4204 /* restore the last_fetch_count */
4205 stmt->last_fetch_count = last_fetch;
4206 stmt->last_fetch_count_include_ommitted = last_fetch2;
4207 stmt->bind_row = bind_save;
4208 if (irdflds->rowStatusArray)
4210 switch (ret)
4212 case SQL_ERROR:
4213 irdflds->rowStatusArray[irow] = SQL_ROW_ERROR;
4214 break;
4215 case SQL_SUCCESS:
4216 irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS;
4217 break;
4218 case SQL_SUCCESS_WITH_INFO:
4219 default:
4220 irdflds->rowStatusArray[irow] = ret;
4221 break;
4225 return SQL_SUCCESS;
4228 /* SQL_NEED_DATA callback for PGAPI_SetPos */
4229 typedef struct {
4230 BOOL need_data_callback, auto_commit_needed;
4231 QResultClass *res;
4232 StatementClass *stmt;
4233 ARDFields *opts;
4234 GetDataInfo *gdata;
4235 SQLLEN idx, start_row, end_row, ridx;
4236 UWORD fOption;
4237 SQLSETPOSIROW irow, nrow, processed;
4238 } spos_cdata;
4239 static
4240 RETCODE spos_callback(RETCODE retcode, void *para)
4242 CSTR func = "spos_callback";
4243 RETCODE ret;
4244 spos_cdata *s = (spos_cdata *) para;
4245 QResultClass *res;
4246 ARDFields *opts;
4247 ConnectionClass *conn;
4248 SQLULEN global_ridx;
4249 SQLLEN kres_ridx, pos_ridx = 0;
4251 ret = retcode;
4252 mylog("%s: %d in\n", func, s->need_data_callback);
4253 if (s->need_data_callback)
4255 s->processed++;
4256 if (SQL_ERROR != retcode)
4258 s->nrow++;
4259 s->idx++;
4261 } else
4263 s->ridx = -1;
4264 s->idx = s->nrow = s->processed = 0;
4266 res = s->res;
4267 opts = s->opts;
4268 if (!res || !opts)
4270 SC_set_error(s->stmt, STMT_SEQUENCE_ERROR,
4271 "Passed res or opts for spos_callback is NULL",
4272 func);
4273 return SQL_ERROR;
4275 s->need_data_callback = FALSE;
4276 for (; SQL_ERROR != ret && s->nrow <= s->end_row; s->idx++)
4278 global_ridx = RowIdx2GIdx(s->idx, s->stmt);
4279 if (SQL_ADD != s->fOption)
4281 if ((int) global_ridx >= QR_get_num_total_tuples(res))
4282 break;
4283 if (res->keyset)
4285 kres_ridx = GIdx2KResIdx(global_ridx, s->stmt, res);
4286 if (kres_ridx >= res->num_cached_keys)
4287 break;
4288 if (kres_ridx >= 0) /* the row may be deleted and not in the rowset */
4290 if (0 ==
4291 (res->keyset[kres_ridx].
4292 status & CURS_IN_ROWSET))
4293 continue;
4297 if (s->nrow < s->start_row)
4299 s->nrow++;
4300 continue;
4302 s->ridx = s->nrow;
4303 pos_ridx = s->idx;
4304 if (0 != s->irow || !opts->row_operation_ptr
4305 || opts->row_operation_ptr[s->nrow] == SQL_ROW_PROCEED)
4307 switch (s->fOption)
4309 case SQL_UPDATE:
4310 ret = SC_pos_update(s->stmt, s->nrow, global_ridx);
4311 break;
4312 case SQL_DELETE:
4313 ret = SC_pos_delete(s->stmt, s->nrow, global_ridx);
4314 break;
4315 case SQL_ADD:
4316 ret = SC_pos_add(s->stmt, s->nrow);
4317 break;
4318 case SQL_REFRESH:
4319 ret = SC_pos_refresh(s->stmt, s->nrow, global_ridx);
4320 break;
4322 if (SQL_NEED_DATA == ret)
4324 spos_cdata *cbdata =
4325 (spos_cdata *) malloc(sizeof(spos_cdata));
4327 memcpy(cbdata, s, sizeof(spos_cdata));
4328 cbdata->need_data_callback = TRUE;
4329 enqueueNeedDataCallback(s->stmt, spos_callback, cbdata);
4330 return ret;
4332 s->processed++;
4334 if (SQL_ERROR != ret)
4335 s->nrow++;
4337 conn = SC_get_conn(s->stmt);
4338 if (s->auto_commit_needed)
4339 PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
4340 if (s->irow > 0)
4342 if (SQL_ADD != s->fOption && s->ridx >= 0) /* for SQLGetData */
4344 s->stmt->currTuple = RowIdx2GIdx(pos_ridx, s->stmt);
4345 QR_set_position(res, pos_ridx);
4347 } else if (SC_get_IRDF(s->stmt)->rowsFetched)
4348 *(SC_get_IRDF(s->stmt)->rowsFetched) = s->processed;
4349 res->recent_processed_row_count = s->stmt->diag_row_count =
4350 s->processed;
4351 if (opts)
4353 inolog("processed=%d ret=%d rowset=%d", s->processed, ret,
4354 opts->size_of_rowset_odbc2);
4355 inolog(",%d\n", opts->size_of_rowset);
4358 return ret;
4362 * This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
4363 * This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
4365 // VX_CLEANUP: In ODBC 3, SQLExtendedFetch has been replaced with
4366 // SQLFetchScroll. Need to check whether either of them works.
4367 RETCODE SQL_API
4368 PGAPI_SetPos(HSTMT hstmt,
4369 SQLSETPOSIROW irow,
4370 SQLUSMALLINT fOption, SQLUSMALLINT fLock)
4372 CSTR func = "PGAPI_SetPos";
4373 RETCODE ret;
4374 ConnectionClass *conn;
4375 SQLLEN rowsetSize;
4376 int i;
4377 UInt2 gdata_allocated;
4378 GetDataInfo *gdata_info;
4379 GetDataClass *gdata = NULL;
4380 spos_cdata s;
4382 s.stmt = (StatementClass *) hstmt;
4383 if (!s.stmt)
4385 SC_log_error(func, NULL_STRING, NULL);
4386 return SQL_INVALID_HANDLE;
4389 s.irow = irow;
4390 s.fOption = fOption;
4391 s.auto_commit_needed = FALSE;
4392 s.opts = SC_get_ARDF(s.stmt);
4393 gdata_info = SC_get_GDTI(s.stmt);
4394 gdata = gdata_info->gdata;
4395 mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, s.fOption,
4396 s.irow, fLock, s.stmt->currTuple);
4397 if (s.stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
4399 else if (s.fOption != SQL_POSITION && s.fOption != SQL_REFRESH)
4401 SC_set_error(s.stmt, STMT_NOT_IMPLEMENTED_ERROR,
4402 "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos",
4403 func);
4404 return SQL_ERROR;
4407 if (!(s.res = SC_get_Curres(s.stmt)))
4409 SC_set_error(s.stmt, STMT_INVALID_CURSOR_STATE_ERROR,
4410 "Null statement result in PGAPI_SetPos.", func);
4411 return SQL_ERROR;
4414 rowsetSize =
4415 (s.stmt->transition_status ==
4416 7 ? s.opts->size_of_rowset_odbc2 : s.opts->size_of_rowset);
4417 if (s.irow == 0) /* bulk operation */
4419 if (SQL_POSITION == s.fOption)
4421 SC_set_error(s.stmt, STMT_INVALID_CURSOR_POSITION,
4422 "Bulk Position operations not allowed.", func);
4423 return SQL_ERROR;
4425 s.start_row = 0;
4426 s.end_row = rowsetSize - 1;
4427 } else
4429 if (SQL_ADD != s.fOption && s.irow > s.stmt->last_fetch_count)
4431 SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE,
4432 "Row value out of range", func);
4433 return SQL_ERROR;
4435 s.start_row = s.end_row = s.irow - 1;
4438 gdata_allocated = gdata_info->allocated;
4439 mylog("num_cols=%d gdatainfo=%d\n", QR_NumPublicResultCols(s.res),
4440 gdata_allocated);
4441 /* Reset for SQLGetData */
4442 if (gdata)
4444 for (i = 0; i < gdata_allocated; i++)
4445 gdata[i].data_left = -1;
4447 ret = SQL_SUCCESS;
4448 // VX_CLEANUP: It might be possible to extend the carnage from here.
4450 s.need_data_callback = FALSE;
4451 #define return DONT_CALL_RETURN_FROM_HERE???
4452 /* StartRollbackState(s.stmt); */
4453 ret = spos_callback(SQL_SUCCESS, &s);
4454 #undef return
4455 if (s.stmt->internal)
4456 ret = DiscardStatementSvp(s.stmt, ret, FALSE);
4457 mylog("%s returning %d\n", func, ret);
4458 return ret;
4462 /* Sets options that control the behavior of cursors. */
4463 RETCODE SQL_API
4464 PGAPI_SetScrollOptions(HSTMT hstmt,
4465 SQLUSMALLINT fConcurrency,
4466 SQLLEN crowKeyset, SQLUSMALLINT crowRowset)
4468 CSTR func = "PGAPI_SetScrollOptions";
4469 StatementClass *stmt = (StatementClass *) hstmt;
4471 mylog("%s: fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
4472 func, fConcurrency, crowKeyset, crowRowset);
4473 SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR,
4474 "SetScroll option not implemeted", func);
4476 return SQL_ERROR;
4480 /* Set the cursor name on a statement handle */
4481 RETCODE SQL_API
4482 PGAPI_SetCursorName(HSTMT hstmt,
4483 const SQLCHAR FAR * szCursor, SQLSMALLINT cbCursor)
4485 CSTR func = "PGAPI_SetCursorName";
4486 StatementClass *stmt = (StatementClass *) hstmt;
4488 mylog("%s: hstmt=%p, szCursor=%p, cbCursorMax=%d\n", func, hstmt,
4489 szCursor, cbCursor);
4491 if (!stmt)
4493 SC_log_error(func, NULL_STRING, NULL);
4494 return SQL_INVALID_HANDLE;
4497 SET_NAME(stmt->cursor_name,
4498 make_string(szCursor, cbCursor, NULL, 0));
4499 return SQL_SUCCESS;
4503 /* Return the cursor name for a statement handle */
4504 RETCODE SQL_API
4505 PGAPI_GetCursorName(HSTMT hstmt,
4506 SQLCHAR FAR * szCursor,
4507 SQLSMALLINT cbCursorMax,
4508 SQLSMALLINT FAR * pcbCursor)
4510 CSTR func = "PGAPI_GetCursorName";
4511 StatementClass *stmt = (StatementClass *) hstmt;
4512 size_t len = 0;
4513 RETCODE result;
4515 mylog("%s: hstmt=%p, szCursor=%p, cbCursorMax=%d, pcbCursor=%p\n",
4516 func, hstmt, szCursor, cbCursorMax, pcbCursor);
4518 if (!stmt)
4520 SC_log_error(func, NULL_STRING, NULL);
4521 return SQL_INVALID_HANDLE;
4523 result = SQL_SUCCESS;
4524 len = strlen(SC_cursor_name(stmt));
4526 if (szCursor)
4528 strncpy_null((char *)szCursor, SC_cursor_name(stmt), cbCursorMax);
4530 if (len >= cbCursorMax)
4532 result = SQL_SUCCESS_WITH_INFO;
4533 SC_set_error(stmt, STMT_TRUNCATED,
4534 "The buffer was too small for the GetCursorName.",
4535 func);
4539 if (pcbCursor)
4540 *pcbCursor = (SQLSMALLINT) len;
4543 * Because this function causes no db-access, there's
4544 * no need to call DiscardStatementSvp()
4547 return result;