2 * Description: This module contains functions related to
3 * retrieving result information through the ODBC API.
9 #include "dlg_specific.h"
11 #include "connection.h"
12 #include "statement.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
;
33 mylog("%s: entering...\n", func
);
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)
45 inolog("returning RowCount=%d\n", *pcrow
);
50 res
= SC_get_Curres(stmt
);
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.",
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
);
66 } else if (QR_NumResultCols(res
) > 0)
69 QR_get_num_total_tuples(res
) - res
->dl_count
;
70 mylog("RowCount=%d\n", *pcrow
);
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
);
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",
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
);
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
];
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
;
130 * This returns the number of columns associated with the database
131 * attached to "hstmt".
134 PGAPI_NumResultCols(HSTMT hstmt
, SQLSMALLINT FAR
* pccol
)
136 CSTR func
= "PGAPI_NumResultCols";
137 StatementClass
*stmt
= (StatementClass
*) hstmt
;
138 QResultClass
*result
;
141 RETCODE ret
= SQL_SUCCESS
;
143 mylog("%s: entering...\n", func
);
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)
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
,
168 parse_statement(stmt
, FALSE
);
171 if (SC_parsed_status(stmt
) != STMT_PARSE_FATAL
)
174 *pccol
= SC_get_IRDF(stmt
)->nfields
;
175 mylog("PARSE: %s: *pccol = %d\n", func
, *pccol
);
181 if (!SC_pre_execute_ok(stmt
, FALSE
, -1, func
))
187 result
= SC_get_Curres(stmt
);
188 *pccol
= QR_NumPublicResultCols(result
);
194 ret
= DiscardStatementSvp(stmt
, ret
, FALSE
);
200 * Return information about the database column the user wants
204 PGAPI_DescribeCol(HSTMT hstmt
,
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
;
220 mylog("no result exists!\n");
223 if (QR_NumResultCols(res
) <= icol
)
225 mylog("not enough columns in result!\n");
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
);
237 *pfSqlType
= SQL_INTEGER
;
238 *pcbColDef
= pgtype_column_size(stmt
, ft
, icol
, 10);
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
);
247 *pfSqlType
= SQL_DOUBLE
;
248 *pcbColDef
= pgtype_column_size(stmt
, ft
, icol
, 10);
249 *pibScale
= pgtype_precision(stmt
, ft
, icol
, 10);
251 case VX_TYPE_DATETIME
:
252 *pfSqlType
= SQL_VARCHAR
;
257 *pfSqlType
= SQL_VARCHAR
;
258 *pcbColDef
= QR_get_fieldsize(res
, icol
);
270 /* Returns result column descriptor information for a result set. */
272 PGAPI_ColAttributes(HSTMT hstmt
,
274 SQLUSMALLINT fDescType
,
276 SQLSMALLINT cbDescMax
,
277 SQLSMALLINT FAR
* pcbDesc
, SQLLEN FAR
* pfDesc
)
279 CSTR func
= "PGAPI_ColAttributes";
280 StatementClass
*stmt
= (StatementClass
*) hstmt
;
284 ConnectionClass
*conn
;
289 const char *p
= NULL
;
291 const FIELD_INFO
*fi
= NULL
;
292 const TABLE_INFO
*ti
= NULL
;
295 mylog("%s: entering..col=%d %d len=%d.\n", func
, icol
, fDescType
,
300 SC_log_error(func
, NULL_STRING
, NULL
);
301 return SQL_INVALID_HANDLE
;
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");
322 case SQL_DESC_OCTET_LENGTH
:
329 stmt
->options
.use_bookmarks
==
330 SQL_UB_VARIABLE
? SQL_BINARY
: SQL_INTEGER
;
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
359 if (fDescType
== SQL_DESC_COUNT
)
367 if (SC_parsed_status(stmt
) != STMT_PARSE_FATAL
&& irdflds
->fi
)
371 SC_set_error(stmt
, STMT_INVALID_COLUMN_NUMBER_ERROR
,
372 "Invalid column number in ColAttributes.",
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
);
385 BOOL build_fi
= FALSE
;
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
:
405 if (!SC_pre_execute_ok(stmt
, build_fi
, col_idx
, func
))
408 res
= SC_get_Curres(stmt
);
409 cols
= QR_NumPublicResultCols(res
);
412 * Column Count is a special case. The Column number is ignored
415 if (fDescType
== SQL_DESC_COUNT
)
425 SC_set_error(stmt
, STMT_INVALID_COLUMN_NUMBER_ERROR
,
426 "Invalid column number in ColAttributes.",
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
))
438 field_type
= FI_type(fi
);
441 mylog("colAttr: col %d field_type=%d fi,ti=%p,%p\n", col_idx
,
446 case SQL_COLUMN_AUTO_INCREMENT
: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
447 if (fi
&& fi
->auto_increment
)
450 value
= pgtype_auto_increment(stmt
, field_type
);
451 if (value
== -1) /* non-numeric becomes FALSE (ODBC Doc) */
453 mylog("AUTO_INCREMENT=%d\n", value
);
457 case SQL_COLUMN_CASE_SENSITIVE
: /* == SQL_DESC_CASE_SENSITIVE */
458 value
= pgtype_case_sensitive(stmt
, field_type
);
462 * This special case is handled above.
464 * case SQL_COLUMN_COUNT:
466 case SQL_COLUMN_DISPLAY_SIZE
: /* == SQL_DESC_DISPLAY_SIZE */
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
);
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
);
485 /* otherwise same as column name -- FALL THROUGH!!! */
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
->
495 QR_get_fieldname(res
, col_idx
);
497 mylog("%s: COLUMN_NAME = '%s'\n", func
, p
);
500 case SQL_COLUMN_LENGTH
:
503 0) ? fi
->length
: pgtype_buffer_length(stmt
,
508 /* if (-1 == value) I'm not sure which is right */
511 mylog("%s: col %d, column_length = %d\n", func
, col_idx
, value
);
514 case SQL_COLUMN_MONEY
: /* == SQL_DESC_FIXED_PREC_SCALE */
515 value
= pgtype_money(stmt
, field_type
);
516 inolog("COLUMN_MONEY=%d\n", value
);
519 case SQL_DESC_NULLABLE
:
520 if (SC_has_outer_join(stmt
))
524 fi
? fi
->nullable
: pgtype_nullable(stmt
, field_type
);
525 inolog("COLUMN_NULLABLE=%d\n", value
);
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
);
533 case SQL_COLUMN_PRECISION
: /* in 2.x */
536 0) ? fi
->column_size
: pgtype_column_size(stmt
,
543 mylog("%s: col %d, column_size = %d\n", func
, col_idx
, value
);
546 case SQL_COLUMN_QUALIFIER_NAME
: /* == SQL_DESC_CATALOG_NAME */
547 p
= ti
? CurrCatString(conn
) : NULL_STRING
; /* empty string means *not supported* */
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
);
557 case SQL_COLUMN_SEARCHABLE
: /* == SQL_DESC_SEARCHABLE */
558 value
= pgtype_searchable(stmt
, field_type
);
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
);
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
);
572 case SQL_COLUMN_TYPE_NAME
: /* == SQL_DESC_TYPE_NAME */
573 p
= pgtype_to_name(stmt
, field_type
, fi
&& fi
->auto_increment
);
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) */
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;
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
)
600 fi
? SAFE_NAME(fi
->column_name
) : QR_get_fieldname(res
,
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
);
612 case SQL_DESC_BASE_COLUMN_NAME
:
614 p
= fi
? SAFE_NAME(fi
->column_name
) : QR_get_fieldname(res
,
617 mylog("%s: BASE_COLUMN_NAME = '%s'\n", func
, p
);
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
);
624 case SQL_DESC_LENGTH
: /* different from SQL_COLUMN_LENGTH */
627 0) ? fi
->length
: pgtype_desclength(stmt
, field_type
,
633 mylog("%s: col %d, desc_length = %d\n", func
, col_idx
, value
);
635 case SQL_DESC_OCTET_LENGTH
:
638 0) ? fi
->length
: pgtype_transfer_octet_length(stmt
,
644 mylog("%s: col %d, octet_length = %d\n", func
, col_idx
, value
);
646 case SQL_DESC_PRECISION
: /* different from SQL_COLUMN_PRECISION */
647 if (value
= FI_precision(fi
), value
<= 0)
649 pgtype_precision(stmt
, field_type
, col_idx
,
654 mylog("%s: col %d, desc_precision = %d\n", func
, col_idx
,
657 case SQL_DESC_SCALE
: /* different from SQL_COLUMN_SCALE */
658 value
= pgtype_scale(stmt
, field_type
, col_idx
);
662 case SQL_DESC_LOCAL_TYPE_NAME
:
663 p
= pgtype_to_name(stmt
, field_type
, fi
&& fi
->auto_increment
);
666 value
= pgtype_to_sqldesctype(stmt
, field_type
, col_idx
);
668 case SQL_DESC_NUM_PREC_RADIX
:
669 value
= pgtype_radix(stmt
, field_type
);
671 case SQL_DESC_LITERAL_PREFIX
:
672 p
= pgtype_literal_prefix(stmt
, field_type
);
674 case SQL_DESC_LITERAL_SUFFIX
:
675 p
= pgtype_literal_suffix(stmt
, field_type
);
677 case SQL_DESC_UNNAMED
:
678 value
= (fi
&& NAME_IS_NULL(fi
->column_name
)
680 column_alias
)) ? SQL_UNNAMED
:
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
);
688 SC_set_error(stmt
, STMT_INVALID_OPTION_IDENTIFIER
,
689 "ColAttribute for this type not implemented yet",
694 result
= SQL_SUCCESS
;
697 { /* char/binary data */
698 size_t len
= strlen(p
);
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.",
714 *pcbDesc
= (SQLSMALLINT
) len
;
726 /* Returns result data for a single column in the current row. */
728 PGAPI_GetData(HSTMT hstmt
,
731 PTR rgbValue
, SQLLEN cbValueMax
, SQLLEN FAR
* pcbValue
)
733 CSTR func
= "PGAPI_GetData";
735 StatementClass
*stmt
= (StatementClass
*) hstmt
;
740 RETCODE result
= SQL_SUCCESS
;
741 char get_bookmark
= FALSE
;
743 SQLSMALLINT target_type
;
745 mylog("%s: enter, stmt=%p icol=%d\n", func
, stmt
, icol
);
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.",
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",
771 if (SQL_ARD_TYPE
== fCType
)
774 BindInfoClass
*binfo
= NULL
;
776 opts
= SC_get_ARDF(stmt
);
778 binfo
= opts
->bookmark
;
779 else if (icol
<= opts
->allocated
&& opts
->bindings
)
780 binfo
= &opts
->bindings
[icol
- 1];
783 target_type
= binfo
->returntype
;
784 mylog("SQL_ARD_TYPE=%d\n", target_type
);
787 SC_set_error(stmt
, STMT_STATUS_ERROR
,
788 "GetData can't determine the type via ARD",
793 target_type
= fCType
;
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",
804 /* Make sure it is the bookmark data type */
808 case SQL_C_VARBOOKMARK
:
812 ("GetData Column 0 is type %d not of type SQL_C_BOOKMARK",
814 SC_set_error(stmt
, STMT_PROGRAM_TYPE_OUT_OF_RANGE
,
815 "Column 0 is not of type SQL_C_BOOKMARK",
823 /* use zero-based column numbers */
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
);
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.",
849 mylog(" num_rows = %d\n", num_rows
);
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)");
864 BOOL contents_get
= FALSE
;
868 if (SQL_C_BOOKMARK
== target_type
|| 4 <= cbValueMax
)
871 *((SQLULEN
*) rgbValue
) = SC_get_bookmark(stmt
);
875 *pcbValue
= sizeof(SQLULEN
);
878 result
= SQL_SUCCESS
;
881 SC_set_error(stmt
, STMT_TRUNCATED
,
882 "The buffer was too small for the GetData.",
884 result
= SQL_SUCCESS_WITH_INFO
;
889 field_type
= QR_get_field_type(res
, icol
);
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
,
904 result
= SQL_SUCCESS
;
907 case COPY_UNSUPPORTED_TYPE
:
908 SC_set_error(stmt
, STMT_RESTRICTED_DATA_TYPE_ERROR
,
909 "Received an unsupported type from Postgres.",
914 case COPY_UNSUPPORTED_CONVERSION
:
915 SC_set_error(stmt
, STMT_RESTRICTED_DATA_TYPE_ERROR
,
916 "Couldn't handle the necessary data type conversion.",
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
;
927 case COPY_GENERAL_ERROR
: /* error msg already filled in */
931 case COPY_NO_DATA_FOUND
:
932 /* SC_log_error(func, "no data found", stmt); */
933 result
= SQL_NO_DATA_FOUND
;
937 SC_set_error(stmt
, STMT_INTERNAL_ERROR
,
938 "Unrecognized return value from copy_and_convert_field.",
947 result
= DiscardStatementSvp(stmt
, result
, FALSE
);
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
;
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
);
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
);
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",
993 if (stmt
->status
== STMT_EXECUTING
)
995 SC_set_error(stmt
, STMT_SEQUENCE_ERROR
,
996 "Can't fetch while statement is still executing.",
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",
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
);
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
);
1030 retval
= DiscardStatementSvp(stmt
, retval
, FALSE
);
1034 static RETCODE SQL_API
1035 SC_pos_reload_needed(StatementClass
* stmt
, SQLULEN req_size
,
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
;
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",
1050 if (0 == res
->dl_count
)
1052 if (SQL_FETCH_PRIOR
== orientation
)
1054 if (sta
+ 1 >= (SQLLEN
) nth
)
1056 *nearest
= sta
+ 1 - nth
;
1060 return -(SQLLEN
) (sta
+ 1);
1063 nearp
= sta
- 1 + nth
;
1064 if (nearp
< num_tuples
)
1069 *nearest
= num_tuples
;
1070 return -(SQLLEN
) (num_tuples
- sta
);
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
])
1088 inolog("nearest=%d\n", *nearest
);
1097 if (!QR_once_reached_eof(res
))
1098 num_tuples
= INT_MAX
;
1100 i
< res
->dl_count
&& *nearest
>= (SQLLEN
) deleted
[i
];
1103 if (sta
<= (SQLLEN
) deleted
[i
])
1106 if (*nearest
>= num_tuples
)
1108 *nearest
= num_tuples
;
1109 count
= *nearest
- sta
;
1113 } else if (SQL_FETCH_PRIOR
== orientation
)
1115 for (i
= sta
, keyset
= res
->keyset
+ sta
; i
>= 0; i
--, keyset
--)
1119 status
& (CURS_SELF_DELETING
| CURS_SELF_DELETED
|
1120 CURS_OTHER_DELETED
)))
1123 inolog(" nearest=%d\n", *nearest
);
1131 for (i
= sta
, keyset
= res
->keyset
+ sta
;
1132 i
< num_tuples
; i
++, keyset
++)
1136 status
& (CURS_SELF_DELETING
| CURS_SELF_DELETED
|
1137 CURS_OTHER_DELETED
)))
1140 inolog(" nearest=%d\n", *nearest
);
1145 *nearest
= num_tuples
;
1147 inolog(" nearest not found\n");
1148 return -(SQLLEN
) count
;
1152 move_cursor_position_if_needed(StatementClass
* self
,
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;
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
;
1181 inolog("!!move_offset=%d calc=%d\n", res
->move_offset
,
1182 INT_MAX
- self
->rowset_start
);
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
));
1198 if (0 == move_offset
)
1200 if (move_offset
> 0)
1202 QR_set_move_forward(res
);
1203 res
->move_offset
= move_offset
;
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). */
1234 PGAPI_ExtendedFetch(HSTMT hstmt
,
1235 SQLUSMALLINT fFetchType
,
1237 SQLULEN FAR
* pcrow
,
1238 SQLUSMALLINT FAR
* rgfRowStatus
,
1239 SQLLEN bookmark_offset
, SQLLEN rowsetSize
)
1241 CSTR func
= "PGAPI_ExtendedFetch";
1242 StatementClass
*stmt
= (StatementClass
*) hstmt
;
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
;
1254 BOOL currp_is_valid
, reached_eof
;
1256 mylog("stmt=%p rowsetSize=%d\n", stmt
, rowsetSize
);
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.",
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.",
1287 opts
= SC_get_ARDF(stmt
);
1289 * If a bookmark colunmn is bound but bookmark usage is off, then
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",
1301 if (stmt
->status
== STMT_EXECUTING
)
1303 SC_set_error(stmt
, STMT_SEQUENCE_ERROR
,
1304 "Can't fetch while statement is still executing.",
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",
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
);
1328 /* Initialize to no rows fetched */
1330 for (i
= 0; i
< rowsetSize
; i
++)
1331 *(rgfRowStatus
+ i
) = SQL_ROW_NOROW
;
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;
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
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
,
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
;
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
);
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
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",
1406 SC_set_rowset_start(stmt
,
1408 0 ? 0 : (num_tuples
- rowsetSize
),
1410 } else if (QR_haskeyset(res
))
1413 getNthValid(res
, SC_get_rowset_start(stmt
) - 1,
1414 SQL_FETCH_PRIOR
, rowsetSize
, &rowset_start
),
1417 SC_set_error(stmt
, STMT_POS_BEFORE_RECORDSET
,
1418 "fetch prior and before the beggining",
1420 SC_set_rowset_start(stmt
, 0, TRUE
);
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",
1430 SC_set_rowset_start(stmt
, 0, TRUE
);
1432 SC_inc_rowset_start(stmt
, -rowsetSize
);
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
);
1442 case SQL_FETCH_LAST
:
1443 mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n",
1444 num_tuples
, stmt
->currTuple
);
1448 QR_set_move_from_the_last(res
);
1449 res
->move_offset
= rowsetSize
;
1451 SC_set_rowset_start(stmt
,
1453 0 ? 0 : (num_tuples
- rowsetSize
), TRUE
);
1456 case SQL_FETCH_ABSOLUTE
:
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 */
1464 EXTFETCH_RETURN_BOF(stmt
, res
)}
1465 /* Position before the desired row */
1468 if (getNthValid(res
, 0, SQL_FETCH_NEXT
, irow
, &rowset_start
)
1471 EXTFETCH_RETURN_EOF(stmt
, res
)} else
1472 should_set_rowset_start
= TRUE
;
1474 /* Position with respect to the end of the result set */
1478 (res
, num_tuples
- 1, SQL_FETCH_PRIOR
, -irow
,
1479 &rowset_start
) <= 0)
1481 EXTFETCH_RETURN_BOF(stmt
, res
)} else
1485 QR_set_move_from_the_last(res
);
1486 res
->move_offset
= -irow
;
1488 should_set_rowset_start
= TRUE
;
1493 case SQL_FETCH_RELATIVE
:
1496 * Refresh the current rowset -- not currently implemented,
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
;
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
;
1521 case SQL_FETCH_BOOKMARK
:
1523 SQLLEN bidx
= SC_resolve_bookmark(irow
);
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)
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
;
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
;
1556 SC_set_error(stmt
, STMT_FETCH_OUT_OF_RANGE
,
1557 "Unsupported PGAPI_ExtendedFetch Direction", func
);
1562 * CHECK FOR PROPER CURSOR STATE
1566 * Handle Declare Fetch style specially because the end is not really
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
))
1603 SQLLEN rowset_end
, req_size
;
1605 getNthValid(res
, rowset_start
, SQL_FETCH_NEXT
, rowsetSize
,
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
))
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
;
1627 stmt
->bind_row
= 0; /* set the binding location */
1628 result
= SC_fetch(stmt
);
1629 if (SQL_ERROR
== result
)
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
);
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
);
1645 for (i
= 0, fc_io
= 0;
1646 SQL_NO_DATA_FOUND
!= result
&& SQL_ERROR
!= result
; currp
++)
1649 currp_is_valid
= FALSE
;
1652 if (currp
< res
->num_cached_keys
)
1654 currp_is_valid
= TRUE
;
1655 res
->keyset
[currp
].status
&= ~CURS_IN_ROWSET
; /* Off the flag first */
1658 mylog("Umm current row is out of keyset\n");
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
)
1674 /* Determine Function status */
1675 if (result
== SQL_SUCCESS_WITH_INFO
)
1677 else if (result
== SQL_ERROR
)
1680 /* Determine Row Status */
1683 if (result
== SQL_ERROR
)
1684 *(rgfRowStatus
+ i
) = SQL_ROW_ERROR
;
1685 else if (currp_is_valid
)
1688 (res
->keyset
[currp
].status
& KEYSET_INFO_PUBLIC
);
1689 if (pstatus
!= 0 && pstatus
!= SQL_ROW_ADDED
)
1691 rgfRowStatus
[i
] = pstatus
;
1693 rgfRowStatus
[i
] = SQL_ROW_SUCCESS
;
1694 /* refresh the status */
1695 /* if (SQL_ROW_DELETED != pstatus) */
1696 res
->keyset
[currp
].status
&= (~KEYSET_INFO_PUBLIC
);
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 */
1703 if (i
>= rowsetSize
)
1705 stmt
->bind_row
= (SQLSETPOSIROW
) i
; /* set the binding location */
1706 result
= SC_fetch(stmt
);
1708 if (SQL_ERROR
== result
)
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 */
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 */
1728 inolog("pcrow=%d\n", i
);
1731 /* Only DeclareFetch should wind up here */
1732 result
= SQL_NO_DATA_FOUND
;
1736 result
= SQL_SUCCESS_WITH_INFO
;
1737 else if (SC_get_errornumber(stmt
) == STMT_POS_BEFORE_RECORDSET
)
1738 result
= SQL_SUCCESS_WITH_INFO
;
1740 result
= SQL_SUCCESS
;
1745 result
= DiscardStatementSvp(stmt
, result
, FALSE
);
1751 * This determines whether there are more results sets available for
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
;
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
)
1769 if (stmt
->multi_statement
< 0)
1770 PGAPI_NumParams(stmt
, &num_p
);
1771 if (stmt
->multi_statement
> 0)
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;
1787 PGAPI_FreeStmt(hstmt
, SQL_CLOSE
);
1788 ret
= SQL_NO_DATA_FOUND
;
1790 mylog("%s: returning %d\n", func
, ret
);
1796 * Stuff for updatable cursors.
1798 static Int2
getNumResultCols(const QResultClass
* res
)
1800 Int2 res_cols
= QR_NumPublicResultCols(res
);
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
);
1825 SQLLEN
ClearCachedRows(TupleField
* tuple
, int num_fields
,
1830 for (i
= 0; i
< num_fields
* num_rows
; i
++, tuple
++)
1834 inolog("freeing tuple[%d][%d].value=%p\n", i
/ num_fields
,
1835 i
% num_fields
, tuple
->value
);
1837 tuple
->value
= NULL
;
1844 SQLLEN
ReplaceCachedRows(TupleField
* otuple
, const TupleField
* ituple
,
1845 int num_fields
, SQLLEN num_rows
)
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
++)
1855 free(otuple
->value
);
1856 otuple
->value
= NULL
;
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
;
1870 int MoveCachedRows(TupleField
* otuple
, TupleField
* ituple
,
1871 Int2 num_fields
, SQLLEN num_rows
)
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
++)
1881 free(otuple
->value
);
1882 otuple
->value
= NULL
;
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
;
1897 static BOOL
tupleExists(const StatementClass
* stmt
,
1898 const KeySet
* keyset
)
1901 const TABLE_INFO
*ti
= stmt
->ti
[0];
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
);
1911 snprintf(selstr
, sizeof(selstr
),
1912 "select 1 from \"%s\" where ctid = '(%d,%d)'",
1913 SAFE_NAME(ti
->table_name
), keyset
->blocknum
,
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
)
1922 static BOOL
enlargeAdded(QResultClass
* res
, UInt4 number
,
1923 const StatementClass
* stmt
)
1926 KeySet
*added_keyset
;
1927 TupleField
*added_tuples
;
1928 int num_fields
= res
->num_fields
;
1930 alloc
= res
->ad_alloc
;
1932 alloc
= number
> 10 ? number
: 10;
1934 while (alloc
< number
)
1939 if (alloc
<= res
->ad_alloc
)
1941 if (added_keyset
= (KeySet
*)
1942 realloc(res
->added_keyset
, sizeof(KeySet
) * alloc
),
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
),
1957 added_keyset
= NULL
;
1959 res
->added_keyset
= added_keyset
;
1960 res
->added_tuples
= added_tuples
;
1966 res
->ad_alloc
= alloc
;
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
;
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
;
1984 if (QR_get_cursor(res
))
1985 index
= -(SQLLEN
) res
->ad_count
;
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
))
1996 if (ad_count
> 0 && 0 == res
->ad_alloc
)
1998 if (!enlargeAdded(res
, ad_count
+ 1, stmt
))
2000 added_keyset
= res
->added_keyset
;
2001 added_tuples
= res
->added_tuples
;
2003 keyset
= added_keyset
+ ad_count
;
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
,
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
);
2029 rmidx
= index
- res
->num_total_read
;
2030 if (rmidx
>= res
->ad_count
)
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;
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
);
2046 mylog("RemoveAdded removed=1 count=%d\n", res
->ad_count
);
2049 static void CommitAdded(QResultClass
* res
)
2051 KeySet
*added_keyset
;
2055 mylog("CommitAdded res=%p\n", res
);
2056 if (!res
|| !res
->added_keyset
)
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
)
2090 Int2 dl_count
, new_alloc
;
2092 KeySet
*deleted_keyset
;
2094 Int2 num_fields
= res
->num_fields
;
2096 inolog("AddDeleted %d\n", index
);
2099 dl_count
= res
->dl_count
;
2101 if (!QR_get_cursor(res
))
2107 QR_MALLOC_return_with_error(res
->deleted
, SQLULEN
,
2108 sizeof(SQLULEN
) * new_alloc
, res
,
2109 "Deleted index malloc error",
2111 QR_MALLOC_return_with_error(res
->deleted_keyset
, KeySet
,
2112 sizeof(KeySet
) * new_alloc
, res
,
2113 "Deleted keyset malloc error",
2115 deleted
= res
->deleted
;
2116 deleted_keyset
= res
->deleted_keyset
;
2117 res
->dl_alloc
= new_alloc
;
2120 if (dl_count
>= res
->dl_alloc
)
2122 new_alloc
= res
->dl_alloc
* 2;
2124 QR_REALLOC_return_with_error(res
->deleted
, SQLULEN
,
2125 sizeof(SQLULEN
) * new_alloc
,
2127 "Dleted index realloc error",
2129 deleted
= res
->deleted
;
2130 QR_REALLOC_return_with_error(res
->deleted_keyset
, KeySet
,
2131 sizeof(KeySet
) * new_alloc
,
2133 "Dleted KeySet realloc error",
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
)
2146 memmove(deleted
+ 1, deleted
, sizeof(SQLLEN
) * (dl_count
- i
));
2147 memmove(deleted_keyset
+ 1, deleted_keyset
,
2148 sizeof(KeySet
) * (dl_count
- i
));
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.
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;
2168 static void RemoveDeleted(QResultClass
* res
, SQLLEN index
)
2170 int i
, mv_count
, rm_count
= 0;
2172 SQLULEN
*deleted
, num_read
= QR_get_num_total_read(res
);
2173 KeySet
*deleted_keyset
;
2175 mylog("RemoveDeleted index=%d\n", index
);
2179 pidx
= num_read
- index
- 1;
2183 if (index
>= num_read
)
2184 midx
= num_read
- index
- 1;
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;
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
));
2206 mylog("RemoveDeleted removed count=%d,%d\n", rm_count
,
2210 static void CommitDeleted(QResultClass
* res
)
2214 KeySet
*deleted_keyset
;
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
)
2253 KeySet
*updated_keyset
;
2254 TupleField
*updated_tuples
= NULL
;
2256 alloc
= res
->up_alloc
;
2258 alloc
= number
> 10 ? number
: 10;
2260 while (alloc
< number
)
2264 if (alloc
<= res
->up_alloc
)
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
;
2278 if (updated_keyset
= (KeySet
*)
2279 realloc(res
->updated_keyset
, sizeof(KeySet
) * alloc
),
2283 res
->updated
= NULL
;
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
),
2294 res
->updated
= NULL
;
2295 free(res
->updated_keyset
);
2296 res
->updated_keyset
= NULL
;
2300 res
->updated
= updated
;
2301 res
->updated_keyset
= updated_keyset
;
2302 res
->updated_tuples
= updated_tuples
;
2303 res
->up_alloc
= alloc
;
2308 static void AddUpdated(StatementClass
* stmt
, SQLLEN index
)
2312 KeySet
*updated_keyset
, *keyset
;
2313 TupleField
*updated_tuples
= NULL
, *tuple_updated
, *tuple
;
2316 SQLLEN upd_idx
, upd_add_idx
;
2321 inolog("AddUpdated index=%d\n", index
);
2324 if (res
= SC_get_Curres(stmt
), !res
)
2328 kres_ridx
= GIdx2KResIdx(index
, stmt
, res
);
2329 if (kres_ridx
< 0 || kres_ridx
>= res
->num_cached_keys
)
2331 keyset
= res
->keyset
+ kres_ridx
;
2332 if (!QR_get_cursor(res
))
2334 up_count
= res
->up_count
;
2335 if (up_count
> 0 && 0 == res
->up_alloc
)
2337 num_fields
= res
->num_fields
;
2338 tuple_updated
= res
->backend_tuples
+ kres_ridx
* num_fields
;
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
)
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
;
2364 ~(CURS_SELF_ADDING
| CURS_SELF_UPDATING
|
2365 CURS_SELF_DELETING
);
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);
2387 if (!enlargeUpdated(res
, res
->up_count
+ 1, stmt
))
2389 updated
= res
->updated
;
2390 updated_keyset
= res
->updated_keyset
;
2391 updated_tuples
= res
->updated_tuples
;
2393 updated
[up_count
] = index
;
2394 updated_keyset
[up_count
] = *keyset
;
2395 updated_keyset
[up_count
].status
= status
;
2398 tuple
= updated_tuples
+ num_fields
* up_count
;
2399 memset(tuple
, 0, sizeof(TupleField
) * num_fields
);
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);
2429 pidx
= num_read
- index
- 1;
2433 if (index
>= num_read
)
2434 midx
= num_read
- index
- 1;
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
;
2445 updated_keyset
->blocknum
== keyset
->blocknum
&&
2446 updated_keyset
->offset
== keyset
->offset
)
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;
2457 memmove(updated
, updated
+ 1,
2458 sizeof(SQLULEN
) * mv_count
);
2459 memmove(updated_keyset
, updated_keyset
+ 1,
2460 sizeof(KeySet
) * mv_count
);
2462 memmove(updated_tuples
, updated_tuples
+ num_fields
,
2463 sizeof(TupleField
) * num_fields
* mv_count
);
2469 mylog("RemoveUpdatedAfter removed count=%d,%d\n", rm_count
,
2473 static void CommitUpdated(QResultClass
* res
)
2475 KeySet
*updated_keyset
;
2479 mylog("CommitUpdated res=%p\n", res
);
2482 if (!QR_get_cursor(res
))
2484 if (res
->up_count
<= 0)
2486 if (updated_keyset
= res
->updated_keyset
, !updated_keyset
)
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
)
2518 SQLLEN index
, kres_ridx
;
2524 inolog("DiscardRollback");
2525 if (QR_get_cursor(res
))
2533 if (0 == res
->rb_count
|| NULL
== res
->rollback
)
2535 rollback
= res
->rollback
;
2536 keyset
= res
->keyset
;
2537 for (i
= 0; i
< res
->rb_count
; i
++)
2539 index
= rollback
[i
].index
;
2541 kres_is_valid
= FALSE
;
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
;
2553 keyset
[kres_ridx
].status
&=
2554 ~(CURS_SELF_DELETING
| CURS_SELF_UPDATING
|
2556 keyset
[kres_ridx
].status
|=
2558 (CURS_SELF_DELETING
| CURS_SELF_UPDATING
|
2559 CURS_SELF_ADDING
)) << 3);
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
,
2570 static void UndoRollback(StatementClass
* stmt
, QResultClass
* res
,
2574 SQLLEN index
, ridx
, kres_ridx
;
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
)
2582 rollback
= res
->rollback
;
2583 keyset
= res
->keyset
;
2589 Int2 doubtp
, rollbps
;
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
)
2604 } else if (SQL_REFRESH
== rollback
[i
].option
)
2606 if (texist
|| doubtp
== i
)
2615 inolog(" doubtp=%d\n", doubtp
);
2618 inolog(" doubtp=%d,rollbp=%d\n", doubtp
, 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
);
2633 pidx
= res
->num_total_read
- index
- 1;
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
,
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
;
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
);
2679 kres_is_valid
= FALSE
;
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
);
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
)
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
--;
2709 } else if (SQL_REFRESH
== rollback
[i
].option
)
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
)
2727 sprintf(tidval
, "(%d,%d)", wkey
->blocknum
,
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
+
2735 qres
->backend_tuples
, num_fields
,
2737 wkey
->status
&= ~CURS_NEEDS_REREAD
;
2739 QR_Destructor(qres
);
2744 res
->rb_count
= rollbp
;
2748 res
->rollback
= NULL
;
2753 void ProcessRollback(ConnectionClass
* conn
, BOOL undo
, BOOL partial
)
2756 StatementClass
*stmt
;
2759 for (i
= 0; i
< conn
->num_stmts
; i
++)
2761 if (stmt
= conn
->stmts
[i
], !stmt
)
2763 for (res
= SC_get_Result(stmt
); res
; res
= res
->next
)
2766 UndoRollback(stmt
, res
, partial
);
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
,
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);
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
)
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
);
2804 else if ((flag
& USE_INSERTED_TID
) != 0)
2808 selstr
= (char *)malloc(len
);
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
);
2820 snprintf(selstr
, len
,
2821 "%s where ctid = currtid2('%s', '%s') %s",
2822 stmt
->load_statement
,
2823 SAFE_NAME(ti
->table_name
), tidval
, oideqstr
);
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
);
2837 SC_set_error(stmt
, STMT_INTERNAL_ERROR
,
2838 "can't find the add and updating row because of the lack of oid",
2843 mylog("selstr=%s\n", selstr
);
2844 qres
= CC_send_query(SC_get_conn(stmt
), selstr
, NULL
, 0, stmt
);
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";
2858 SQLLEN res_ridx
, kres_ridx
;
2861 QResultClass
*res
, *qres
;
2862 IRDFields
*irdflds
= SC_get_IRDF(stmt
);
2863 RETCODE ret
= SQL_ERROR
;
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
);
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
);
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
);
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
);
2892 } else if (0 != (res
->keyset
[kres_ridx
].status
& CURS_SELF_ADDING
))
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
);
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
);
2924 qres
= positioned_load(stmt
, 0, &oidint
, tid
);
2927 positioned_load(stmt
, use_ctid
? LATEST_TUPLE_LOAD
: 0,
2928 &oidint
, use_ctid
? tidval
: NULL
);
2929 if (!QR_command_maybe_successful(qres
))
2932 SC_replace_error_with_res(stmt
, STMT_ERROR_TAKEN_FROM_BACKEND
,
2933 "positioned_load failed", qres
, TRUE
);
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.
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
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
);
2961 MoveCachedRows(tuple_old
, tuple_new
, effective_fields
,
2966 SC_set_error(stmt
, STMT_ROW_VERSION_CHANGED
,
2967 "the content was deleted after last fetch",
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
);
2983 SC_pos_reload(StatementClass
* stmt
, SQLULEN global_ridx
, UInt2
* count
,
2986 return SC_pos_reload_with_tid(stmt
, global_ridx
, count
, logKind
,
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
);
2997 int j
, rowc
, rcnt
= 0;
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
++)
3014 if (res
->reload_count
> 0)
3016 for (j
= rowc
; j
< keys_per_fetch
; j
++)
3019 strcpy(sval
, ",NULL");
3021 strcpy(sval
, "NULL");
3022 sval
= strchr(sval
, '\0');
3025 rowc
= -1; /* end of loop */
3027 if (rowc
< 0 || rowc
>= keys_per_fetch
)
3032 qres
= CC_send_query(conn
, qval
, NULL
, CREATE_KEYSET
, stmt
);
3033 if (QR_command_maybe_successful(qres
))
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
;
3046 if (oid
== getOid(res
, k
))
3048 l
= GIdx2CacheIdx(k
, stmt
, res
);
3050 res
->backend_tuples
+
3051 res
->num_fields
* l
;
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
)
3060 tuple
->value
= tuplew
->value
;
3061 tuple
->len
= tuplew
->len
;
3062 tuplew
->value
= NULL
;
3065 res
->keyset
[k
].status
&= ~CURS_NEEDS_REREAD
;
3072 SC_set_error(stmt
, STMT_EXEC_ERROR
, "Data Load Error",
3075 QR_Destructor(qres
);
3078 QR_Destructor(qres
);
3093 if (res
->reload_count
> 0)
3094 keys_per_fetch
= res
->reload_count
;
3101 if (rows_per_fetch
>= pre_fetch_count
* 2)
3102 keys_per_fetch
= pre_fetch_count
;
3104 keys_per_fetch
= rows_per_fetch
;
3105 if (!keys_per_fetch
)
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
,
3115 "Couldn't alloc qval",
3117 sprintf(qval
, "PREPARE \"%s\"", planname
);
3118 sval
= strchr(qval
, '\0');
3119 for (j
= 0; j
< keys_per_fetch
; j
++)
3122 strcpy(sval
, "(tid");
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
++)
3133 strcpy(sval
, "($1");
3135 sprintf(sval
, ",$%d", j
+ 1);
3136 sval
= strchr(sval
, '\0');
3139 qres
= CC_send_query(conn
, qval
, NULL
, 0, stmt
);
3140 if (QR_command_maybe_successful(qres
))
3142 res
->reload_count
= keys_per_fetch
;
3145 SC_set_error(stmt
, STMT_EXEC_ERROR
,
3146 "Prepare for Data Load Error",
3149 QR_Destructor(qres
);
3152 QR_Destructor(qres
);
3154 allen
= 25 + 23 * keys_per_fetch
;
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
,
3163 "Couldn't alloc qval", -1);
3165 if (res
->reload_count
> 0)
3167 sprintf(qval
, "EXECUTE \"_KEYSET_%p\"(", res
);
3171 memcpy(qval
, stmt
->load_statement
, lodlen
);
3172 sval
= qval
+ lodlen
;
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
);
3182 sprintf(sval
, ",'(%u,%u)'", blocknum
, offset
);
3184 sprintf(sval
, "'(%u,%u)'", blocknum
, offset
);
3185 sval
= strchr(sval
, '\0');
3195 static RETCODE SQL_API
3196 SC_pos_reload_needed(StatementClass
* stmt
, SQLULEN req_size
,
3199 CSTR func
= "SC_pos_reload_needed";
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.",
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
);
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
)
3233 ((pre_fetch_count
- 1) / req_rows_size
+ 1) * req_rows_size
;
3234 limitrow
= RowIdx2GIdx(rows_per_fetch
, stmt
);
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
)
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
;
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
)
3262 for (i
= SC_get_rowset_start(stmt
), kres_ridx
=
3263 GIdx2KResIdx(i
, stmt
, res
); i
< limitrow
; i
++, kres_ridx
++)
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
;
3273 LoadFromKeyset(stmt
, res
, rows_per_fetch
, limitrow
), rowc
< 0)
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
)
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
;
3298 static RETCODE SQL_API
3299 SC_pos_newload(StatementClass
* stmt
, const UInt4
* oidint
, BOOL tidRef
,
3302 CSTR func
= "SC_pos_newload";
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
);
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
);
3324 positioned_load(stmt
,
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
);
3334 SQLLEN count
= QR_get_num_cached_tuples(qres
);
3336 QR_set_position(qres
, 0);
3339 int effective_fields
= res
->num_fields
;
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
))
3356 if (num_total_rows
==
3357 CacheIdx2GIdx(num_cached_rows
, stmt
, res
))
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
)
3377 if (res
->num_cached_keys
>= res
->count_keyset_allocated
)
3379 if (!res
->count_keyset_allocated
)
3380 tuple_size
= TUPLE_MALLOC_INC
;
3382 tuple_size
= res
->count_keyset_allocated
* 2;
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
++;
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
;
3405 tuple_size
= res
->count_backend_allocated
* 2;
3406 res
->backend_tuples
=
3407 (TupleField
*) realloc(res
->backend_tuples
,
3409 sizeof(TupleField
) *
3411 if (!res
->backend_tuples
)
3416 "Out of memory while reading tuples.",
3418 QR_Destructor(qres
);
3421 res
->count_backend_allocated
= tuple_size
;
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
++;
3436 } else if (0 == count
)
3437 ret
= SQL_NO_DATA_FOUND
;
3440 SC_set_error(stmt
, STMT_ROW_VERSION_CHANGED
,
3441 "the driver cound't identify inserted rows",
3445 /* stmt->currTuple = SC_get_rowset_start(stmt) + ridx; */
3447 QR_Destructor(qres
);
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
)
3460 QResultClass
*tres
= SC_get_Curres(ustmt
);
3461 const char *cmdstr
= QR_get_command(tres
);
3463 if (cmdstr
&& sscanf(cmdstr
, "UPDATE %d", &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);
3473 SC_pos_reload_with_tid(stmt
, global_ridx
,
3474 (UInt2
*) 0, SQL_UPDATE
,
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",
3484 if (stmt
->options
.cursor_type
==
3485 SQL_CURSOR_KEYSET_DRIVEN
)
3486 SC_pos_reload(stmt
, global_ridx
, (UInt2
*) 0, 0);
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
);
3500 /* SQL_NEED_DATA callback for SC_pos_update */
3504 StatementClass
*stmt
, *qstmt
;
3507 SQLULEN global_ridx
;
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
;
3518 mylog("pos_update_callback in\n");
3520 irow_update(ret
, s
->stmt
, s
->qstmt
, s
->irow
,
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
);
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
);
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
)
3552 s
->irdflds
->rowStatusArray
[s
->irow
] = SQL_ROW_UPDATED
;
3555 s
->irdflds
->rowStatusArray
[s
->irow
] = ret
;
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
;
3569 ConnectionClass
*conn
;
3570 ARDFields
*opts
= SC_get_ARDF(stmt
);
3571 BindInfoClass
*bindings
= opts
->bindings
;
3580 SQLLEN
*used
, kres_ridx
;
3581 Int4 bind_size
= opts
->bind_size
;
3585 s
.global_ridx
= global_ridx
;
3586 s
.irdflds
= SC_get_IRDF(s
.stmt
);
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
);
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
);
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
);
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
);
3621 getTid(s
.res
, kres_ridx
, &blocknum
, &pgoffset
);
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
));
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
);
3637 used
= LENADDR_SHIFT(used
, bind_size
* s
.irow
);
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
)
3644 sprintf(updstr
, "%s, \"%s\" = ?", updstr
,
3645 GET_NAME(fi
[i
]->column_name
));
3647 sprintf(updstr
, "%s \"%s\" = ?", updstr
,
3648 GET_NAME(fi
[i
]->column_name
));
3652 mylog("%d null bind\n", i
);
3654 conn
= SC_get_conn(s
.stmt
);
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
);
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
);
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
);
3693 used
= LENADDR_SHIFT(used
, bind_size
* s
.irow
);
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
,
3703 bindings
[i
].returntype
,
3704 pgtype_to_concise_type(s
.stmt
,
3707 fi
[i
]->column_size
>
3710 pgtype_column_size(s
.stmt
,
3713 (SQLSMALLINT
) fi
[i
]->
3721 s
.qstmt
->exec_start_row
= s
.qstmt
->exec_end_row
= s
.irow
;
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
,
3732 /* else if (ret != SQL_SUCCESS) this is unneccesary
3733 SC_error_copy(s.stmt, s.qstmt, TRUE); */
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
);
3746 SC_pos_delete(StatementClass
* stmt
,
3747 SQLSETPOSIROW irow
, SQLULEN global_ridx
)
3749 CSTR func
= "SC_pos_update";
3751 QResultClass
*res
, *qres
;
3752 ConnectionClass
*conn
= SC_get_conn(stmt
);
3753 IRDFields
*irdflds
= SC_get_IRDF(stmt
);
3758 UInt4 blocknum
, qflag
;
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
);
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
);
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
);
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
);
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
))
3802 "delete from \"%s\".\"%s\" where ctid = '(%u, %u)'",
3803 SAFE_NAME(ti
->schema_name
), SAFE_NAME(ti
->table_name
),
3806 sprintf(dltstr
, "delete from \"%s\" where ctid = '(%u, %u)'",
3807 SAFE_NAME(ti
->table_name
), blocknum
, offset
);
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
);
3817 // VX_CLEANUP: GO_INTO_TRANSACTION doesn't seem to be used elsewhere
3818 qres
= CC_send_query(conn
, dltstr
, NULL
, qflag
, stmt
);
3820 if (QR_command_maybe_successful(qres
))
3823 const char *cmdstr
= QR_get_command(qres
);
3825 if (cmdstr
&& sscanf(cmdstr
, "DELETE %d", &dltcnt
) == 1)
3830 SC_pos_reload(stmt
, global_ridx
, (UInt2
*) 0,
3832 if (!SQL_SUCCEEDED(tret
))
3834 } else if (dltcnt
== 0)
3836 SC_set_error(stmt
, STMT_ROW_VERSION_CHANGED
,
3837 "the content was changed before deletion",
3840 if (stmt
->options
.cursor_type
==
3841 SQL_CURSOR_KEYSET_DRIVEN
)
3842 SC_pos_reload(stmt
, global_ridx
, (UInt2
*) 0, 0);
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
);
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
)
3871 irdflds
->rowStatusArray
[irow
] = SQL_ROW_DELETED
;
3874 irdflds
->rowStatusArray
[irow
] = ret
;
3880 static RETCODE SQL_API
3881 irow_insert(RETCODE ret
, StatementClass
* stmt
, StatementClass
* istmt
,
3884 CSTR func
= "irow_insert";
3886 if (ret
!= SQL_ERROR
)
3889 OID oid
, *poid
= NULL
;
3890 ARDFields
*opts
= SC_get_ARDF(stmt
);
3891 QResultClass
*ires
= SC_get_Curres(istmt
), *tres
;
3893 BindInfoClass
*bookmark
;
3895 tres
= (ires
->next
? ires
->next
: ires
);
3896 cmdstr
= QR_get_command(tres
);
3898 sscanf(cmdstr
, "INSERT %u %d", &oid
, &addcnt
) == 2 &&
3901 ConnectionClass
*conn
= SC_get_conn(stmt
);
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
)
3918 if (SQL_NO_DATA_FOUND
== qret
)
3920 qret
= SC_pos_newload(stmt
, poid
, FALSE
, NULL
);
3921 if (SQL_ERROR
== qret
)
3924 bookmark
= opts
->bookmark
;
3925 if (bookmark
&& bookmark
->buffer
)
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
,
3937 bookmark
->returntype
,
3938 bookmark
->buffer
+ offset
,
3940 LENADDR_SHIFT(bookmark
->used
,
3942 LENADDR_SHIFT(bookmark
->used
,
3947 SC_set_error(stmt
, STMT_ERROR_TAKEN_FROM_BACKEND
,
3948 "SetPos insert return error", func
);
3954 /* SQL_NEED_DATA callback for SC_pos_add */
3958 StatementClass
*stmt
, *qstmt
;
3963 static RETCODE
pos_add_callback(RETCODE retcode
, void *para
)
3965 RETCODE ret
= retcode
;
3966 padd_cdata
*s
= (padd_cdata
*) para
;
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);
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
;
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
);
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
);
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
)
4009 s
->irdflds
->rowStatusArray
[s
->irow
] = SQL_ROW_ADDED
;
4012 s
->irdflds
->rowStatusArray
[s
->irow
] = ret
;
4019 RETCODE
SC_pos_add(StatementClass
* stmt
, SQLSETPOSIROW irow
)
4021 CSTR func
= "SC_pos_add";
4022 int num_cols
, add_cols
, i
;
4026 ConnectionClass
*conn
;
4028 ARDFields
*opts
= SC_get_ARDF(stmt
);
4030 BindInfoClass
*bindings
= opts
->bindings
;
4031 FIELD_INFO
**fi
= SC_get_IRDF(stmt
)->fi
;
4036 Int4 bind_size
= opts
->bind_size
;
4038 int func_cs_count
= 0;
4040 mylog("POS ADD fi=%p ti=%p\n", fi
, stmt
->ti
);
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
);
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
);
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
));
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
);
4074 if (opts
->row_offset_ptr
)
4075 offset
= *opts
->row_offset_ptr
;
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
);
4090 used
= LENADDR_SHIFT(used
, bind_size
* s
.irow
);
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
);
4098 sprintf(addstr
, "%s, \"%s\"", addstr
,
4099 GET_NAME(fi
[i
]->column_name
));
4101 sprintf(addstr
, "%s\"%s\"", addstr
,
4102 GET_NAME(fi
[i
]->column_name
));
4103 PGAPI_BindParameter(hstmt
, (SQLUSMALLINT
)++ add_cols
,
4105 bindings
[i
].returntype
,
4106 pgtype_to_concise_type(s
.stmt
,
4109 fi
[i
]->column_size
>
4111 column_size
: pgtype_column_size(s
.
4113 i
, UNKNOWNS_AS_MAX
),
4114 (SQLSMALLINT
) fi
[i
]->decimal_digits
,
4120 mylog("%d null bind\n", i
);
4123 #define return DONT_CALL_RETURN_FROM_HERE???
4124 ENTER_INNER_CONN_CS(conn
, func_cs_count
);
4127 sprintf(addstr
, "%s) values (", addstr
);
4128 for (i
= 0; i
< add_cols
; i
++)
4131 strcat(addstr
, ", ?");
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
;
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
);
4150 /* else if (ret != SQL_SUCCESS) this is unneccesary
4151 SC_error_copy(s.stmt, s.qstmt, TRUE); */
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
);
4163 CLEANUP_FUNC_CONN_CS(func_cs_count
, conn
);
4168 * Stuff for updatable cursors end.
4172 SC_pos_refresh(StatementClass
* stmt
, SQLSETPOSIROW irow
,
4173 SQLULEN global_ridx
)
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
;
4187 QResultClass
*res
= SC_get_Curres(stmt
);
4188 if (res
&& res
->keyset
)
4190 SQLLEN kres_ridx
= GIdx2KResIdx(global_ridx
, stmt
, res
);
4192 && kres_ridx
< QR_get_num_cached_tuples(res
))
4195 (CURS_NEEDS_REREAD
& res
->keyset
[kres_ridx
].status
))
4196 tuple_reload
= TRUE
;
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
)
4213 irdflds
->rowStatusArray
[irow
] = SQL_ROW_ERROR
;
4216 irdflds
->rowStatusArray
[irow
] = SQL_ROW_SUCCESS
;
4218 case SQL_SUCCESS_WITH_INFO
:
4220 irdflds
->rowStatusArray
[irow
] = ret
;
4228 /* SQL_NEED_DATA callback for PGAPI_SetPos */
4230 BOOL need_data_callback
, auto_commit_needed
;
4232 StatementClass
*stmt
;
4235 SQLLEN idx
, start_row
, end_row
, ridx
;
4237 SQLSETPOSIROW irow
, nrow
, processed
;
4240 RETCODE
spos_callback(RETCODE retcode
, void *para
)
4242 CSTR func
= "spos_callback";
4244 spos_cdata
*s
= (spos_cdata
*) para
;
4247 ConnectionClass
*conn
;
4248 SQLULEN global_ridx
;
4249 SQLLEN kres_ridx
, pos_ridx
= 0;
4252 mylog("%s: %d in\n", func
, s
->need_data_callback
);
4253 if (s
->need_data_callback
)
4256 if (SQL_ERROR
!= retcode
)
4264 s
->idx
= s
->nrow
= s
->processed
= 0;
4270 SC_set_error(s
->stmt
, STMT_SEQUENCE_ERROR
,
4271 "Passed res or opts for spos_callback is NULL",
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
))
4285 kres_ridx
= GIdx2KResIdx(global_ridx
, s
->stmt
, res
);
4286 if (kres_ridx
>= res
->num_cached_keys
)
4288 if (kres_ridx
>= 0) /* the row may be deleted and not in the rowset */
4291 (res
->keyset
[kres_ridx
].
4292 status
& CURS_IN_ROWSET
))
4297 if (s
->nrow
< s
->start_row
)
4304 if (0 != s
->irow
|| !opts
->row_operation_ptr
4305 || opts
->row_operation_ptr
[s
->nrow
] == SQL_ROW_PROCEED
)
4310 ret
= SC_pos_update(s
->stmt
, s
->nrow
, global_ridx
);
4313 ret
= SC_pos_delete(s
->stmt
, s
->nrow
, global_ridx
);
4316 ret
= SC_pos_add(s
->stmt
, s
->nrow
);
4319 ret
= SC_pos_refresh(s
->stmt
, s
->nrow
, global_ridx
);
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
);
4334 if (SQL_ERROR
!= ret
)
4337 conn
= SC_get_conn(s
->stmt
);
4338 if (s
->auto_commit_needed
)
4339 PGAPI_SetConnectOption(conn
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_ON
);
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
=
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
);
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.
4368 PGAPI_SetPos(HSTMT hstmt
,
4370 SQLUSMALLINT fOption
, SQLUSMALLINT fLock
)
4372 CSTR func
= "PGAPI_SetPos";
4374 ConnectionClass
*conn
;
4377 UInt2 gdata_allocated
;
4378 GetDataInfo
*gdata_info
;
4379 GetDataClass
*gdata
= NULL
;
4382 s
.stmt
= (StatementClass
*) hstmt
;
4385 SC_log_error(func
, NULL_STRING
, NULL
);
4386 return SQL_INVALID_HANDLE
;
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",
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
);
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
);
4426 s
.end_row
= rowsetSize
- 1;
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
);
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
),
4441 /* Reset for SQLGetData */
4444 for (i
= 0; i
< gdata_allocated
; i
++)
4445 gdata
[i
].data_left
= -1;
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
);
4455 if (s
.stmt
->internal
)
4456 ret
= DiscardStatementSvp(s
.stmt
, ret
, FALSE
);
4457 mylog("%s returning %d\n", func
, ret
);
4462 /* Sets options that control the behavior of cursors. */
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
);
4480 /* Set the cursor name on a statement handle */
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
);
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));
4503 /* Return the cursor name for a statement handle */
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
;
4515 mylog("%s: hstmt=%p, szCursor=%p, cbCursorMax=%d, pcbCursor=%p\n",
4516 func
, hstmt
, szCursor
, cbCursorMax
, pcbCursor
);
4520 SC_log_error(func
, NULL_STRING
, NULL
);
4521 return SQL_INVALID_HANDLE
;
4523 result
= SQL_SUCCESS
;
4524 len
= strlen(SC_cursor_name(stmt
));
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.",
4540 *pcbCursor
= (SQLSMALLINT
) len
;
4543 * Because this function causes no db-access, there's
4544 * no need to call DiscardStatementSvp()