2) Fix a bug in SQLColAttribute().
char ret = TRUE;
if (!CC_is_in_trans(self))
{
- QResultClass *res = CC_send_query(self, "BEGIN", NULL, TRUE);
+ QResultClass *res = CC_send_query(self, "BEGIN", NULL, CLEAR_RESULT_ON_ABORT);
mylog("CC_begin: sending BEGIN!\n");
if (res != NULL)
char ret = FALSE;
if (CC_is_in_trans(self))
{
- QResultClass *res = CC_send_query(self, "COMMIT", NULL, TRUE);
+ QResultClass *res = CC_send_query(self, "COMMIT", NULL, CLEAR_RESULT_ON_ABORT);
mylog("CC_commit: sending COMMIT!\n");
CC_set_no_trans(self);
{
if (CC_is_in_trans(self))
{
- QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, TRUE);
+ QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, CLEAR_RESULT_ON_ABORT);
mylog("CC_abort: sending ABORT!\n");
CC_set_no_trans(self);
*/
mylog("sending an empty query...\n");
- res = CC_send_query(self, " ", NULL, TRUE);
+ res = CC_send_query(self, " ", NULL, CLEAR_RESULT_ON_ABORT);
if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY)
{
mylog("got no result from the empty query. (probably database does not exist)\n");
* Multibyte handling is available ?
*/
#ifdef MULTIBYTE
- if (PG_VERSION_GE(self, 7.0))
+ if (PG_VERSION_GE(self, 6.4))
{
CC_lookup_characterset(self);
if (self->errornumber != 0)
if (self->client_encoding)
free(self->client_encoding);
self->client_encoding = NULL;
- if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, TRUE), res)
+ if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, CLEAR_RESULT_ON_ABORT), res)
{
self->client_encoding = strdup("UNICODE");
QR_Destructor(res);
else if (self->unicode)
{
self->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
- self->errormsg = "Unicode isn't supported before 7.0";
+ self->errormsg = "Unicode isn't supported before 6.4";
return 0;
}
#endif /* UNICODE_SUPPORT */
* 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
*/
QResultClass *
-CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_result_on_abort)
+CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag)
{
QResultClass *result_in = NULL,
*cmdres = NULL,
*retres = NULL,
*res = NULL;
+ BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0),
+ create_keyset = ((flag & CREATE_KEYSET) != 0);
char swallow,
*wq;
int id;
if (query_completed)
{
res->next = QR_Constructor();
+ if (create_keyset)
+ QR_set_haskeyset(res->next);
mylog("send_query: 'T' no result_in: res = %u\n", res->next);
if (!res->next)
{
}
if (!used_passed_result_object)
{
+ if (create_keyset)
+ QR_set_haskeyset(res);
if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL))
{
self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
char CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
char CC_get_error(ConnectionClass *self, int *number, char **message);
-QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL);
+QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag);
void CC_clear_error(ConnectionClass *self);
char *CC_create_errormsg(ConnectionClass *self);
int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
void CC_log_error(const char *func, const char *desc, const ConnectionClass *self);
int CC_get_max_query_len(const ConnectionClass *self);
+/* CC_send_query_options */
+#define CLEAR_RESULT_ON_ABORT 1L
+#define CREATE_KEYSET (1L << 1) /* create keyset for updatable curosrs */
#endif
#ifdef DRIVER_CURSOR_IMPLEMENT
BOOL search_from_pos = FALSE;
#endif /* DRIVER_CURSOR_IMPLEMENT */
+ Int4 from_pos = -1, where_pos = -1;
if (ci->disallow_premature)
prepare_dummy_cursor = stmt->pre_executing;
else if (!stmt->ti || stmt->ntab != 1)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
else
- search_from_pos = TRUE;
+ {
+ /** search_from_pos = TRUE; **/
+ from_pos = stmt->from_pos;
+ where_pos = stmt->where_pos;
+ }
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
#ifdef MULTIBYTE
make_encoded_str(&encstr, conn, old_statement);
#endif
-
for (opos = 0; opos < oldstmtlen; opos++)
{
+ if (from_pos == (Int4) opos)
+ {
+ CVT_APPEND_STR(", CTID, OID ");
+ }
+ else if (where_pos == (Int4) opos)
+ {
+ stmt->load_statement = malloc(npos + 1);
+ memcpy(stmt->load_statement, new_statement, npos);
+ stmt->load_statement[npos] = '\0';
+ }
#ifdef MULTIBYTE
oldchar = encoded_byte_check(&encstr, opos);
if (ENCODE_STATUS(encstr) != 0)
#ifdef DRIVER_CURSOR_IMPLEMENT
if (search_from_pos)
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+ if (!stmt->load_statement && from_pos >=0)
+ {
+ stmt->load_statement = malloc(npos + 1);
+ memcpy(stmt->load_statement, new_statement, npos);
+ stmt->load_statement[npos] = '\0';
+ }
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (prepare_dummy_cursor && SC_is_pre_executable(stmt))
{
case STMT_INVALID_CURSOR_STATE_ERROR:
strcpy(szSqlState, "24000");
break;
+ case STMT_ERROR_IN_ROW:
+ strcpy(szSqlState, "01S01");
+ break;
case STMT_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
if (self->statement)
free(self->statement);
+ if (self->stmt_with_params)
+ free(self->stmt_with_params);
+ self->stmt_with_params = NULL;
+ if (self->load_statement)
+ free(self->load_statement);
+ self->load_statement = NULL;
self->statement = make_string(szSqlStr, cbSqlStr, NULL);
if (!self->statement)
if (stmt->statement)
free(stmt->statement);
+ if (stmt->stmt_with_params)
+ free(stmt->stmt_with_params);
+ stmt->stmt_with_params = NULL;
+ if (stmt->load_statement)
+ free(stmt->load_statement);
+ stmt->load_statement = NULL;
/*
* keep a copy of the un-parametrized statement, in case they try to
BOOL in_trans = CC_is_in_trans(conn);
BOOL issued_begin = FALSE,
begin_included = FALSE;
- QResultClass *res;
+ QResultClass *res, *curres;
if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
begin_included = TRUE;
}
/* we are now in a transaction */
CC_set_in_trans(conn);
- res = CC_send_query(conn, stmt->stmt_with_params, NULL, TRUE);
+ res = CC_send_query(conn, stmt->stmt_with_params, NULL, CLEAR_RESULT_ON_ABORT);
if (!res)
{
CC_abort(conn);
return SQL_ERROR;
}
SC_set_Result(stmt, res);
+ for (curres = res; !curres->num_fields; curres = curres->next)
+ ;
+ SC_set_Curres(stmt, curres);
if (CC_is_in_autocommit(conn))
{
if (issued_begin)
{
mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
- res = CC_send_query(conn, stmt_string, NULL, TRUE);
+ res = CC_send_query(conn, stmt_string, NULL, CLEAR_RESULT_ON_ABORT);
CC_set_no_trans(conn);
if (!res)
return ret;
if (!conn->server_encoding)
{
- if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res)
+ if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
if (!conn->server_encoding)
return ret;
sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
- bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+ bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
if (!bError && continueExec)
{
sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName);
- if (res = CC_send_query(conn, query, NULL, TRUE), res)
+ if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
strcpy(saveoid, QR_get_value_backend_row(res, 0, 0));
}
/* restore the client encoding */
sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
- bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+ bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
if (bError || !continueExec)
return ret;
sprintf(query, "select relname from pg_class where OID = %s", saveoid);
- if (res = CC_send_query(conn, query, NULL, TRUE), res)
+ if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
{
return ret;
if (!conn->server_encoding)
{
- if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res)
+ if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
if (!conn->server_encoding)
return ret;
sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
- bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+ bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
if (!bError && continueExec)
{
sprintf(query, "select attrelid, attnum from pg_class, pg_attribute "
"where relname = '%s' and attrelid = pg_class.oid "
"and attname = '%s'", serverTableName, serverColumnName);
- if (res = CC_send_query(conn, query, NULL, TRUE), res)
+ if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
{
}
/* restore the cleint encoding */
sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
- bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+ bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
if (bError || !continueExec)
return ret;
sprintf(query, "select attname from pg_attribute where attrelid = %s and attnum = %s", saveattrelid, saveattnum);
- if (res = CC_send_query(conn, query, NULL, TRUE), res)
+ if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
{
if (QR_get_num_tuples(res) > 0)
{
" case when prorettype = 0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc");
my_strcat(proc_query, " where proname like '%.*s'", szProcName, cbProcName);
- if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res)
+ if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "PGAPI_Procedures query error";
my_strcat(proc_query, " relname like '%.*s' and", esc_table_name, escTbnamelen);
}
strcat(proc_query, " pg_user.usesysid = relowner");
- if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res)
+ if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
{
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "PGAPI_TablePrivileges query error";
}
strncpy_null(proc_query, "select usename, usesysid, usesuper from pg_user", sizeof(proc_query));
tablecount = QR_get_num_tuples(res);
- if (allures = CC_send_query(conn, proc_query, NULL, TRUE), !allures)
+ if (allures = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !allures)
{
QR_Destructor(res);
stmt->errornumber = STMT_EXEC_ERROR;
char *grolist, *uid, *delm;
snprintf(proc_query, sizeof(proc_query) - 1, "select grolist from pg_group where groname = '%s'", user);
- if (gres = CC_send_query(conn, proc_query, NULL, TRUE))
+ if (gres = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT))
{
grolist = QR_get_value_backend_row(gres, 0, 0);
if (grolist && grolist[0] == '{')
break;
case SQL_KEYSET_CURSOR_ATTRIBUTES1:
len = 4;
- value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
+ value = 0;
+ if (ci->updatable_cursors || ci->drivers.lie)
+ value |= (SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
| SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
| SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
| SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
- | SQL_CA1_POS_REFRESH;
+ | SQL_CA1_POS_REFRESH);
if (ci->drivers.lie)
- value |=
- ( SQL_CA1_BULK_ADD
+ value |= (SQL_CA1_BULK_ADD
| SQL_CA1_BULK_UPDATE_BY_BOOKMARK
| SQL_CA1_BULK_DELETE_BY_BOOKMARK
| SQL_CA1_BULK_FETCH_BY_BOOKMARK
break;
case SQL_KEYSET_CURSOR_ATTRIBUTES2:
len = 4;
- value = SQL_CA2_OPT_ROWVER_CONCURRENCY
+ value = 0;
+ if (ci->updatable_cursors || ci->drivers.lie)
+ value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY
| SQL_CA2_SENSITIVITY_ADDITIONS
| SQL_CA2_SENSITIVITY_DELETIONS
- | SQL_CA2_SENSITIVITY_UPDATES;
+ | SQL_CA2_SENSITIVITY_UPDATES);
if (ci->drivers.lie)
- value |=
- ( SQL_CA2_READ_ONLY_CONCURRENCY
+ value |= (SQL_CA2_READ_ONLY_CONCURRENCY
| SQL_CA2_LOCK_CONCURRENCY
| SQL_CA2_OPT_VALUES_CONCURRENCY
| SQL_CA2_MAX_ROWS_SELECT
len = 4;
value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
| SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
- | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
- | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
- | SQL_CA1_POS_REFRESH;
+ | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION;
+ if (ci->updatable_cursors)
+ value |= (SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
+ | SQL_CA1_POS_REFRESH);
break;
case SQL_STATIC_CURSOR_ATTRIBUTES2:
len = 4;
- value = SQL_CA2_OPT_ROWVER_CONCURRENCY
+ value = 0;
+ if (ci->updatable_cursors)
+ value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY
| SQL_CA2_SENSITIVITY_ADDITIONS
| SQL_CA2_SENSITIVITY_DELETIONS
- | SQL_CA2_SENSITIVITY_UPDATES;
+ | SQL_CA2_SENSITIVITY_UPDATES);
break;
case SQL_ODBC_INTERFACE_CONFORMANCE:
char *encstr = NULL;
QResultClass *res;
- res = CC_send_query(self, "select pg_client_encoding()", NULL, TRUE);
+ res = CC_send_query(self, "select pg_client_encoding()", NULL, CLEAR_RESULT_ON_ABORT);
if (res)
{
char *enc = QR_get_value_backend_row(res, 0, 0);
return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value);
}
-static RETCODE SQL_API
-ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
- SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
- RETCODE ret = SQL_SUCCESS;
- PTR tptr;
- switch (FieldIdentifier)
- {
- case SQL_DESC_ARRAY_SIZE:
- stmt->options.rowset_size = (SQLUINTEGER) Value;
- break;
- case SQL_DESC_ARRAY_STATUS_PTR:
- stmt->options.row_operation_ptr = Value;
- break;
- case SQL_DESC_BIND_OFFSET_PTR:
- stmt->options.row_offset_ptr = Value;
- break;
- case SQL_DESC_BIND_TYPE:
- stmt->options.bind_size = (SQLUINTEGER) Value;
- break;
-
- case SQL_DESC_DATA_PTR:
- if (!RecNumber)
- stmt->bookmark.buffer = Value;
- else
- stmt->bindings[RecNumber - 1].buffer = Value;
- break;
- case SQL_DESC_INDICATOR_PTR:
- if (!RecNumber)
- tptr = stmt->bookmark.used;
- else
- tptr = stmt->bindings[RecNumber - 1].used;
- if (Value != tptr)
- {
- ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR";
- }
- break;
- case SQL_DESC_OCTET_LENGTH_PTR:
- if (!RecNumber)
- stmt->bookmark.used = Value;
- else
- stmt->bindings[RecNumber - 1].used = Value;
- break;
- default:ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- stmt->errormsg = "not implemedted yet";
- }
- return ret;
-}
-
-static RETCODE SQL_API
-APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
- SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
- RETCODE ret = SQL_SUCCESS;
- switch (FieldIdentifier)
- {
- case SQL_DESC_ARRAY_SIZE:
- stmt->options.paramset_size = (SQLUINTEGER) Value;
- break;
- case SQL_DESC_ARRAY_STATUS_PTR:
- stmt->options.param_operation_ptr = Value;
- break;
- case SQL_DESC_BIND_OFFSET_PTR:
- stmt->options.param_offset_ptr = Value;
- break;
- case SQL_DESC_BIND_TYPE:
- stmt->options.param_bind_type = (SQLUINTEGER) Value;
- break;
-
- case SQL_DESC_DATA_PTR:
- if (stmt->parameters_allocated < RecNumber)
- PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0);
- stmt->parameters[RecNumber - 1].buffer = Value;
- break;
- case SQL_DESC_INDICATOR_PTR:
- if (stmt->parameters_allocated < RecNumber ||
- Value != stmt->parameters[RecNumber - 1].used)
- {
- ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR";
- }
- break;
- case SQL_DESC_OCTET_LENGTH_PTR:
- if (stmt->parameters_allocated < RecNumber)
- PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0);
- stmt->parameters[RecNumber - 1].used = Value;
- break;
- default:ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- }
- return ret;
-}
-
-static RETCODE SQL_API
-IRDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
- SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
- RETCODE ret = SQL_SUCCESS;
- switch (FieldIdentifier)
- {
- case SQL_DESC_ARRAY_STATUS_PTR:
- stmt->options.rowStatusArray = (SQLUSMALLINT *) Value;
- break;
- case SQL_DESC_ROWS_PROCESSED_PTR:
- stmt->options.rowsFetched = (SQLUINTEGER *) Value;
- break;
- default:ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- }
- return ret;
-}
-
-static RETCODE SQL_API
-IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
- SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
- RETCODE ret = SQL_SUCCESS;
- switch (FieldIdentifier)
- {
- case SQL_DESC_ARRAY_STATUS_PTR:
- stmt->options.param_status_ptr = (SQLUSMALLINT *) Value;
- break;
- case SQL_DESC_ROWS_PROCESSED_PTR:
- stmt->options.param_processed_ptr = (SQLUINTEGER *) Value;
- break;
- default:ret = SQL_ERROR;
- stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
- }
- return ret;
-}
-
/* new function */
RETCODE SQL_API
SQLSetDescField(SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
PTR Value, SQLINTEGER BufferLength)
{
- RETCODE ret = SQL_SUCCESS;
- HSTMT hstmt;
- SQLUINTEGER descType;
- StatementClass *stmt;
- static const char *func = "SQLSetDescField";
+ RETCODE ret;
mylog("[[SQLSetDescField]] h=%u rec=%d field=%d val=%x\n", DescriptorHandle, RecNumber, FieldIdentifier, Value);
- hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType);
- mylog("stmt=%x type=%d\n", hstmt, descType);
- stmt = (StatementClass *) hstmt;
- switch (descType)
- {
- case SQL_ATTR_APP_ROW_DESC:
- ret = ARDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
- break;
- case SQL_ATTR_APP_PARAM_DESC:
- ret = APDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
- break;
- case SQL_ATTR_IMP_ROW_DESC:
- ret = IRDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
- break;
- case SQL_ATTR_IMP_PARAM_DESC:
- ret = IPDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
- break;
- default:ret = SQL_ERROR;
- stmt->errornumber = STMT_INTERNAL_ERROR;
- stmt->errormsg = "Error not implemented";
- }
- if (ret == SQL_ERROR)
- SC_log_error(func, "", stmt);
+ ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, FieldIdentifier,
+ Value, BufferLength);
return ret;
}
PTR Data, SQLINTEGER *StringLength,
SQLINTEGER *Indicator)
{
- const char *func = "SQLSetDescField";
+ const char *func = "SQLSetDescRec";
mylog("[[SQLSetDescRec]]\n");
mylog("Error not implemented\n");
static char *func = "set_statement_option";
char changed = FALSE;
ConnInfo *ci = NULL;
+ UDWORD setval;
if (conn)
ci = &(conn->connInfo);
* positioned update isn't supported so cursor concurrency is
* read-only
*/
- mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
- if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER)
- {
- if (conn)
- conn->stmtOptions.scroll_concurrency = vParam;
- if (stmt)
- stmt->options.scroll_concurrency = vParam;
- }
- else
- {
- if (conn)
- conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER;
- if (stmt)
- stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER;
+ mylog("SetStmtOption(): SQL_CONCURRENCY = %d ", vParam);
+ setval = SQL_CONCUR_READ_ONLY;
+ if (SQL_CONCUR_READ_ONLY == vParam)
+ ;
+ if (ci->drivers.lie)
+ setval = vParam;
+ else if (ci->updatable_cursors)
+ setval = SQL_CONCUR_ROWVER;
+ if (conn)
+ conn->stmtOptions.scroll_concurrency = setval;
+ else if (stmt)
+ stmt->options.scroll_concurrency = setval;
+ if (setval != vParam)
changed = TRUE;
- }
+ mylog("-> %d\n", setval);
break;
case SQL_CURSOR_TYPE:
* if declare/fetch, then type can only be forward. otherwise,
* it can only be forward or static.
*/
- mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
-
+ mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d ", vParam);
+ setval = SQL_CURSOR_FORWARD_ONLY;
if (ci->drivers.lie)
- {
- if (conn)
- conn->stmtOptions.cursor_type = vParam;
- if (stmt)
- stmt->options.cursor_type = vParam;
- }
- else
- {
- if (ci->drivers.use_declarefetch)
- {
- if (conn)
- conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
- if (stmt)
- stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+ setval = vParam;
+ else if (ci->drivers.use_declarefetch)
+ ;
+ else if (SQL_CURSOR_STATIC == vParam)
+ setval = vParam;
+ /** else if (SQL_CURSOR_KEYSET_DRIVEN == vParam && ci->updatable)
+ setval = vParam; **/
- if (vParam != SQL_CURSOR_FORWARD_ONLY)
- changed = TRUE;
- }
- else
- {
- if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
- {
- if (conn)
- conn->stmtOptions.cursor_type = vParam; /* valid type */
- if (stmt)
- stmt->options.cursor_type = vParam; /* valid type */
- }
- else
- {
- if (conn)
- conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
- if (stmt)
- stmt->options.cursor_type = SQL_CURSOR_STATIC;
-
- changed = TRUE;
- }
- }
- }
+ if (conn)
+ conn->stmtOptions.cursor_type = setval;
+ else if (stmt)
+ stmt->options.cursor_type = setval;
+ if (setval != vParam)
+ changed = TRUE;
+ mylog("-> %d\n", setval);
break;
case SQL_KEYSET_SIZE: /* ignored, but saved and returned */
in_distinct = FALSE,
in_on = FALSE,
in_from = FALSE,
- from_found = FALSE,
in_where = FALSE,
in_table = FALSE;
char in_field = FALSE,
i,
k = 0,
n,
- first_where = 0,
blevel = 0;
FIELD_INFO **fi;
TABLE_INFO **ti;
HSTMT hcol_stmt;
StatementClass *col_stmt;
RETCODE result;
+ BOOL updatable = TRUE;
mylog("%s: entering...\n", func);
stmt->nfld = 0;
stmt->ntab = 0;
+ stmt->from_pos = -1;
+ stmt->where_pos = -1;
#ifdef MULTIBYTE
while (pptr = ptr, (ptr = getNextToken(conn->ccsc, pptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) != NULL)
if (!stricmp(token, "distinct"))
{
in_distinct = TRUE;
+ updatable = FALSE;
mylog("DISTINCT\n");
continue;
{
in_select = FALSE;
in_from = TRUE;
- if (!from_found &&
+ if (stmt->from_pos < 0 &&
(!strnicmp(pptr, "from", 4)))
{
mylog("First ");
- from_found = TRUE;
+ stmt->from_pos = pptr - stmt->statement;
}
mylog("FROM\n");
in_from = FALSE;
in_where = TRUE;
- if (!first_where &&
- (!stricmp(token, "where")))
- first_where = ptr - stmt->statement;
+ if (!stricmp(token, "where"))
+ {
+ if (stmt->where_pos < 0)
+ stmt->where_pos = pptr - stmt->statement;
+ }
+ else if (stricmp(token, "order"))
+ updatable = FALSE;
mylog("WHERE...\n");
break;
*/
/* Call SQLColumns for each table and store the result */
+ if (stmt->ntab > 1)
+ updatable = FALSE;
+ else if (stmt->from_pos < 0)
+ updatable = FALSE;
for (i = 0; i < stmt->ntab; i++)
{
/* See if already got it */
*/
for (i = 0; i < stmt->nfld;)
{
+ fi[i]->updatable = updatable;
/* Dont worry about functions or quotes */
if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
{
+ fi[i]->updatable = FALSE;
i++;
continue;
}
mylog("about to copy at %d\n", n + i);
getColInfo(the_ti->col_info, fi[n + i], n);
+ fi[n + i]->updatable = updatable;
mylog("done copying\n");
}
else if (fi[i]->ti)
{
if (!searchColInfo(fi[i]->ti->col_info, fi[i]))
+ {
parse = FALSE;
-
+ fi[i]->updatable = FALSE;
+ }
i++;
}
/* Don't know the table -- search all tables in "from" list */
else
{
- parse = FALSE;
for (k = 0; k < stmt->ntab; k++)
{
if (searchColInfo(ti[k]->col_info, fi[i]))
{
fi[i]->ti = ti[k]; /* now know the table */
- parse = TRUE;
break;
}
}
+ if (k >= stmt->ntab)
+ {
+ parse = FALSE;
+ fi[i]->updatable = FALSE;
+ }
i++;
}
}
*
* Comments: See "notice.txt" for copyright and license information.
*
- * $Id: psqlodbc.h,v 1.59 2002/03/11 10:25:57 inoue Exp $
+ * $Id: psqlodbc.h,v 1.60 2002/03/14 05:42:03 inoue Exp $
*
*/
typedef struct EnvironmentClass_ EnvironmentClass;
typedef struct TupleNode_ TupleNode;
typedef struct TupleField_ TupleField;
+typedef struct KeySet_ KeySet;
typedef struct col_info COL_INFO;
typedef struct lo_arg LO_ARG;
SQLAllocStmt @3
SQLBindCol @4
SQLCancel @5
-SQLColAttributes @6
+; SQLColAttributes @6 */
SQLConnect @7
SQLDescribeCol @8
SQLDisconnect @9
SQLAllocStmt @3
SQLBindCol @4
SQLCancel @5
-SQLColAttributes @6
+; SQLColAttributes @6
SQLConnect @7
SQLDescribeCol @8
SQLDisconnect @9
rv->cache_size = 0;
rv->rowset_size = 1;
+ rv->haskeyset = 0;
+ rv->keyset = NULL;
}
mylog("exit QR_Constructor\n");
free(self->backend_tuples);
self->backend_tuples = NULL;
}
+ if (self->keyset)
+ {
+ free(self->keyset);
+ self->keyset = NULL;
+ }
self->fcount = 0;
mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
self->count_allocated = 0;
self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
+ if (self->haskeyset)
+ self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size);
if (!self->backend_tuples)
{
self->status = PGRES_FATAL_ERROR;
sprintf(buf, "close %s", self->cursor);
mylog("QResult: closing cursor: '%s'\n", buf);
- res = CC_send_query(self->conn, buf, NULL, TRUE);
+ res = CC_send_query(self->conn, buf, NULL, CLEAR_RESULT_ON_ABORT);
self->inTuples = FALSE;
self->currTuple = -1;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
+ if (self->haskeyset)
+ self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * self->cache_size);
self->count_allocated = self->cache_size;
}
sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
qi.row_size = self->cache_size;
qi.result_in = self;
qi.cursor = NULL;
- res = CC_send_query(self->conn, fetch, &qi, TRUE);
+ res = CC_send_query(self->conn, fetch, &qi, CLEAR_RESULT_ON_ABORT);
if (res == NULL)
{
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
+ if (self->haskeyset)
+ self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * tuple_size);
self->count_allocated = tuple_size;
}
{
Int2 field_lf;
TupleField *this_tuplefield;
+ KeySet *this_keyset = NULL;
char bmp,
bitmap[MAX_FIELDS]; /* Max. len of the bitmap */
Int2 bitmaplen; /* len of the bitmap in bytes */
/* set the current row to read the fields into */
this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
+ if (self->haskeyset)
+ {
+ this_keyset = self->keyset + self->fcount;
+ this_keyset->status = 0;
+ }
bitmaplen = (Int2) num_fields / BYTELEN;
if ((num_fields % BYTELEN) > 0)
else
bmp <<= 1;
}
+ if (this_keyset)
+ {
+ if (this_tuplefield[num_fields - 2].value)
+ sscanf(this_tuplefield[num_fields - 2].value, "(%u,%hu)",
+ &this_keyset->blocknum, &this_keyset->offset);
+ if (this_tuplefield[num_fields - 1].value)
+ sscanf(this_tuplefield[num_fields - 1].value, "%u",
+ &this_keyset->oid);
+ }
self->currTuple++;
return TRUE;
}
char inTuples; /* is a fetch of rows from the backend in
* progress? */
char aborted; /* was aborted? */
+ char haskeyset; /* this result contains keyset ? */
+ KeySet *keyset;
+
};
#define QR_get_fields(self) (self->fields)
#define QR_set_status(self, condition) ( self->status = condition )
#define QR_set_message(self, message_) ( self->message = message_)
#define QR_set_aborted(self, aborted_) ( self->aborted = aborted_)
+#define QR_set_haskeyset(self) (self->haskeyset = TRUE)
#define QR_get_message(self) (self->message)
#define QR_get_command(self) (self->command)
*pccol = QR_NumResultCols(result);
/* updatable cursors */
- if (ci->updatable_cursors &&
- stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ if (result->keyset)
*pccol -= 2;
}
*/
#if (ODBCVER >= 0x0300)
- if (0 == icol) /* bookmark column */
+ if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
{
switch (fDescType)
{
* Column Count is a special case. The Column number is ignored
* in this case.
*/
+#if (ODBCVER >= 0x0300)
+ if (fDescType == SQL_DESC_COUNT)
+#else
if (fDescType == SQL_COLUMN_COUNT)
+#endif /* ODBCVER */
{
if (pfDesc)
*pfDesc = cols;
}
field_type = QR_get_field_type(SC_get_Curres(stmt), col_idx);
+ if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[col_idx])
+ fi = stmt->fi[col_idx];
}
mylog("colAttr: col %d field_type = %d\n", col_idx, field_type);
value = pgtype_auto_increment(stmt, field_type);
if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
value = FALSE;
+inolog("AUTO_INCREMENT=%d\n", value);
break;
#if (ODBCVER >= 0x0300)
case SQL_DESC_NAME:
-#else
- case SQL_COLUMN_NAME:
#endif /* ODBCVER */
+ case SQL_COLUMN_NAME:
p = fi ? (fi->alias[0] ? fi->alias : fi->name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx);
mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
value = pgtype_money(stmt, field_type);
+inolog("COLUMN_MONEY=%d\n", value);
break;
#if (ODBCVER >= 0x0300)
case SQL_DESC_NULLABLE:
-#else
- case SQL_COLUMN_NULLABLE:
#endif /* ODBCVER */
+ case SQL_COLUMN_NULLABLE:
value = fi ? fi->nullable : pgtype_nullable(stmt, field_type);
+inolog("COLUMN_NULLABLE=%d\n", value);
break;
case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
case SQL_COLUMN_SCALE:
value = pgtype_scale(stmt, field_type, col_idx);
+inolog("COLUMN_SCALE=%d\n", value);
break;
case SQL_COLUMN_SEARCHABLE: /* SQL_DESC_SEARCHABLE */
case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
value = pgtype_to_sqltype(stmt, field_type);
+inolog("COLUMN_TYPE=%d\n", value);
break;
case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
* if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
* else
*/
- value = SQL_ATTR_WRITE;
+ value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN;
mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
break;
if (result == SQL_ERROR)
*(rgfRowStatus + i) = SQL_ROW_ERROR;
#ifdef DRIVER_CURSOR_IMPLEMENT
- /* this should be refined */
- else if (result > 10 && result < 20)
- *(rgfRowStatus + i) = result - 10;
+ else if (res->keyset)
+ {
+ UWORD pstatus = res->keyset[stmt->currTuple].status & KEYSET_INFO_PUBLIC;
+ if (pstatus != 0)
+ {
+ rgfRowStatus[i] = pstatus;
+ res->keyset[stmt->currTuple].status &= (~KEYSET_INFO_PUBLIC);
+ }
+ else
+ rgfRowStatus[i] = SQL_ROW_SUCCESS;
+ }
#endif /* DRIVER_CURSOR_IMPLEMENT */
else
*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
mylog("%s: entering...\n", func);
if (stmt && (res = SC_get_Curres(stmt)))
- SC_get_Curres(stmt) = res->next;
+ SC_set_Curres(stmt, res->next);
if (SC_get_Curres(stmt))
return SQL_SUCCESS;
return SQL_NO_DATA_FOUND;
/*
* Stuff for updatable cursors.
*/
+static const char *getOidValue(const QResultClass *res, int index)
+{
+ return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 1);
+}
+static UInt4 getOid(const QResultClass *res, int index)
+{
+ return res->keyset[index].oid;
+}
+static const char *getTidValue(const QResultClass *res, int index)
+{
+ return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 2);
+}
+static void getTid(const QResultClass *res, int index, UInt4 *blocknum, UInt2 *offset)
+{
+ *blocknum = res->keyset[index].blocknum;
+ *offset = res->keyset[index].offset;
+}
+static void KeySetSet(const QResultClass *res, int index)
+{
+ int num_fields = res->num_fields;
+ TupleField *tuple = res->backend_tuples + num_fields * index;
+ KeySet *keyset = res->keyset + index;
+
+ sscanf(tuple[num_fields - 2].value, "(%u,%hu)",
+ &keyset->blocknum, &keyset->offset);
+ sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid);
+}
+
static QResultClass *
positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
{
- int i;
QResultClass *qres;
- char selstr[4096];
+ char *selstr;
+ UInt4 len;
- sprintf(selstr, "select");
- for (i = 0; i < res_cols; i++)
- sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
- sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
+ len = strlen(stmt->load_statement);
if (tidval)
{
+ len += 100;
+ selstr = malloc(len);
if (latest)
- sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
- selstr, stmt->ti[0]->name, tidval);
- else
- sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
+ sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid);
+ else
+ sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid);
}
- sprintf(selstr, "%s oid = %u", selstr, oid),
- mylog("selstr=%s\n", selstr);
- qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, TRUE);
+ else
+ {
+ len += 20;
+ selstr = malloc(len);
+ sprintf(selstr, "%s where oid = %u", stmt->load_statement, oid);
+ }
+
+ mylog("selstr=%s\n", selstr);
+ qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, CLEAR_RESULT_ON_ABORT);
+free(selstr);
return qres;
}
{
int i,
res_cols;
- UWORD rcnt,
- global_ridx;
- UInt4 oid;
+ UWORD rcnt, global_ridx, offset;
+ UInt4 oid, blocknum;
QResultClass *res,
*qres;
RETCODE ret = SQL_ERROR;
- char *tidval,
- *oidval;
+ char tidval[32];
mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti);
rcnt = 0;
}
global_ridx = irow + stmt->rowset_start;
res_cols = QR_NumResultCols(res);
- if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ if (!(oid = getOid(res, global_ridx)))
+ return SQL_SUCCESS_WITH_INFO;
+ getTid(res, global_ridx, &blocknum, &offset);
+ sprintf(tidval, "(%u, %u)", blocknum, offset);
+ /*if (!(oidval = getOidValue(res, global_ridx)))
return SQL_SUCCESS_WITH_INFO;
sscanf(oidval, "%u", &oid);
- tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+ tidval = getTidValue(res, global_ridx);*/
res_cols -= 2;
if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
{
- TupleField *tupleo,
- *tuplen;
+ TupleField *tupleo, *tuplen;
rcnt = QR_get_num_tuples(qres);
tupleo = res->backend_tuples + res->num_fields * global_ridx;
tupleo[i].value = tuplen[i].value;
tuplen[i].value = NULL;
}
+ if (res->keyset)
+ {
+ if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
+ strcmp(tupleo[res->num_fields - 2].value, tidval))
+ res->keyset[global_ridx].status |= SQL_ROW_UPDATED;
+ KeySetSet(res, global_ridx);
+ }
ret = SQL_SUCCESS;
}
else
free(tupleo[res_cols + 1].value);
tupleo[res_cols + 1].value = NULL;
tupleo[res_cols + 1].len = 0;
+ res->keyset[global_ridx].status |= SQL_ROW_DELETED;
}
}
QR_Destructor(qres);
}
if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
{
- TupleField *tupleo,
- *tuplen;
+ TupleField *tupleo, *tuplen;
int count = QR_get_num_tuples(qres);
QR_set_position(qres, 0);
QR_Destructor(qres);
return SQL_ERROR;
}
+ if (res->haskeyset)
+ res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size);
res->count_allocated = tuple_size;
}
tupleo = res->backend_tuples + res->num_fields * res->fcount;
tupleo[i].value = tuplen[i].value;
tuplen[i].value = NULL;
}
+ KeySetSet(res, res->fcount);
res->fcount++;
ret = SQL_SUCCESS;
}
}
static RETCODE SQL_API
-irow_update(RETCODE ret, StatementClass *stmt, UWORD irow)
+irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow)
{
if (ret != SQL_ERROR)
{
int updcnt;
- const char *cmdstr = QR_get_command(SC_get_Curres(stmt));
+ const char *cmdstr = QR_get_command(SC_get_Curres(ustmt));
if (cmdstr &&
sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
BindInfoClass *bindings = stmt->bindings;
char updstr[4096];
RETCODE ret;
- char *tidval,
- *oidval;
- UInt4 offset;
+ UInt4 oid, offset, blocknum;
+ UInt2 pgoffset;
Int4 *used;
mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, SC_get_Curres(stmt)->base, stmt->fi, stmt->ti);
}
global_ridx = irow + stmt->rowset_start;
res_cols = QR_NumResultCols(res);
- if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ /*if (!(oidval = getOidValue(res, global_ridx)))*/
+ if (!(oid = getOid(res, global_ridx)))
{
stmt->errormsg = "The row is already deleted";
return SQL_ERROR;
}
- tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+ /*tidval = getTidValue(res, global_ridx);*/
+ getTid(res, global_ridx, &blocknum, &pgoffset);
sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
num_cols = stmt->nfld;
int res_cols = QR_NumResultCols(res);
StatementClass *qstmt;
- sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
- tidval, oidval);
+ /*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
+ tidval, oidval);*/
+ sprintf(updstr, "%s where ctid = '(%u, %u)' and oid = %u", updstr,
+ blocknum, pgoffset, oid);
mylog("updstr=%s\n", updstr);
if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
return SQL_ERROR;
stmt->errormsg = "SetPos with data_at_exec not yet supported";
ret = SQL_ERROR;
}
- ret = irow_update(ret, qstmt, irow);
+ ret = irow_update(ret, stmt, qstmt, irow);
PGAPI_FreeStmt(hstmt, SQL_DROP);
}
else
ret = SQL_SUCCESS_WITH_INFO;
+ if (SQL_SUCCESS == ret && res->keyset)
+ res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | DRV_SELF_UPDATED);
#if (ODBCVER >= 0x0300)
if (stmt->options.rowStatusArray)
{
case SQL_SUCCESS:
stmt->options.rowStatusArray[irow] = SQL_ROW_UPDATED;
break;
- case SQL_SUCCESS_WITH_INFO:
- stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
- break;
+ default:
+ stmt->options.rowStatusArray[irow] = ret;
}
}
#endif /* ODBCVER */
UWORD irow)
{
int res_cols;
- UWORD global_ridx;
- QResultClass *res,
- *qres;
+ UWORD global_ridx, offset;
+ QResultClass *res, *qres;
BindInfoClass *bindings = stmt->bindings;
char dltstr[4096];
RETCODE ret;
- char *oidval;
+ /*const char *oidval;*/
+ UInt4 oid, blocknum;
mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti);
if (!(res = SC_get_Curres(stmt)))
}
res_cols = QR_NumResultCols(res);
global_ridx = irow + stmt->rowset_start;
- if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+ /* if (!(oidval = getOidValue(res, global_ridx)))*/
+ if (!(oid = getOid(res, global_ridx)))
{
stmt->errormsg = "The row is already deleted";
return SQL_ERROR;
}
- sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",
- stmt->ti[0]->name,
- QR_get_value_backend_row(SC_get_Curres(stmt), global_ridx, res_cols - 2),
- oidval);
+ getTid(res, global_ridx, &blocknum, &offset);
+ /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/
+ sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u",
+ stmt->ti[0]->name, blocknum, offset, oid);
mylog("dltstr=%s\n", dltstr);
- qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, TRUE);
+ qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, CLEAR_RESULT_ON_ABORT);
+ ret = SQL_SUCCESS;
if (qres && QR_command_successful(qres))
{
int dltcnt;
}
if (qres)
QR_Destructor(qres);
+ if (SQL_SUCCESS == ret && res->keyset)
+ res->keyset[global_ridx].status |= (SQL_ROW_DELETED | DRV_SELF_DELETED);
#if (ODBCVER >= 0x0300)
if (stmt->options.rowStatusArray)
{
case SQL_SUCCESS:
stmt->options.rowStatusArray[irow] = SQL_ROW_DELETED;
break;
- case SQL_SUCCESS_WITH_INFO:
- stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
- break;
+ default:
+ stmt->options.rowStatusArray[irow] = ret;
}
}
#endif /* ODBCVER */
}
static RETCODE SQL_API
-irow_insert(RETCODE ret, StatementClass *stmt, int addpos)
+irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos)
{
if (ret != SQL_ERROR)
{
int addcnt;
UInt4 oid;
- const char *cmdstr = QR_get_command(SC_get_Curres(stmt));
+ const char *cmdstr = QR_get_command(SC_get_Curres(istmt));
if (cmdstr &&
sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
SC_pos_newload(stmt, oid, NULL);
if (stmt->bookmark.buffer)
{
- char buf[32];
+ char buf[32];
+ UInt4 offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0;
- sprintf(buf, "%ld", addpos);
+ sprintf(buf, "%ld", addpos + 1);
copy_and_convert_field(stmt, 0, buf,
- SQL_C_ULONG, stmt->bookmark.buffer,
- 0, stmt->bookmark.used);
+ SQL_C_ULONG, stmt->bookmark.buffer + offset,
+ 0, stmt->bookmark.used ? stmt->bookmark.used
+ + (offset >> 2) : NULL);
}
}
else
}
if (add_cols > 0)
{
+ int brow_save;
sprintf(addstr, "%s) values (", addstr);
for (i = 0; i < add_cols; i++)
stmt->errormsg = "SetPos with data_at_exec not yet supported";
ret = SQL_ERROR;
}
- ret = irow_insert(ret, qstmt, res->fcount);
+ brow_save = stmt->bind_row;
+ stmt->bind_row = irow;
+ ret = irow_insert(ret, stmt, qstmt, res->fcount);
+ stmt->bind_row = brow_save;
}
else
ret = SQL_SUCCESS_WITH_INFO;
PGAPI_FreeStmt(hstmt, SQL_DROP);
+ if (SQL_SUCCESS == ret && res->keyset)
+ res->keyset[res->fcount - 1].status |= DRV_SELF_ADDED;
#if (ODBCVER >= 0x0300)
if (stmt->options.rowStatusArray)
{
case SQL_SUCCESS:
stmt->options.rowStatusArray[irow] = SQL_ROW_ADDED;
break;
- case SQL_SUCCESS_WITH_INFO:
- stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
- break;
+ default:
+ stmt->options.rowStatusArray[irow] = ret;
}
}
#endif /* ODBCVER */
UWORD fLock)
{
static char *func = "PGAPI_SetPos";
+ RETCODE ret;
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_cols,
}
#ifdef DRIVER_CURSOR_IMPLEMENT
- mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple);
+ mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, fOption, irow, fLock, stmt->currTuple);
if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
;
else
}
num_cols = QR_NumResultCols(res);
- if (irow == 0)
+ if (irow == 0) /* bulk operation */
{
- stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
- stmt->errormsg = "Driver does not support Bulk operations.";
- SC_log_error(func, "", stmt);
- return SQL_ERROR;
+ int processed;
+
+ if (SQL_POSITION == fOption)
+ {
+ stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+ stmt->errormsg = "Bulk Fresh operations not allowed.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ret = SQL_SUCCESS;
+ for (i = 0, processed = 0; i < stmt->options.rowset_size; i++)
+ {
+#if (ODBCVER >= 0x0300)
+ if (!stmt->options.row_operation_ptr || stmt->options.row_operation_ptr[i] == SQL_ROW_PROCEED)
+ {
+#endif /* ODBCVER */
+ if (ret = PGAPI_SetPos(hstmt, (UWORD) (i + 1), fOption, fLock), SQL_ERROR == ret)
+ break;
+ processed++;
+#if (ODBCVER >= 0x0300)
+ }
+#endif /* ODBCVER */
+ }
+ if (processed > 0 && SQL_ERROR == ret)
+ {
+ processed++;
+ ret = SQL_SUCCESS_WITH_INFO;
+ stmt->errornumber = STMT_ERROR_IN_ROW;
+ }
+ if (stmt->options.rowsFetched)
+ *stmt->options.rowsFetched = processed;
+ return ret;
}
if (irow > stmt->last_fetch_count)
rv->statement = NULL;
rv->stmt_with_params = NULL;
+ rv->load_statement = NULL;
rv->stmt_size_limit = -1;
rv->statement_type = STMT_TYPE_UNKNOWN;
free(self->stmt_with_params);
self->stmt_with_params = NULL;
}
+ if (self->load_statement)
+ free(self->load_statement);
SC_free_params(self, STMT_FREE_PARAMS_ALL);
* SQLParamData/SQLPutData is called.
*/
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
+ if (self->stmt_with_params)
+ free(self->stmt_with_params);
+ self->stmt_with_params = NULL;
+ if (self->load_statement)
+ free(self->load_statement);
+ self->load_statement = NULL;
return TRUE;
}
QResultClass *res = SC_get_Curres(self);
ConnectionClass *conn = self->hdbc;
int pos;
+ BOOL detailmsg = FALSE;
static char msg[4096];
msg[0] = '\0';
if (res && res->message)
+ {
strcpy(msg, res->message);
+ detailmsg = TRUE;
+ }
else if (self->errormsg)
strcpy(msg, self->errormsg);
{
SocketClass *sock = conn->sock;
- if (conn->errormsg && conn->errormsg[0] != '\0')
+ if (!detailmsg && conn->errormsg && conn->errormsg[0] != '\0')
{
pos = strlen(msg);
- /*sprintf(&msg[pos], ";\n%s", conn->errormsg);*/
+ sprintf(&msg[pos], ";\n%s", conn->errormsg);
}
if (sock && sock->errormsg && sock->errormsg[0] != '\0')
int retval,
result;
-#ifdef DRIVER_CURSOR_IMPLEMENT
- int updret;
-#endif /* DRIVER_CURSOR_IMPLEMENT */
Int2 num_cols,
lf;
Oid type;
}
#ifdef DRIVER_CURSOR_IMPLEMENT
- updret = 0;
if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
{
- if (!QR_get_value_backend_row(res, self->currTuple, num_cols - 1))
- updret = SQL_ROW_DELETED;
num_cols -= 2;
}
#endif /* DRIVER_CURSOR_IMPLEMENT */
if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */
-#ifdef DRIVER_CURSOR_IMPLEMENT
- return updret ? updret + 10 : SQL_SUCCESS;
-#else
return SQL_SUCCESS;
-#endif /* DRIVER_CURSOR_IMPLEMENT */
for (lf = 0; lf < num_cols; lf++)
{
mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer);
}
}
-#ifdef DRIVER_CURSOR_IMPLEMENT
- if (updret)
- result = updret + 10;
-#endif /* DRIVER_CURSOR_IMPLEMENT */
return result;
}
if (self->statement_type == STMT_TYPE_SELECT)
{
char fetch[128];
+ UDWORD qflag = (SQL_CONCUR_ROWVER == self->options.scroll_concurrency ? CREATE_KEYSET : 0);
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
/* send the declare/select */
- res = CC_send_query(conn, self->stmt_with_params, NULL, FALSE);
+ res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);
if (SC_is_fetchcursor(self) && res != NULL &&
QR_command_successful(res))
{
*/
sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
- res = CC_send_query(conn, fetch, &qi, FALSE);
+ res = CC_send_query(conn, fetch, &qi, qflag);
}
mylog(" done sending the query:\n");
}
{
/* not a SELECT statement so don't use a cursor */
mylog(" it's NOT a select statement: stmt=%u\n", self);
- res = CC_send_query(conn, self->stmt_with_params, NULL, FALSE);
+ res = CC_send_query(conn, self->stmt_with_params, NULL, 0);
/*
* We shouldn't send COMMIT. Postgres backend does the autocommit
#define STMT_BAD_ERROR 27
#define STMT_INVALID_OPTION_IDENTIFIER 28
#define STMT_RETURN_NULL_WITHOUT_INDICATOR 29
+#define STMT_ERROR_IN_ROW 30
/* statement types */
enum
char quote;
char dquote;
char numeric;
+ char updatable;
char dot[MAX_TABLE_LEN + 1];
char name[MAX_COLUMN_LEN + 1];
char alias[MAX_COLUMN_LEN + 1];
char miscinfo;
SWORD errorpos;
SWORD error_recsize;
+ char *load_statement; /* to (re)load updatable individual rows */
+ Int4 from_pos;
+ Int4 where_pos;
};
#define SC_get_conn(a) (a->hdbc)
#define SC_set_Result(a, b) (a->result = a->curres = b)
#define SC_get_Result(a) (a->result)
+#define SC_set_Curres(a, b) (a->curres = b)
#define SC_get_Curres(a) (a->curres)
/* options for SC_free_params() */
TupleField tuple[1];
};
+/* keyset(TID + OID) info */
+struct KeySet_
+{
+ UWORD status;
+ UWORD offset;
+ UDWORD blocknum;
+ UDWORD oid;
+};
+#define KEYSET_INFO_PUBLIC 0x0f
+#define DRV_SELF_ADDED (1L << 4)
+#define DRV_SELF_DELETED (1L << 5)
+#define DRV_SELF_UPDATED (1L << 6)
+
/* These macros are wrappers for the corresponding set_tuplefield functions
but these handle automatic NULL determination and call set_tuplefield_null()
if appropriate for the datatype (used by SQLGetTypeInfo).