From: Hiroshi Inoue Date: Mon, 18 Feb 2002 03:16:11 +0000 (+0000) Subject: The version is now 7.01.0010. X-Git-Tag: REL7_3~2126 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c16d095b5b395560a567db8ae8cfa06899150f9;p=postgresql The version is now 7.01.0010. 1) Handle parameter array. 2) Allow re-use of the connection handle after SQLDisconnect. 3) Reject NULL if no indicator specified. 4) Improve the handling of '_' in table name. 5) Unify internal begin/commit/abort operations. 6) Change SQLTables() to return null not "" for the table_owner. 7) Fix a bug about parameter handling reported by Benoit Menendez. 8) Add cast in handling ODBC date/time escape sequences. 9) Fix a bug about cache_size handing in declare/fetch mode. [ODBC3.0 related] 10) Improve the handling of descriptor handles(ODBC3.0). 11) Improve the type handling of some types for ODBC3.0. [Thanks to Marcelo Aceto for his useful patches] 12) Allow nested ODBC escape. 13) Allow changing autocommit on/off inside the transaction block. 14) Improve the handling of ODBC scalar functions. --- diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index e9f5cc3482..f3fb353735 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -134,6 +134,8 @@ PGAPI_BindParameter( stmt->parameters[ipar].EXEC_buffer = NULL; } + if (pcbValue && stmt->options.param_offset_ptr) + pcbValue += (*stmt->options.param_offset_ptr >> 2); /* Data at exec macro only valid for C char/binary data */ if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC || *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)) diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index e057d7b73f..66ef5e9ae4 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -309,12 +309,6 @@ CC_Destructor(ConnectionClass *self) mylog("after CC_Cleanup\n"); -#ifdef MULTIBYTE - if (self->client_encoding) - free(self->client_encoding); - if (self->server_encoding) - free(self->server_encoding); -#endif /* MULTIBYTE */ /* Free up statement holders */ if (self->stmts) { @@ -323,23 +317,6 @@ CC_Destructor(ConnectionClass *self) } mylog("after free statement holders\n"); - /* Free cached table info */ - if (self->col_info) - { - int i; - - for (i = 0; i < self->ntables; i++) - { - if (self->col_info[i]->result) /* Free the SQLColumns - * result structure */ - QR_Destructor(self->col_info[i]->result); - - free(self->col_info[i]); - } - free(self->col_info); - } - - free(self); mylog("exit CC_Destructor\n"); @@ -380,6 +357,59 @@ CC_clear_error(ConnectionClass *self) } +/* + * Used to begin a transaction. + */ +char +CC_begin(ConnectionClass *self) +{ + char ret = TRUE; + if (!CC_is_in_trans(self)) + { + QResultClass *res = CC_send_query(self, "BEGIN", NULL); + mylog("CC_begin: sending BEGIN!\n"); + + if (res != NULL) + { + ret = (!QR_aborted(res) && QR_command_successful(res)); + QR_Destructor(res); + if (ret) + CC_set_in_trans(self); + } + else + ret = FALSE; + } + + return ret; +} + +/* + * Used to commit a transaction. + * We are almost always in the middle of a transaction. + */ +char +CC_commit(ConnectionClass *self) +{ + char ret = FALSE; + if (CC_is_in_trans(self)) + { + QResultClass *res = CC_send_query(self, "COMMIT", NULL); + mylog("CC_commit: sending COMMIT!\n"); + + CC_set_no_trans(self); + + if (res != NULL) + { + ret = QR_command_successful(res); + QR_Destructor(res); + } + else + ret = FALSE; + } + + return ret; +} + /* * Used to cancel a transaction. * We are almost always in the middle of a transaction. @@ -387,22 +417,17 @@ CC_clear_error(ConnectionClass *self) char CC_abort(ConnectionClass *self) { - QResultClass *res; - if (CC_is_in_trans(self)) { - res = NULL; - + QResultClass *res = CC_send_query(self, "ROLLBACK", NULL); mylog("CC_abort: sending ABORT!\n"); - res = CC_send_query(self, "ABORT", NULL); CC_set_no_trans(self); if (res != NULL) QR_Destructor(res); else return FALSE; - } return TRUE; @@ -461,6 +486,37 @@ CC_cleanup(ConnectionClass *self) } #endif + self->status = CONN_NOT_CONNECTED; + self->transact_status = CONN_IN_AUTOCOMMIT; + memset(&self->connInfo, 0, sizeof(ConnInfo)); +#ifdef DRIVER_CURSOR_IMPLEMENT + self->connInfo.updatable_cursors = 1; +#endif /* DRIVER_CURSOR_IMPLEMENT */ + memcpy(&(self->connInfo.drivers), &globals, sizeof(globals)); +#ifdef MULTIBYTE + if (self->client_encoding) + free(self->client_encoding); + self->client_encoding = NULL; + if (self->server_encoding) + free(self->server_encoding); + self->server_encoding = NULL; +#endif /* MULTIBYTE */ + /* Free cached table info */ + if (self->col_info) + { + int i; + + for (i = 0; i < self->ntables; i++) + { + if (self->col_info[i]->result) /* Free the SQLColumns result structure */ + QR_Destructor(self->col_info[i]->result); + + free(self->col_info[i]); + } + free(self->col_info); + self->col_info = NULL; + } + self->ntables = 0; mylog("exit CC_Cleanup\n"); return TRUE; } @@ -516,7 +572,6 @@ md5_auth_send(ConnectionClass *self, const char *salt) ConnInfo *ci = &(self->connInfo); SocketClass *sock = self->sock; -mylog("MD5 user=%s password=%s\n", ci->username, ci->password); if (!(pwd1 = malloc(MD5_PASSWD_LEN + 1))) return 1; if (!EncryptMD5(ci->password, ci->username, strlen(ci->username), pwd1)) @@ -601,9 +656,10 @@ CC_connect(ConnectionClass *self, char do_password) ci->drivers.conn_settings, encoding ? encoding : ""); #else - qlog(" extra_systable_prefixes='%s', conn_settings='%s'\n", + qlog(" extra_systable_prefixes='%s', conn_settings='%s', protocol='%s'\n", ci->drivers.extra_systable_prefixes, - ci->drivers.conn_settings); + ci->drivers.conn_settings, + ci->protocol); #endif if (self->status != CONN_NOT_CONNECTED) @@ -1037,7 +1093,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) ReadyToReturn, tuples_return = FALSE, query_completed = FALSE, - before_64 = PG_VERSION_LT(self, 6.4); + before_64 = PG_VERSION_LT(self, 6.4), + used_passed_result_object = FALSE; /* ERROR_MSG_LENGTH is suffcient */ static char msgbuffer[ERROR_MSG_LENGTH + 1]; @@ -1289,6 +1346,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) else { /* next fetch, so reuse an existing result */ + used_passed_result_object = TRUE; /* * called from QR_next_tuple and must return * immediately. @@ -1373,9 +1431,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) QR_Destructor(res); if (result_in && retres != result_in) { - if (qi && qi->result_in) - ; - else + if (!used_passed_result_object) QR_Destructor(result_in); } return retres; diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index a80c31b921..0ef91a139f 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -292,6 +292,8 @@ ConnectionClass *CC_Constructor(void); char CC_Destructor(ConnectionClass *self); int CC_cursor_count(ConnectionClass *self); char CC_cleanup(ConnectionClass *self); +char CC_begin(ConnectionClass *self); +char CC_commit(ConnectionClass *self); char CC_abort(ConnectionClass *self); int CC_set_translation(ConnectionClass *self); char CC_connect(ConnectionClass *self, char do_password); diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 0b609a07db..1f3c89981e 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -51,77 +51,80 @@ * - thomas 2000-04-03 */ char *mapFuncs[][2] = { -/* { "ASCII", "ascii" }, */ - {"CHAR", "chr"}, - {"CONCAT", "textcat"}, -/* { "DIFFERENCE", "difference" }, */ -/* { "INSERT", "insert" }, */ - {"LCASE", "lower"}, - {"LEFT", "ltrunc"}, - {"LOCATE", "strpos"}, - {"LENGTH", "char_length"}, -/* { "LTRIM", "ltrim" }, */ - {"RIGHT", "rtrunc"}, -/* { "REPEAT", "repeat" }, */ -/* { "REPLACE", "replace" }, */ -/* { "RTRIM", "rtrim" }, */ -/* { "SOUNDEX", "soundex" }, */ - {"SUBSTRING", "substr"}, - {"UCASE", "upper"}, - -/* { "ABS", "abs" }, */ -/* { "ACOS", "acos" }, */ -/* { "ASIN", "asin" }, */ -/* { "ATAN", "atan" }, */ -/* { "ATAN2", "atan2" }, */ - {"CEILING", "ceil"}, -/* { "COS", "cos" }, */ -/* { "COT", "cot" }, */ -/* { "DEGREES", "degrees" }, */ -/* { "EXP", "exp" }, */ -/* { "FLOOR", "floor" }, */ - {"LOG", "ln"}, - {"LOG10", "log"}, -/* { "MOD", "mod" }, */ -/* { "PI", "pi" }, */ - {"POWER", "pow"}, -/* { "RADIANS", "radians" }, */ - {"RAND", "random"}, -/* { "ROUND", "round" }, */ -/* { "SIGN", "sign" }, */ -/* { "SIN", "sin" }, */ -/* { "SQRT", "sqrt" }, */ -/* { "TAN", "tan" }, */ - {"TRUNCATE", "trunc"}, - - {"CURRENT_DATE", "curdate"}, - {"CURRENT_TIME", "curtime"}, - {"CURRENT_TIMESTAMP", "odbc_timestamp"}, - {"CURRENT_USER", "odbc_current_user"}, - {"SESSION_USER", "odbc_session_user"}, -/* { "CURDATE", "curdate" }, */ -/* { "CURTIME", "curtime" }, */ -/* { "DAYNAME", "dayname" }, */ -/* { "DAYOFMONTH", "dayofmonth" }, */ -/* { "DAYOFWEEK", "dayofweek" }, */ -/* { "DAYOFYEAR", "dayofyear" }, */ -/* { "HOUR", "hour" }, */ -/* { "MINUTE", "minute" }, */ -/* { "MONTH", "month" }, */ -/* { "MONTHNAME", "monthname" }, */ -/* { "NOW", "now" }, */ -/* { "QUARTER", "quarter" }, */ -/* { "SECOND", "second" }, */ -/* { "WEEK", "week" }, */ -/* { "YEAR", "year" }, */ +/* { "ASCII", "ascii" }, built_in */ + {"CHAR", "chr($*)" }, + {"CONCAT", "textcat($*)" }, +/* { "DIFFERENCE", "difference" }, how to ? */ + {"INSERT", "substring($1 from 1 for $2 - 1) || $4 || substring($1 from $2 + $3)" }, + {"LCASE", "lower($*)" }, + {"LEFT", "ltrunc($*)" }, + {"%2LOCATE", "strpos($2, $1)" }, /* 2 parameters */ + {"%3LOCATE", "strpos(substring($2 from $3), $1) + $3 - 1" }, /* 3 parameters */ + {"LENGTH", "char_length($*)"}, +/* { "LTRIM", "ltrim" }, built_in */ + {"RIGHT", "rtrunc($*)" }, + {"SPACE", "repeat('' '', $1)" }, +/* { "REPEAT", "repeat" }, built_in */ +/* { "REPLACE", "replace" }, ??? */ +/* { "RTRIM", "rtrim" }, built_in */ +/* { "SOUNDEX", "soundex" }, how to ? */ + {"SUBSTRING", "substr($*)" }, + {"UCASE", "upper($*)" }, + +/* { "ABS", "abs" }, built_in */ +/* { "ACOS", "acos" }, built_in */ +/* { "ASIN", "asin" }, built_in */ +/* { "ATAN", "atan" }, built_in */ +/* { "ATAN2", "atan2" }, bui;t_in */ + {"CEILING", "ceil($*)" }, +/* { "COS", "cos" }, built_in */ +/* { "COT", "cot" }, built_in */ +/* { "DEGREES", "degrees" }, built_in */ +/* { "EXP", "exp" }, built_in */ +/* { "FLOOR", "floor" }, built_in */ + {"LOG", "ln($*)" }, + {"LOG10", "log($*)" }, +/* { "MOD", "mod" }, built_in */ +/* { "PI", "pi" }, built_in */ + {"POWER", "pow($*)" }, +/* { "RADIANS", "radians" }, built_in */ + {"%0RAND", "random()" }, /* 0 parameters */ + {"%1RAND", "(setseed($1) * .0 + random())" }, /* 1 parameters */ +/* { "ROUND", "round" }, built_in */ +/* { "SIGN", "sign" }, built_in */ +/* { "SIN", "sin" }, built_in */ +/* { "SQRT", "sqrt" }, built_in */ +/* { "TAN", "tan" }, built_in */ + {"TRUNCATE", "trunc($*)" }, + + {"CURRENT_DATE", "current_date" }, + {"CURRENT_TIME", "current_time" }, + {"CURRENT_TIMESTAMP", "current_timestamp" }, + {"CURRENT_USER", "cast(current_user as text)" }, + {"SESSION_USER", "cast(session_user as text)" }, + {"CURDATE", "current_date" }, + {"CURTIME", "current_time" }, + {"DAYNAME", "to_char($1, 'Day')" }, + {"DAYOFMONTH", "cast(extract(day from $1) as integer)" }, + {"DAYOFWEEK", "(cast(extract(dow from $1) as integer) + 1)" }, + {"DAYOFYEAR", "cast(extract(doy from $1) as integer)" }, + {"HOUR", "cast(extract(hour from $1) as integer)" }, + {"MINUTE", "cast(extract(minute from $1) as integer)" }, + {"MONTH", "cast(extract(month from $1) as integer)" }, + {"MONTHNAME", " to_char($1, 'Month')" }, +/* { "NOW", "now" }, built_in */ + {"QUARTER", "cast(extract(quarter from $1) as integer)" }, + {"SECOND", "cast(extract(second from $1) as integer)" }, + {"WEEK", "cast(extract(week from $1) as integer)" }, + {"YEAR", "cast(extract(year from $1) as integer)" }, /* { "DATABASE", "database" }, */ - {"IFNULL", "coalesce"}, - {"USER", "odbc_user"}, + {"IFNULL", "coalesce($*)" }, + {"USER", "cast(current_user as text)" }, {0, 0} }; -static char *mapFunction(const char *func); +static const char *mapFunction(const char *func, int param_count); static unsigned int conv_from_octal(const unsigned char *s); static unsigned int conv_from_hex(const unsigned char *s); static char *conv_to_octal(unsigned char val); @@ -307,9 +310,10 @@ int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col) { BindInfoClass *bic = &(stmt->bindings[col]); + UInt4 offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0; - return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) bic->buffer, - (SDWORD) bic->buflen, (SDWORD *) bic->used); + return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) (bic->buffer + offset), + (SDWORD) bic->buflen, (SDWORD *) (bic->used + (offset >> 2))); } @@ -318,6 +322,7 @@ int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue) { + static char *func = "copy_and_convert_field"; Int4 len = 0, copy_len = 0; SIMPLE_TIME st; @@ -387,8 +392,17 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 * doing nothing to the buffer. */ if (pcbValue) + { *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA; - return COPY_OK; + return COPY_OK; + } + else + { + stmt->errornumber = STMT_RETURN_NULL_WITHOUT_INDICATOR; + stmt->errormsg = "StrLen_or_IndPtr was a null pointer and NULL data was retrieved"; + SC_log_error(func, "", stmt); + return SQL_ERROR; + } } if (stmt->hdbc->DataSourceToDriver != NULL) @@ -863,6 +877,26 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 *((UDWORD *) rgbValue + bind_row) = atol(neut_str); break; +#if (ODBCVER >= 0x0300) && defined(ODBCINT64) +#ifdef WIN32 + case SQL_C_SBIGINT: + len = 8; + if (bind_size > 0) + *(SQLBIGINT *) ((char *) rgbValue + (bind_row * bind_size)) = _atoi64(neut_str); + else + *((SQLBIGINT *) rgbValue + bind_row) = _atoi64(neut_str); + break; + + case SQL_C_UBIGINT: + len = 8; + if (bind_size > 0) + *(SQLUBIGINT *) ((char *) rgbValue + (bind_row * bind_size)) = _atoi64(neut_str); + else + *((SQLUBIGINT *) rgbValue + bind_row) = _atoi64(neut_str); + break; + +#endif /* WIN32 */ +#endif /* ODBCINT64 */ case SQL_C_BINARY: /* truncate if necessary */ @@ -1146,6 +1180,11 @@ table_for_update(const char *stmt, int *endpos) return !wstmt[0] || isspace((unsigned char) wstmt[0]); } +#ifdef MULTIBYTE +#define my_strchr(s1,c1) multibyte_strchr(s1,c1) +#else +#define my_strchr(s1,c1) strchr(s1,c1) +#endif /* * This function inserts parameters into an SQL statements. * It will also modify a SELECT statement for use with declare/fetch cursors. @@ -1193,10 +1232,13 @@ copy_statement_with_parameters(StatementClass *stmt) char token_save[64]; int token_len; BOOL prev_token_end; + UInt4 offset = stmt->options.param_offset_ptr ? *stmt->options.param_offset_ptr : 0; + UInt4 current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; #ifdef DRIVER_CURSOR_IMPLEMENT BOOL search_from_pos = FALSE; #endif /* DRIVER_CURSOR_IMPLEMENT */ + if (ci->disallow_premature) prepare_dummy_cursor = stmt->pre_executing; @@ -1320,25 +1362,15 @@ copy_statement_with_parameters(StatementClass *stmt) */ else if (oldchar == '{') { - char *esc; - char *begin = &old_statement[opos + 1]; + char *begin = &old_statement[opos], *end; -#ifdef MULTIBYTE - char *end = multibyte_strchr(begin, '}'); - -#else - char *end = strchr(begin, '}'); -#endif - - if (!end) - continue; /* procedure calls */ if (stmt->statement_type == STMT_TYPE_PROCCALL) { - int lit_call_len = 4; + int lit_call_len = 4; while (isspace((unsigned char) old_statement[++opos])); - /* '=?' to accept return values exists ? */ + /* '?=' to accept return values exists ? */ if (old_statement[opos] == '?') { param_number++; @@ -1358,28 +1390,18 @@ copy_statement_with_parameters(StatementClass *stmt) } opos += lit_call_len; CVT_APPEND_STR("SELECT "); -#ifdef MULTIBYTE - if (multibyte_strchr(&old_statement[opos], '(')) -#else - if (strchr(&old_statement[opos], '(')) -#endif /* MULTIBYTE */ + if (my_strchr(&old_statement[opos], '(')) proc_no_param = FALSE; continue; } - *end = '\0'; - - esc = convert_escape(begin); - if (esc) - CVT_APPEND_STR(esc); - else - { /* it's not a valid literal so just copy */ - *end = '}'; - CVT_APPEND_CHAR(oldchar); - continue; + if (convert_escape(begin, stmt, &npos, &new_stsize, &end) != CONVERT_ESCAPE_OK) + { + stmt->errormsg = "ODBC escape convert error"; + stmt->errornumber = STMT_EXEC_ERROR; + return SQL_ERROR; } - - opos += end - begin + 1; - *end = '}'; + opos = end - old_statement; /* positioned at the last } */ + new_statement = stmt->stmt_with_params; continue; } /* End of a procedure call */ @@ -1500,11 +1522,30 @@ copy_statement_with_parameters(StatementClass *stmt) } else { + UInt4 bind_size = stmt->options.param_bind_type; + UInt4 ctypelen; - - used = stmt->parameters[param_number].used ? *stmt->parameters[param_number].used : SQL_NTS; - - buffer = stmt->parameters[param_number].buffer; + buffer = stmt->parameters[param_number].buffer + offset; + if (current_row > 0) + { + if (bind_size > 0) + buffer += (bind_size * current_row); + else if (ctypelen = ctype_length(stmt->parameters[param_number].CType), ctypelen > 0) + buffer += current_row * ctypelen; + else + buffer += current_row * stmt->parameters[param_number].buflen; + } + if (stmt->parameters[param_number].used) + { + UInt4 p_offset = offset; + if (bind_size > 0) + p_offset = offset + bind_size * current_row; + else + p_offset = offset + sizeof(SDWORD) * current_row; + used = *(SDWORD *)((char *)stmt->parameters[param_number].used + p_offset); + } + else + used = SQL_NTS; } /* Handle NULL parameter data */ @@ -1570,6 +1611,20 @@ copy_statement_with_parameters(StatementClass *stmt) *((SDWORD *) buffer)); break; +#if (ODBCVER >= 0x0300) && defined(ODBCINT64) +#ifdef WIN32 + case SQL_C_SBIGINT: + sprintf(param_string, "%I64d", + *((SQLBIGINT *) buffer)); + break; + + case SQL_C_UBIGINT: + sprintf(param_string, "%I64u", + *((SQLUBIGINT *) buffer)); + break; + +#endif /* WIN32 */ +#endif /* ODBCINT64 */ case SQL_C_SSHORT: case SQL_C_SHORT: sprintf(param_string, "%d", @@ -1706,7 +1761,7 @@ copy_statement_with_parameters(StatementClass *stmt) parse_datetime(cbuf, &st); } - sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d); + sprintf(tmp, "'%.4d-%.2d-%.2d'::date", st.y, st.m, st.d); CVT_APPEND_STR(tmp); break; @@ -1721,7 +1776,7 @@ copy_statement_with_parameters(StatementClass *stmt) parse_datetime(cbuf, &st); } - sprintf(tmp, "'%.2d:%.2d:%.2d'", st.hh, st.mm, st.ss); + sprintf(tmp, "'%.2d:%.2d:%.2d'::time", st.hh, st.mm, st.ss); CVT_APPEND_STR(tmp); break; @@ -1743,7 +1798,7 @@ copy_statement_with_parameters(StatementClass *stmt) */ tmp[0] = '\''; /* Time zone stuff is unreliable */ - stime2timestamp(&st, tmp + 1, FALSE, PG_VERSION_GE(conn, 7.2)); + stime2timestamp(&st, tmp + 1, USE_ZONE, PG_VERSION_GE(conn, 7.2)); strcat(tmp, "'"); CVT_APPEND_STR(tmp); @@ -1772,28 +1827,13 @@ copy_statement_with_parameters(StatementClass *stmt) /* begin transaction if needed */ if (!CC_is_in_trans(conn)) { - QResultClass *res; - char ok; - - res = CC_send_query(conn, "BEGIN", NULL); - if (!res) + if (!CC_begin(conn)) { stmt->errormsg = "Could not begin (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) - { - stmt->errormsg = "Could not begin (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - - CC_set_in_trans(conn); } /* store the oid */ @@ -1823,28 +1863,13 @@ copy_statement_with_parameters(StatementClass *stmt) /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) { - QResultClass *res; - char ok; - - res = CC_send_query(conn, "COMMIT", NULL); - if (!res) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) + if (!CC_commit(conn)) { stmt->errormsg = "Could not commit (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } - - CC_set_no_trans(conn); } } @@ -1951,49 +1976,95 @@ copy_statement_with_parameters(StatementClass *stmt) } -static char * -mapFunction(const char *func) +static const char * +mapFunction(const char *func, int param_count) { int i; for (i = 0; mapFuncs[i][0]; i++) - if (!stricmp(mapFuncs[i][0], func)) + { + if (mapFuncs[i][0][0] == '%') + { + if (mapFuncs[i][0][1] - '0' == param_count && + !stricmp(mapFuncs[i][0] + 2, func)) + return mapFuncs[i][1]; + } + else if (!stricmp(mapFuncs[i][0], func)) return mapFuncs[i][1]; + } return NULL; } +static int inner_convert_escape(const ConnectionClass *conn, const char *value, char *result, UInt4 maxLen, const char **input_resume, UInt4 *count); +static int processParameters(const ConnectionClass *conn, const char *value, char *result, UInt4 maxLen, UInt4 *input_consumed, UInt4 *count, Int4 param_pos[][2]); + /* - * convert_escape() - * - * This function returns a pointer to static memory! + * inner_convert_escape() + * work with embedded escapes sequences */ -char * -convert_escape(char *value) + +static +int inner_convert_escape(const ConnectionClass *conn, const char *value, + char *result, UInt4 maxLen, const char **input_resume, + UInt4 *count) { - static char escape[1024]; - char key[33]; - + static const char *func = "inner_convert_escape"; + int subret, param_count; + char valnts[1024], params[1024]; + char key[33], *end; + const char *valptr; + UInt4 vlen, prtlen, input_consumed, param_consumed; + Int4 param_pos[16][2]; + + valptr = value; + if (*valptr == '{') /* skip the first { */ + valptr++; /* Separate off the key, skipping leading and trailing whitespace */ - while ((*value != '\0') && isspace((unsigned char) *value)) - value++; - sscanf(value, "%32s", key); - while ((*value != '\0') && (!isspace((unsigned char) *value))) - value++; - while ((*value != '\0') && isspace((unsigned char) *value)) - value++; - - mylog("convert_escape: key='%s', val='%s'\n", key, value); - - if ((strcmp(key, "d") == 0) || - (strcmp(key, "t") == 0) || - (strcmp(key, "oj") == 0) || /* {oj syntax support for 7.1 - * servers */ - (strcmp(key, "ts") == 0)) + while ((*valptr != '\0') && isspace((unsigned char) *valptr)) + valptr++; + sscanf(valptr, "%32s", key); + while ((*valptr != '\0') && (!isspace((unsigned char) *valptr))) + valptr++; + while ((*valptr != '\0') && isspace((unsigned char) *valptr)) + valptr++; + + if (end = my_strchr(valptr, '}'), NULL == end) + { + mylog("%s couldn't find the ending }\n",func); + return CONVERT_ESCAPE_ERROR; + } + if (vlen = (UInt4)(end - valptr), maxLen <= vlen) + return CONVERT_ESCAPE_OVERFLOW; + memcpy(valnts, valptr, vlen); + valnts[vlen] = '\0'; + *input_resume = valptr + vlen; /* resume from the last } */ + mylog("%s: key='%s', val='%s'\n", func, key, valnts); + + if (strcmp(key, "d") == 0) + { + /* Literal; return the escape part adding type cast */ + prtlen = snprintf(result, maxLen, "%s::date", valnts); + } + else if (strcmp(key, "t") == 0) + { + /* Literal; return the escape part adding type cast */ + prtlen = snprintf(result, maxLen, "%s::time", valnts); + } + else if (strcmp(key, "ts") == 0) + { + /* Literal; return the escape part adding type cast */ + if (PG_VERSION_LT(conn, 7.1)) + prtlen = snprintf(result, maxLen, "%s::datetime", valnts); + else + prtlen = snprintf(result, maxLen, "%s::timestamp", valnts); + } + else if (strcmp(key, "oj") == 0) /* {oj syntax support for 7.1 * servers */ { /* Literal; return the escape part as-is */ - strncpy(escape, value, sizeof(escape) - 1); + strncpy(result, valnts, maxLen); + prtlen = vlen; } else if (strcmp(key, "fn") == 0) { @@ -2001,52 +2072,286 @@ convert_escape(char *value) * Function invocation Separate off the func name, skipping * trailing whitespace. */ - char *funcEnd = value; - char svchar; - char *mapFunc; + char *funcEnd = valnts; + char svchar; + const char *mapExpr; + + params[sizeof(params)-1] = '\0'; while ((*funcEnd != '\0') && (*funcEnd != '(') && - (!isspace((unsigned char) *funcEnd))) + (!isspace((unsigned char) *funcEnd))) funcEnd++; svchar = *funcEnd; *funcEnd = '\0'; - sscanf(value, "%32s", key); + sscanf(valnts, "%32s", key); *funcEnd = svchar; while ((*funcEnd != '\0') && isspace((unsigned char) *funcEnd)) funcEnd++; /* - * We expect left parenthesis here, else return fn body as-is + * We expect left parenthesis here, else return fn body as-is * since it is one of those "function constants". */ if (*funcEnd != '(') { - strncpy(escape, value, sizeof(escape) - 1); - return escape; + strncpy(result, valnts, maxLen); + return CONVERT_ESCAPE_OK; } - mapFunc = mapFunction(key); /* - * We could have mapFunction() return key if not in table... - - * thomas 2000-04-03 + * Process parameter list and inner escape + * sequences + * Aceto 2002-01-29 */ - if (mapFunc == NULL) + + valptr += (UInt4)(funcEnd - valnts); + if (subret = processParameters(conn, valptr, params, sizeof(params) - 1, &input_consumed, ¶m_consumed, param_pos), CONVERT_ESCAPE_OK != subret) + return CONVERT_ESCAPE_ERROR; + + for (param_count = 0;; param_count++) { - /* If unrecognized function name, return fn body as-is */ - strncpy(escape, value, sizeof(escape) - 1); - return escape; + if (param_pos[param_count][0] < 0) + break; } - /* copy mapped name and remaining input string */ - strcpy(escape, mapFunc); - strncat(escape, funcEnd, sizeof(escape) - 1 - strlen(mapFunc)); + if (param_count == 1 && + param_pos[0][1] < param_pos[0][0]) + param_count = 0; + + mapExpr = mapFunction(key, param_count); + if (mapExpr == NULL) + prtlen = snprintf(result, maxLen, "%s%s", key, params); + else + { + const char *mapptr; + int from, to, pidx, paramlen; + + for (prtlen = 0, mapptr = mapExpr; *mapptr; mapptr++) + { + if (prtlen + 1 >= maxLen) /* buffer overflow */ + { + result[prtlen] = '\0'; + prtlen++; + break; + } + if (*mapptr != '$') + { + result[prtlen++] = *mapptr; + continue; + } + mapptr++; + if (*mapptr == '*') + { + from = 1; + to = param_consumed - 2; + } + else if (isdigit(*mapptr)) + { + pidx = *mapptr - '0' - 1; + if (pidx < 0 || + param_pos[pidx][0] <0) + { + qlog("%s %dth param not found for the expression %s\n", pidx + 1, mapExpr); + return CONVERT_ESCAPE_ERROR; + } + from = param_pos[pidx][0]; + to = param_pos[pidx][1]; + } + else + { + qlog("%s internal expression error %s\n", func, mapExpr); + return CONVERT_ESCAPE_ERROR; + } + paramlen = to - from + 1; + if (prtlen + paramlen >= maxLen) /* buffer overflow */ + { + prtlen = maxLen; + break; + } + if (paramlen > 0) + memcpy(&result[prtlen], params + from, paramlen); + prtlen += paramlen; + } + if (prtlen < maxLen) + result[prtlen] = '\0'; + /** prtlen = snprintf(result, maxLen, "%s%s", mapExpr, params); **/ + } + valptr += input_consumed; + *input_resume = valptr; } else { /* Bogus key, leave untranslated */ - return NULL; + return CONVERT_ESCAPE_ERROR; + } + + if (count) + *count = prtlen; + if (prtlen < 0 || prtlen >= maxLen) /* buffer overflow */ + { + mylog("%s %d bytes buffer overflow\n", func, maxLen); + return CONVERT_ESCAPE_OVERFLOW; + } + return CONVERT_ESCAPE_OK; +} + +/* + * processParameters() + * Process function parameters and work with embedded escapes sequences. + */ + +static +int processParameters(const ConnectionClass *conn, const char *value, + char *result, UInt4 maxLen, UInt4 *input_consumed, + UInt4 *output_count, Int4 param_pos[][2]) +{ + int innerParenthesis, subret, param_count; + UInt4 ipos, count, inner_count; + unsigned char stop; + const char *valptr; + char buf[1024]; + BOOL in_quote, in_dquote, in_escape, leadingSpace; + + buf[sizeof(buf)-1] = '\0'; + innerParenthesis = 0; + in_quote = in_dquote = in_escape = leadingSpace = FALSE; + param_count = 0; +#ifdef MULTIBYTE + multibyte_init(); +#endif /* MULTIBYTE */ + /* begin with outer '(' */ + for (stop = FALSE, valptr = value, ipos = count = 0; *valptr != '\0'; ipos++, valptr++) + { + if (leadingSpace) + { + if (isspace(*valptr)) + continue; + leadingSpace = FALSE; + } + if (count + 1 >= maxLen) /* buffer overflow */ + { + *input_consumed = 0; + result[count++] = '\0'; + return CONVERT_ESCAPE_OVERFLOW; + } +#ifdef MULTIBYTE + if (multibyte_char_check(*valptr) != 0) + { + result[count++] = *valptr; + continue; + } + /* + * From here we are guaranteed to handle a 1-byte character. + */ +#endif + if (in_quote) + { + if (in_escape) + in_escape = FALSE; + else if (*valptr == '\\') + in_escape = TRUE; + else if (*valptr == '\'') + in_quote = FALSE; + result[count++] = *valptr; + continue; + } + else if (in_dquote) + { + if (*valptr == '\"') + in_dquote = FALSE; + result[count++] = *valptr; + continue; + } + switch (*valptr) + { + case '\'': + in_quote = TRUE; + break; + case '\"': + in_dquote = TRUE; + break; + case ',': + if (1 == innerParenthesis) + { + param_pos[param_count][1] = count - 1; + param_count++; + param_pos[param_count][0] = count + 1; + param_pos[param_count][1] = -1; + leadingSpace = TRUE; + } + break; + case '(': + if (0 == innerParenthesis) + { + param_pos[param_count][0] = count + 1; + param_pos[param_count][1] = -1; + leadingSpace = TRUE; + } + innerParenthesis++; + break; + + case ')': + innerParenthesis--; + if (0 == innerParenthesis) + { + param_pos[param_count][1] = count - 1; + param_count++; + param_pos[param_count][0] = + param_pos[param_count][1] = -1; + } + break; + + case '}': + stop = TRUE; + break; + + case '{': + if (subret = inner_convert_escape(conn, valptr, buf, sizeof(buf) - 1, &valptr, &inner_count), CONVERT_ESCAPE_OK != subret) + return CONVERT_ESCAPE_ERROR; + + if (inner_count + count >= maxLen) + return CONVERT_ESCAPE_OVERFLOW; + memcpy(&result[count], buf, inner_count); + count += inner_count; + ipos = (UInt4) (valptr - value); + continue; + } + if (stop) /* returns with the last } position */ + break; + result[count++] = *valptr; + } + if (param_pos[param_count][0] >= 0) + { + mylog("processParameters closing ) not found %d\n", innerParenthesis); + return CONVERT_ESCAPE_ERROR; } + result[count] = '\0'; + *input_consumed = ipos; + if (output_count) + *output_count = count; + return CONVERT_ESCAPE_OK; +} + +/* + * convert_escape() + * This function returns a pointer to static memory! + */ + +int +convert_escape(const char *value, StatementClass *stmt, int *npos, int *stsize, const char **val_resume) +{ + int ret, pos = *npos; + UInt4 count; - return escape; + while (ret = inner_convert_escape(SC_get_conn(stmt), value, + stmt->stmt_with_params + pos, *stsize - pos, val_resume, &count), + CONVERT_ESCAPE_OVERFLOW == ret) + { + if ((*stsize = enlarge_statement(stmt, *stsize * 2)) <= 0) + return CONVERT_ESCAPE_ERROR; + } + if (CONVERT_ESCAPE_OK == ret) + *npos += count; + return ret; } @@ -2218,7 +2523,7 @@ convert_special_chars(const char *si, char *dst, int used) multibyte_init(); #endif - for (i = 0; i < max; i++) + for (i = 0; i < max && si[i]; i++) { #ifdef MULTIBYTE if (multibyte_char_check(si[i]) != 0) @@ -2514,26 +2819,12 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, /* begin transaction if needed */ if (!CC_is_in_trans(conn)) { - QResultClass *res; - char ok; - - res = CC_send_query(conn, "BEGIN", NULL); - if (!res) - { - stmt->errormsg = "Could not begin (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) + if (!CC_begin(conn)) { stmt->errormsg = "Could not begin (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; return COPY_GENERAL_ERROR; } - - CC_set_in_trans(conn); } oid = atoi(value); @@ -2577,26 +2868,12 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) { - QResultClass *res; - char ok; - - res = CC_send_query(conn, "COMMIT", NULL); - if (!res) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) + if (!CC_commit(conn)) { stmt->errormsg = "Could not commit (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; return COPY_GENERAL_ERROR; } - - CC_set_no_trans(conn); } stmt->lobj_fd = -1; @@ -2626,26 +2903,12 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) { - QResultClass *res; - char ok; - - res = CC_send_query(conn, "COMMIT", NULL); - if (!res) + if (!CC_commit(conn)) { stmt->errormsg = "Could not commit (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; return COPY_GENERAL_ERROR; } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - - CC_set_no_trans(conn); } stmt->lobj_fd = -1; /* prevent further reading */ diff --git a/src/interfaces/odbc/convert.h b/src/interfaces/odbc/convert.h index 565ce6bf19..b06f72f354 100644 --- a/src/interfaces/odbc/convert.h +++ b/src/interfaces/odbc/convert.h @@ -18,6 +18,10 @@ #define COPY_RESULT_TRUNCATED 3 #define COPY_GENERAL_ERROR 4 #define COPY_NO_DATA_FOUND 5 +/* convert_escape results */ +#define CONVERT_ESCAPE_OK 0 +#define CONVERT_ESCAPE_OVERFLOW 1 +#define CONVERT_ESCAPE_ERROR 2 typedef struct { @@ -35,7 +39,8 @@ int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, I PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue); int copy_statement_with_parameters(StatementClass *stmt); -char *convert_escape(char *value); +int convert_escape(const char *value, StatementClass *stmt, + int *npos, int *stsize, const char **val_resume); BOOL convert_money(const char *s, char *sout, size_t soutmax); char parse_datetime(char *buf, SIMPLE_TIME *st); int convert_linefeeds(const char *s, char *dst, size_t max, BOOL *changed); diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 304d31d33e..1113ca57ac 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -233,6 +233,9 @@ PGAPI_Error( case STMT_INVALID_CURSOR_POSITION: strcpy(szSqlState, "S1109"); break; + case STMT_RETURN_NULL_WITHOUT_INDICATOR: + strcpy(szSqlState, "22002"); + break; case STMT_VALUE_OUT_OF_RANGE: strcpy(szSqlState, "22003"); break; @@ -376,6 +379,9 @@ PGAPI_Error( case STMT_NOT_IMPLEMENTED_ERROR: strcpy(szSqlState, "S1C00"); break; + case STMT_RETURN_NULL_WITHOUT_INDICATOR: + strcpy(szSqlState, "22002"); + break; case CONN_VALUE_OUT_OF_RANGE: case STMT_VALUE_OUT_OF_RANGE: strcpy(szSqlState, "22003"); diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index 5a693b7163..4c6608923a 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -196,7 +196,7 @@ PGAPI_Execute( StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; int i, - retval; + retval, start_row, end_row; mylog("%s: entering...\n", func); @@ -215,7 +215,10 @@ PGAPI_Execute( if (stmt->prepare && stmt->status == STMT_PREMATURE) { if (stmt->inaccurate_result) + { + stmt->exec_current_row = -1; SC_recycle_statement(stmt); + } else { stmt->status = STMT_FINISHED; @@ -278,6 +281,35 @@ PGAPI_Execute( return SQL_ERROR; } + if (start_row = stmt->exec_start_row, start_row < 0) + start_row = 0; + if (end_row = stmt->exec_end_row, end_row < 0) + end_row = stmt->options.paramset_size - 1; + if (stmt->exec_current_row < 0) + stmt->exec_current_row = start_row; + if (stmt->exec_current_row == start_row) + { + if (stmt->options.param_processed_ptr) + *stmt->options.param_processed_ptr = 0; + } + +next_param_row: +#if (ODBCVER >= 0x0300) + if (stmt->options.param_operation_ptr) + { + while (stmt->options.param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE) + { + if (stmt->options.param_status_ptr) + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_UNUSED; + if (stmt->exec_current_row >= end_row) + { + stmt->exec_current_row = -1; + return SQL_SUCCESS; + } + ++stmt->exec_current_row; + } + } +#endif /* ODBCVER */ /* * Check if statement has any data-at-execute parameters when it is * not in SC_pre_execute. @@ -289,17 +321,27 @@ PGAPI_Execute( * execute of this statement? Therefore check for params and * re-copy. */ + UInt4 offset = stmt->options.param_offset_ptr ? *stmt->options.param_offset_ptr : 0; + Int4 bind_size = stmt->options.param_bind_type; + Int4 current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; + stmt->data_at_exec = -1; for (i = 0; i < stmt->parameters_allocated; i++) { Int4 *pcVal = stmt->parameters[i].used; - if (pcVal && (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET)) - stmt->parameters[i].data_at_exec = TRUE; - else - stmt->parameters[i].data_at_exec = FALSE; + stmt->parameters[i].data_at_exec = FALSE; + if (pcVal) + { + if (bind_size > 0) + pcVal = (Int4 *)((char *)pcVal + offset + bind_size * current_row); + else + pcVal = (Int4 *)((char *)pcVal + offset + sizeof(SDWORD) * current_row); + if (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET) + stmt->parameters[i].data_at_exec = TRUE; + } /* Check for data at execution parameters */ - if (stmt->parameters[i].data_at_exec == TRUE) + if (stmt->parameters[i].data_at_exec) { if (stmt->data_at_exec < 0) stmt->data_at_exec = 1; @@ -333,68 +375,88 @@ PGAPI_Execute( mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params); + if (!stmt->inaccurate_result || !conn->connInfo.disallow_premature) + { + retval = SC_execute(stmt); + if (retval != SQL_ERROR) + { + if (stmt->options.param_processed_ptr) + (*stmt->options.param_processed_ptr)++; + } +#if (ODBCVER >= 0x0300) + if (stmt->options.param_status_ptr) + { + switch (retval) + { + case SQL_SUCCESS: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; + break; + case SQL_SUCCESS_WITH_INFO: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; + break; + default: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; + break; + } + } +#endif /* ODBCVER */ + if (retval == SQL_ERROR || + stmt->inaccurate_result || + stmt->exec_current_row >= end_row) + { + stmt->exec_current_row = -1; + return retval; + } + stmt->exec_current_row++; + goto next_param_row; + } /* * Get the field info for the prepared query using dummy backward * fetch. */ - if (stmt->inaccurate_result && conn->connInfo.disallow_premature) + if (SC_is_pre_executable(stmt)) { - if (SC_is_pre_executable(stmt)) + BOOL in_trans = CC_is_in_trans(conn); + BOOL issued_begin = FALSE, + begin_included = FALSE; + QResultClass *res; + + if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0) + begin_included = TRUE; + else if (!in_trans) { - BOOL in_trans = CC_is_in_trans(conn); - BOOL issued_begin = FALSE, - begin_included = FALSE; - QResultClass *res; - - if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0) - begin_included = TRUE; - else if (!in_trans) + if (issued_begin = CC_begin(conn), !issued_begin) { - res = CC_send_query(conn, "BEGIN", NULL); - if (res && !QR_aborted(res)) - issued_begin = TRUE; - if (res) - QR_Destructor(res); - if (!issued_begin) - { - stmt->errornumber = STMT_EXEC_ERROR; - stmt->errormsg = "Handle prepare error"; - return SQL_ERROR; - } - } - /* we are now in a transaction */ - CC_set_in_trans(conn); - stmt->result = res = CC_send_query(conn, stmt->stmt_with_params, NULL); - if (!res || QR_aborted(res)) - { - CC_abort(conn); stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "Handle prepare error"; return SQL_ERROR; } - else - { - if (CC_is_in_autocommit(conn)) - { - if (issued_begin) - { - res = CC_send_query(conn, "COMMIT", NULL); - CC_set_no_trans(conn); - if (res) - QR_Destructor(res); - } - else if (!in_trans && begin_included) - CC_set_no_trans(conn); - } - stmt->status = STMT_FINISHED; - return SQL_SUCCESS; - } + } + /* we are now in a transaction */ + CC_set_in_trans(conn); + stmt->result = res = CC_send_query(conn, stmt->stmt_with_params, NULL); + if (!res || QR_aborted(res)) + { + CC_abort(conn); + stmt->errornumber = STMT_EXEC_ERROR; + stmt->errormsg = "Handle prepare error"; + return SQL_ERROR; } else + { + if (CC_is_in_autocommit(conn)) + { + if (issued_begin) + CC_commit(conn); + else if (!in_trans && begin_included) + CC_set_no_trans(conn); + } + stmt->status = STMT_FINISHED; return SQL_SUCCESS; + } } - - return SC_execute(stmt); + else + return SQL_SUCCESS; } @@ -659,21 +721,7 @@ PGAPI_ParamData( /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(stmt->hdbc)) { - QResultClass *res; - char ok; - - res = CC_send_query(stmt->hdbc, "COMMIT", NULL); - if (!res) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - ok = QR_command_successful(res); - CC_set_no_trans(stmt->hdbc); - QR_Destructor(res); - if (!ok) + if (!CC_commit(stmt->hdbc)) { stmt->errormsg = "Could not commit (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; @@ -687,13 +735,47 @@ PGAPI_ParamData( /* Done, now copy the params and then execute the statement */ if (stmt->data_at_exec == 0) { + int end_row; + retval = copy_statement_with_parameters(stmt); if (retval != SQL_SUCCESS) return retval; stmt->current_exec_param = -1; - return SC_execute(stmt); + retval = SC_execute(stmt); + if (retval != SQL_ERROR) + { + if (stmt->options.param_processed_ptr) + (*stmt->options.param_processed_ptr)++; + } +#if (ODBCVER >= 0x0300) + if (stmt->options.param_status_ptr) + { + switch (retval) + { + case SQL_SUCCESS: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; + break; + case SQL_SUCCESS_WITH_INFO: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; + break; + default: + stmt->options.param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; + break; + } + } +#endif /* ODBCVER */ + if (stmt->exec_end_row < 0) + end_row = stmt->options.paramset_size - 1; + if (retval == SQL_ERROR || + stmt->exec_current_row >= end_row) + { + stmt->exec_current_row = -1; + return retval; + } + stmt->exec_current_row++; + return PGAPI_Execute(stmt); } /* @@ -705,7 +787,7 @@ PGAPI_ParamData( /* At least 1 data at execution parameter, so Fill in the token value */ for (; i < stmt->parameters_allocated; i++) { - if (stmt->parameters[i].data_at_exec == TRUE) + if (stmt->parameters[i].data_at_exec) { stmt->data_at_exec--; stmt->current_exec_param = i; @@ -780,28 +862,13 @@ PGAPI_PutData( /* begin transaction if needed */ if (!CC_is_in_trans(stmt->hdbc)) { - QResultClass *res; - char ok; - - res = CC_send_query(stmt->hdbc, "BEGIN", NULL); - if (!res) - { - stmt->errormsg = "Could not begin (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - ok = QR_command_successful(res); - QR_Destructor(res); - if (!ok) + if (!CC_begin(stmt->hdbc)) { stmt->errormsg = "Could not begin (in-line) a transaction"; stmt->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } - - CC_set_in_trans(stmt->hdbc); } /* store the oid */ diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index c77b3c5e89..865c8ac7ad 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -36,6 +36,9 @@ #include "misc.h" #include "pgtypes.h" #include "pgapifunc.h" +#ifdef MULTIBYTE +#include "multibyte.h" +#endif /* Trigger related stuff for SQLForeign Keys */ @@ -567,7 +570,10 @@ PGAPI_GetInfo( break; case SQL_SEARCH_PATTERN_ESCAPE: /* ODBC 1.0 */ - p = ""; + if (PG_VERSION_GE(conn, 6.5)) + p = "\\"; + else + p = ""; break; case SQL_SERVER_NAME: /* ODBC 1.0 */ @@ -1365,7 +1371,8 @@ PGAPI_Tables( { row = (TupleNode *) malloc(sizeof(TupleNode) + (5 - 1) *sizeof(TupleField)); - set_tuplefield_string(&row->tuple[0], ""); + /*set_tuplefield_string(&row->tuple[0], "");*/ + set_tuplefield_null(&row->tuple[0]); /* * I have to hide the table owner from Access, otherwise it @@ -1378,7 +1385,8 @@ PGAPI_Tables( mylog("%s: table_name = '%s'\n", func, table_name); - set_tuplefield_string(&row->tuple[1], ""); + /* set_tuplefield_string(&row->tuple[1], ""); */ + set_tuplefield_null(&row->tuple[1]); set_tuplefield_string(&row->tuple[2], table_name); set_tuplefield_string(&row->tuple[3], systable ? "SYSTEM TABLE" : (view ? "VIEW" : "TABLE")); set_tuplefield_string(&row->tuple[4], ""); @@ -1413,6 +1421,66 @@ PGAPI_Tables( } +/* + * PostgreSQL needs 2 '\\' to escape '_' and '%'. + */ +static int +reallyEscapeCatalogEscapes(const char *src, int srclen, char *dest, int dst_len) +{ + int i, outlen; + const char *in; + BOOL escape_in = FALSE; + + if (srclen == SQL_NULL_DATA) + { + dest[0] = '\0'; + return STRCPY_NULL; + } + else if (srclen == SQL_NTS) + srclen = strlen(src); + if (srclen <= 0) + return STRCPY_FAIL; +#ifdef MULTIBYTE + multibyte_init(); +#endif + for (i = 0, in = src, outlen = 0; i < srclen && outlen < dst_len; i++, in++) + { +#ifdef MULTIBYTE + if (multibyte_char_check(*in) != 0) + { + dest[outlen++] = *in; + continue; + } +#endif + if (escape_in) + { + switch (*in) + { + case '%': + case '_': + dest[outlen++] = '\\'; /* needs 1 more */ + break; + default: + dest[outlen++] = '\\'; + if (outlen < dst_len) + dest[outlen++] = '\\'; + if (outlen < dst_len) + dest[outlen++] = '\\'; + break; + } + } + if (*in == '\\') + escape_in = TRUE; + else + escape_in = FALSE; + if (outlen < dst_len) + dest[outlen++] = *in; + } + if (outlen < dst_len) + dest[outlen] = '\0'; + return outlen; +} + RETCODE SQL_API PGAPI_Columns( HSTMT hstmt, @@ -1423,7 +1491,8 @@ PGAPI_Columns( UCHAR FAR * szTableName, SWORD cbTableName, UCHAR FAR * szColumnName, - SWORD cbColumnName) + SWORD cbColumnName, + UWORD flag) { static char *func = "PGAPI_Columns"; StatementClass *stmt = (StatementClass *) hstmt; @@ -1476,9 +1545,22 @@ PGAPI_Columns( " and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)", PG_VERSION_LE(conn, 6.2) ? "a.attlen" : "a.atttypmod"); - my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName); - my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner); - my_strcat(columns_query, " and a.attname like '%.*s'", szColumnName, cbColumnName); + if ((flag & PODBC_NOT_SEARCH_PATTERN) != 0) + { + my_strcat(columns_query, " and c.relname = '%.*s'", szTableName, cbTableName); + my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner); + my_strcat(columns_query, " and a.attname = '%.*s'", szColumnName, cbColumnName); + } + else + { + char esc_table_name[MAX_TABLE_LEN * 2]; + int escTbnamelen; + + escTbnamelen = reallyEscapeCatalogEscapes(szTableName, cbTableName, esc_table_name, sizeof(esc_table_name)); + my_strcat(columns_query, " and c.relname like '%.*s'", esc_table_name, escTbnamelen); + my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner); + my_strcat(columns_query, " and a.attname like '%.*s'", szColumnName, cbColumnName); + } /* * give the output in the order the columns were defined when the @@ -1730,7 +1812,7 @@ PGAPI_Columns( *---------- */ qlog("PGAPI_Columns: table='%s',field_name='%s',type=%d,sqltype=%d,name='%s'\n", - table_name, field_name, field_type, pgtype_to_sqltype, field_type_name); + table_name, field_name, field_type, pgtype_to_sqltype(stmt,field_type), field_type_name); useStaticPrecision = TRUE; @@ -1892,8 +1974,10 @@ PGAPI_SpecialColumns( "from pg_user u, pg_class c where " "u.usesysid = c.relowner"); - my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName); - my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner); + /* TableName cannot contain a string search pattern */ + my_strcat(columns_query, " and c.relname = '%.*s'", szTableName, cbTableName); + /* SchemaName cannot contain a string search pattern */ + my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner); result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt); @@ -2114,8 +2198,11 @@ PGAPI_Statistics( * being shown. This would throw everything off. */ col_stmt->internal = TRUE; + /* + * table_name parameter cannot contain a string search pattern. + */ result = PGAPI_Columns(hcol_stmt, "", 0, "", 0, - table_name, (SWORD) strlen(table_name), "", 0); + table_name, (SWORD) strlen(table_name), "", 0, PODBC_NOT_SEARCH_PATTERN); col_stmt->internal = FALSE; if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) @@ -2725,9 +2812,7 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc continueExec = (continueExec && !bError); if (bError && CC_is_in_trans(conn)) { - if (res = CC_send_query(conn, "abort", NULL), res) - QR_Destructor(res); - CC_set_no_trans(conn); + CC_abort(conn); bError = FALSE; } /* restore the client encoding */ @@ -2811,9 +2896,7 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se continueExec = (continueExec && !bError); if (bError && CC_is_in_trans(conn)) { - if (res = CC_send_query(conn, "abort", NULL), res) - QR_Destructor(res); - CC_set_no_trans(conn); + CC_abort(conn); bError = FALSE; } /* restore the cleint encoding */ @@ -3647,7 +3730,7 @@ PGAPI_Procedures( " proname as " "PROCEDURE_NAME" ", '' as " "NUM_INPUT_PARAMS" "," " '' as " "NUM_OUTPUT_PARAMS" ", '' as " "NUM_RESULT_SETS" "," " '' as " "REMARKS" "," - " case when prorettype =0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc"); + " 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); res = CC_send_query(conn, proc_query, NULL); diff --git a/src/interfaces/odbc/odbcapi.c b/src/interfaces/odbc/odbcapi.c index a87c35850c..0527b21c38 100644 --- a/src/interfaces/odbc/odbcapi.c +++ b/src/interfaces/odbc/odbcapi.c @@ -84,8 +84,8 @@ SQLColumns(HSTMT StatementHandle, { mylog("[SQLColumns]"); return PGAPI_Columns(StatementHandle, CatalogName, NameLength1, - SchemaName, NameLength2, TableName, NameLength3, - ColumnName, NameLength4); + SchemaName, NameLength2, TableName, NameLength3, + ColumnName, NameLength4, 0); } @@ -218,7 +218,7 @@ SQLFetch(HSTMT StatementHandle) RETCODE SQL_API SQLFreeConnect(HDBC ConnectionHandle) { - mylog("[SQLFreeStmt]"); + mylog("[SQLFreeConnect]"); return PGAPI_FreeConnect(ConnectionHandle); } diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c index 8ad1dba7e6..1d5a623f74 100644 --- a/src/interfaces/odbc/odbcapi30.c +++ b/src/interfaces/odbc/odbcapi30.c @@ -85,12 +85,47 @@ SQLColAttribute(HSTMT StatementHandle, StringLength, NumericAttribute); } +static HSTMT +descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType) +{ + switch (descType) + { + case SQL_ATTR_APP_ROW_DESC: /* 10010 */ + return StatementHandle; /* this is bogus */ + case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ + return (HSTMT) ((SQLUINTEGER) StatementHandle + 1) ; /* this is bogus */ + case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ + return (HSTMT) ((SQLUINTEGER) StatementHandle + 2); /* this is bogus */ + case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ + return (HSTMT) ((SQLUINTEGER) StatementHandle + 3); /* this is bogus */ + } + return (HSTMT) 0; +} +static HSTMT +statementHandleFromDescHandle(HSTMT DescHandle, SQLINTEGER *descType) +{ + SQLUINTEGER res = (SQLUINTEGER) DescHandle % 4; + switch (res) + { + case 0: *descType = SQL_ATTR_APP_ROW_DESC; /* 10010 */ + break; + case 1: *descType = SQL_ATTR_APP_PARAM_DESC; /* 10011 */ + break; + case 2: *descType = SQL_ATTR_IMP_ROW_DESC; /* 10012 */ + break; + case 3: *descType = SQL_ATTR_IMP_PARAM_DESC; /* 10013 */ + break; + } + return (HSTMT) ((SQLUINTEGER) DescHandle - res); +} + /* new function */ RETCODE SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) { mylog("[[SQLCopyDesc]]\n"); + mylog("Error not implemented\n"); return SQL_ERROR; } @@ -129,7 +164,10 @@ SQLFetchScroll(HSTMT StatementHandle, if (FetchOrientation == SQL_FETCH_BOOKMARK) { if (stmt->options.bookmark_ptr) +{ FetchOffset += *((Int4 *) stmt->options.bookmark_ptr); +mylog("real FetchOffset = %d\n", FetchOffset); +} else { stmt->errornumber = STMT_SEQUENCE_ERROR; @@ -172,6 +210,7 @@ SQLGetDescField(SQLHDESC DescriptorHandle, SQLINTEGER *StringLength) { mylog("[[SQLGetDescField]]\n"); + mylog("Error not implemented\n"); return SQL_ERROR; } @@ -185,6 +224,7 @@ SQLGetDescRec(SQLHDESC DescriptorHandle, SQLSMALLINT *Scale, SQLSMALLINT *Nullable) { mylog("[[SQLGetDescRec]]\n"); + mylog("Error not implemented\n"); return SQL_ERROR; } @@ -305,22 +345,51 @@ SQLGetStmtAttr(HSTMT StatementHandle, RETCODE ret = SQL_SUCCESS; int len = 0; - mylog("[[%s]] %d\n", func, Attribute); + mylog("[[%s]] Handle=%u %d\n", func, StatementHandle, Attribute); switch (Attribute) { case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */ Value = stmt->options.bookmark_ptr; - + len = 4; + break; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ + Value = stmt->options.param_offset_ptr; + len = 4; + break; + case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ + *((SQLUINTEGER *) Value) = stmt->options.param_bind_type; + len = 4; + break; + case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ + Value = stmt->options.param_operation_ptr; + len = 4; + break; + case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ + Value = stmt->options.param_status_ptr; + len = 4; + break; + case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ + Value = stmt->options.param_processed_ptr; + len = 4; + break; + case SQL_ATTR_PARAMSET_SIZE: /* 22 */ + *((SQLUINTEGER *) Value) = stmt->options.paramset_size; + len = 4; + break; + case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ + Value = stmt->options.row_offset_ptr; + len = 4; + break; + case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ + Value = stmt->options.row_operation_ptr; len = 4; break; case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ Value = stmt->options.rowStatusArray; - len = 4; break; case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */ Value = stmt->options.rowsFetched; - len = 4; break; case SQL_ATTR_ROW_ARRAY_SIZE: /* 27 */ @@ -328,30 +397,17 @@ SQLGetStmtAttr(HSTMT StatementHandle, len = 4; break; case SQL_ATTR_APP_ROW_DESC: /* 10010 */ - *((HSTMT *) Value) = StatementHandle; /* this is useless */ - len = 4; - break; case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ - *((HSTMT *) Value) = StatementHandle; /* this is useless */ - len = 4; - break; case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ - *((HSTMT *) Value) = StatementHandle; /* this is useless */ - len = 4; - break; case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ - *((HSTMT *) Value) = StatementHandle; /* this is useless */ len = 4; + *((HSTMT *) Value) = descHandleFromStatementHandle(StatementHandle, Attribute); break; case SQL_ATTR_AUTO_IPD: /* 10001 */ /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ - case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */ case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */ - case SQL_ATTR_ENABLE_AUTO_IPD: /* 15 */ case SQL_ATTR_METADATA_ID: /* 10014 */ @@ -359,11 +415,6 @@ SQLGetStmtAttr(HSTMT StatementHandle, * case SQL_ATTR_PREDICATE_PTR: case * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR: */ - case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ - case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ - case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; stmt->errormsg = "Unsupported statement option (Get)"; SC_log_error(func, "", stmt); @@ -400,14 +451,127 @@ SQLSetConnectAttr(HDBC ConnectionHandle, 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; + 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; + default:ret = SQL_ERROR; + stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + } + 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; + 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) { - mylog("[[SQLSetDescField]]\n"); - return SQL_ERROR; + RETCODE ret = SQL_SUCCESS; + HSTMT hstmt; + SQLUINTEGER descType; + StatementClass *stmt; + 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; + mylog("Error not implemented\n"); + } + return ret; } /* new fucntion */ @@ -419,7 +583,8 @@ SQLSetDescRec(SQLHDESC DescriptorHandle, PTR Data, SQLINTEGER *StringLength, SQLINTEGER *Indicator) { - mylog("[[SQLsetDescRec]]\n"); + mylog("[[SQLSetDescRec]]\n"); + mylog("Error not implemented\n"); return SQL_ERROR; } @@ -466,16 +631,10 @@ SQLSetStmtAttr(HSTMT StatementHandle, { static char *func = "SQLSetStmtAttr"; StatementClass *stmt = (StatementClass *) StatementHandle; - UDWORD rowcount; - mylog("[[%s]] %d,%u\n", func, Attribute, Value); + mylog("[[%s]] Handle=%u %d,%u\n", func, StatementHandle, Attribute, Value); switch (Attribute) { - case SQL_ATTR_PARAMSET_SIZE: /* 22 */ - return PGAPI_ParamOptions(StatementHandle, (UWORD) Value, &rowcount); - case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ - case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ - case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */ case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */ @@ -484,28 +643,41 @@ SQLSetStmtAttr(HSTMT StatementHandle, case SQL_ATTR_APP_ROW_DESC: /* 10010 */ case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ case SQL_ATTR_AUTO_IPD: /* 10001 */ - /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ - case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ - case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ + /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ + case SQL_ATTR_IMP_ROW_DESC: /* 10012 (read-only) */ + case SQL_ATTR_IMP_PARAM_DESC: /* 10013 (read-only) */ case SQL_ATTR_METADATA_ID: /* 10014 */ /* * case SQL_ATTR_PREDICATE_PTR: case * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR: */ - case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ - case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ case SQL_ATTR_PARAM_OPERATION_PTR: /* 19 */ - case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ + case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ case SQL_ATTR_ROW_OPERATION_PTR: /* 24 */ stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; stmt->errormsg = "Unsupported statement option (Set)"; SC_log_error(func, "", stmt); return SQL_ERROR; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: /* 17 */ + stmt->options.param_offset_ptr = (SQLUINTEGER *) Value; + break; + case SQL_ATTR_ROW_BIND_OFFSET_PTR: /* 23 */ + stmt->options.row_offset_ptr = (SQLUINTEGER *) Value; + break; + case SQL_ATTR_FETCH_BOOKMARK_PTR: /* 16 */ stmt->options.bookmark_ptr = Value; - + break; + case SQL_ATTR_PARAM_BIND_TYPE: /* 18 */ + stmt->options.param_bind_type = (SQLUINTEGER) Value; + break; + case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ + stmt->options.param_processed_ptr = (SQLUINTEGER *) Value; + break; + case SQL_ATTR_PARAMSET_SIZE: /* 22 */ + stmt->options.paramset_size = (SQLUINTEGER) Value; break; case SQL_ATTR_ROW_STATUS_PTR: /* 25 */ stmt->options.rowStatusArray = (SQLUSMALLINT *) Value; @@ -642,111 +814,3 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) return SQL_SUCCESS; } -RETCODE SQL_API -PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, - SWORD cbInfoValueMax, SWORD FAR * pcbInfoValue) -{ - static char *func = "PGAPI_GetInfo30"; - ConnectionClass *conn = (ConnectionClass *) hdbc; - char *p = NULL; - int len = 0, - value = 0; - RETCODE result; - - switch (fInfoType) - { - case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: - len = 4; - value = 0; - break; - case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: - len = 4; - value = 0; - break; - - case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: - len = 4; - value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | - SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK; - break; - case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: - len = 4; - value = 0; - break; - case SQL_KEYSET_CURSOR_ATTRIBUTES1: - 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_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 | - SQL_CA2_SENSITIVITY_ADDITIONS | - SQL_CA2_SENSITIVITY_DELETIONS | - SQL_CA2_SENSITIVITY_UPDATES; - break; - - case SQL_STATIC_CURSOR_ATTRIBUTES1: - 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; - break; - case SQL_STATIC_CURSOR_ATTRIBUTES2: - len = 4; - value = SQL_CA2_OPT_ROWVER_CONCURRENCY | - SQL_CA2_SENSITIVITY_ADDITIONS | - SQL_CA2_SENSITIVITY_DELETIONS | - SQL_CA2_SENSITIVITY_UPDATES; - break; - default: - /* unrecognized key */ - conn->errormsg = "Unrecognized key passed to SQLGetInfo."; - conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR; - CC_log_error(func, "", conn); - return SQL_ERROR; - } - result = SQL_SUCCESS; - if (p) - { - /* char/binary data */ - len = strlen(p); - - if (rgbInfoValue) - { - strncpy_null((char *) rgbInfoValue, p, (size_t) cbInfoValueMax); - - if (len >= cbInfoValueMax) - { - result = SQL_SUCCESS_WITH_INFO; - conn->errornumber = STMT_TRUNCATED; - conn->errormsg = "The buffer was too small for tthe InfoValue."; - } - } - } - else - { - /* numeric data */ - if (rgbInfoValue) - { - if (len == 2) - *((WORD *) rgbInfoValue) = (WORD) value; - else if (len == 4) - *((DWORD *) rgbInfoValue) = (DWORD) value; - } - } - - if (pcbInfoValue) - *pcbInfoValue = len; - return result; -} diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index bf2707f4da..80ec7dac68 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -342,14 +342,12 @@ PGAPI_SetConnectOption( break; case SQL_AUTOCOMMIT: + if (vParam == SQL_AUTOCOMMIT_ON && CC_is_in_autocommit(conn)) + break; + else if (vParam == SQL_AUTOCOMMIT_OFF && !CC_is_in_autocommit(conn)) + break; if (CC_is_in_trans(conn)) - { - conn->errormsg = "Cannot switch commit mode while a transaction is in progress"; - conn->errornumber = CONN_TRANSACT_IN_PROGRES; - CC_log_error(func, "", conn); - return SQL_ERROR; - } - + CC_commit(conn); mylog("PGAPI_SetConnectOption: AUTOCOMMIT: transact_status=%d, vparam=%d\n", conn->transact_status, vParam); switch (vParam) @@ -475,7 +473,7 @@ PGAPI_GetConnectOption( break; case SQL_TXN_ISOLATION: /* NOT SUPPORTED */ - *((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE; + *((UDWORD *) pvParam) = SQL_TXN_READ_COMMITTED; break; /* These options should be handled by driver manager */ diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c index e73cb82a32..525fb736fd 100644 --- a/src/interfaces/odbc/parse.c +++ b/src/interfaces/odbc/parse.c @@ -742,7 +742,7 @@ parse_statement(StatementClass *stmt) col_stmt->internal = TRUE; result = PGAPI_Columns(hcol_stmt, "", 0, "", 0, - ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0); + ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0, PODBC_NOT_SEARCH_PATTERN); mylog(" Past PG_Columns\n"); if (result == SQL_SUCCESS) diff --git a/src/interfaces/odbc/pgapifunc.h b/src/interfaces/odbc/pgapifunc.h index 2ebf20371a..3117a6dc37 100644 --- a/src/interfaces/odbc/pgapifunc.h +++ b/src/interfaces/odbc/pgapifunc.h @@ -10,6 +10,7 @@ #include #include +#define PODBC_NOT_SEARCH_PATTERN 1L RETCODE SQL_API PGAPI_AllocConnect(HENV EnvironmentHandle, HDBC FAR * ConnectionHandle); @@ -25,7 +26,8 @@ RETCODE SQL_API PGAPI_Columns(HSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, - SQLCHAR *ColumnName, SQLSMALLINT NameLength4); + SQLCHAR *ColumnName, SQLSMALLINT NameLength4, + UWORD flag); RETCODE SQL_API PGAPI_Connect(HDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, SQLCHAR *UserName, SQLSMALLINT NameLength2, diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index 1388650a65..be822a2eb4 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -51,6 +51,7 @@ Int4 pgtypes_defined[] = { PG_TYPE_BPCHAR, PG_TYPE_DATE, PG_TYPE_TIME, + PG_TYPE_TIME_WITH_TMZONE, PG_TYPE_DATETIME, PG_TYPE_ABSTIME, PG_TYPE_TIMESTAMP, @@ -227,7 +228,11 @@ pgtype_to_sqltype(StatementClass *stmt, Int4 type) /* Change this to SQL_BIGINT for ODBC v3 bjm 2001-01-23 */ case PG_TYPE_INT8: +#if (ODBCVER >= 0x0300) + return SQL_BIGINT; +#else return SQL_CHAR; +#endif /* ODBCVER */ case PG_TYPE_NUMERIC: return SQL_NUMERIC; @@ -273,7 +278,11 @@ pgtype_to_ctype(StatementClass *stmt, Int4 type) switch (type) { case PG_TYPE_INT8: +#if (ODBCVER >= 0x0300) + return SQL_C_SBIGINT; +#else return SQL_C_CHAR; +#endif case PG_TYPE_NUMERIC: return SQL_C_CHAR; case PG_TYPE_INT2: @@ -287,13 +296,25 @@ pgtype_to_ctype(StatementClass *stmt, Int4 type) case PG_TYPE_FLOAT8: return SQL_C_DOUBLE; case PG_TYPE_DATE: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_DATE; +#else return SQL_C_DATE; +#endif /* ODBCVER */ case PG_TYPE_TIME: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_TIME; +#else return SQL_C_TIME; +#endif /* ODBCVER */ case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_TIMESTAMP; +#else return SQL_C_TIMESTAMP; +#endif /* ODBCVER */ case PG_TYPE_MONEY: return SQL_C_FLOAT; case PG_TYPE_BOOL: @@ -386,7 +407,7 @@ pgtype_to_name(StatementClass *stmt, Int4 type) static Int2 getNumericScale(StatementClass *stmt, Int4 type, int col) { - Int4 atttypmod; + Int4 atttypmod = -1; QResultClass *result; ColumnInfoClass *flds; @@ -405,12 +426,16 @@ getNumericScale(StatementClass *stmt, Int4 type, int col) { flds = result->fields; if (flds) - return flds->adtsize[col]; - else + { + atttypmod = flds->atttypmod[col]; + if (atttypmod < 0 && flds->adtsize[col] > 0) + return flds->adtsize[col]; + } + if (atttypmod < 0) return PG_NUMERIC_MAX_SCALE; } - - atttypmod = QR_get_atttypmod(result, col); + else + atttypmod = QR_get_atttypmod(result, col); if (atttypmod > -1) return (atttypmod & 0xffff); else @@ -423,7 +448,7 @@ getNumericScale(StatementClass *stmt, Int4 type, int col) static Int4 getNumericPrecision(StatementClass *stmt, Int4 type, int col) { - Int4 atttypmod; + Int4 atttypmod = -1; QResultClass *result; ColumnInfoClass *flds; @@ -442,16 +467,20 @@ getNumericPrecision(StatementClass *stmt, Int4 type, int col) { flds = result->fields; if (flds) - return flds->adtsize[col]; - else + { + atttypmod = flds->atttypmod[col]; + if (atttypmod < 0 && flds->adtsize[col] > 0) + return flds->adtsize[col]; + } + if (atttypmod < 0) return PG_NUMERIC_MAX_PRECISION; } - - atttypmod = QR_get_atttypmod(result, col); + else + atttypmod = QR_get_atttypmod(result, col); if (atttypmod > -1) return (atttypmod >> 16) & 0xffff; else - return (QR_get_display_size(result, col) >= 0 ? + return (QR_get_display_size(result, col) > 0 ? QR_get_display_size(result, col) : PG_NUMERIC_MAX_PRECISION); } @@ -586,13 +615,19 @@ getTimestampPrecision(StatementClass *stmt, Int4 type, int col) fixed = 8; break; case PG_TYPE_TIME_WITH_TMZONE: - fixed = 11; + if (USE_ZONE) + fixed = 11; + else + fixed = 8; break; case PG_TYPE_TIMESTAMP_NO_TMZONE: fixed = 19; break; default: - fixed = 22; + if (USE_ZONE) + fixed = 22; + else + fixed = 19; break; } scale = getTimestampScale(stmt, type, col); @@ -677,6 +712,8 @@ pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { + int dsize; + switch (type) { case PG_TYPE_INT2: @@ -693,7 +730,8 @@ pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown return 20; /* signed: 19 digits + sign */ case PG_TYPE_NUMERIC: - return getNumericPrecision(stmt, type, col) + 2; + dsize = getNumericPrecision(stmt, type, col); + return dsize < 0 ? dsize : dsize + 2; case PG_TYPE_MONEY: return 15; /* ($9,999,999.99) */ @@ -848,6 +886,7 @@ pgtype_auto_increment(StatementClass *stmt, Int4 type) case PG_TYPE_DATE: case PG_TYPE_TIME: + case PG_TYPE_TIME_WITH_TMZONE: case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: @@ -1013,8 +1052,14 @@ sqltype_to_default_ctype(Int2 sqltype) case SQL_LONGVARCHAR: case SQL_DECIMAL: case SQL_NUMERIC: +#if (ODBCVER < 0x0300) case SQL_BIGINT: return SQL_C_CHAR; +#else + return SQL_C_CHAR; + case SQL_BIGINT: + return SQL_C_SBIGINT; +#endif case SQL_BIT: return SQL_C_BIT; @@ -1041,13 +1086,25 @@ sqltype_to_default_ctype(Int2 sqltype) return SQL_C_BINARY; case SQL_DATE: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_DATE; +#else return SQL_C_DATE; +#endif /* ODBCVER */ case SQL_TIME: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_TIME; +#else return SQL_C_TIME; +#endif /* ODBCVER */ case SQL_TIMESTAMP: +#if (ODBCVER >= 0x0300) + return SQL_C_TYPE_TIMESTAMP; +#else return SQL_C_TIMESTAMP; +#endif /* ODBCVER */ default: /* should never happen */ @@ -1091,12 +1148,21 @@ ctype_length(Int2 ctype) return sizeof(UCHAR); case SQL_C_DATE: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_DATE: +#endif /* ODBCVER */ return sizeof(DATE_STRUCT); case SQL_C_TIME: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIME: +#endif /* ODBCVER */ return sizeof(TIME_STRUCT); case SQL_C_TIMESTAMP: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIMESTAMP: +#endif /* ODBCVER */ return sizeof(TIMESTAMP_STRUCT); case SQL_C_BINARY: diff --git a/src/interfaces/odbc/pgtypes.h b/src/interfaces/odbc/pgtypes.h index 1d276432ce..651817921f 100644 --- a/src/interfaces/odbc/pgtypes.h +++ b/src/interfaces/odbc/pgtypes.h @@ -58,7 +58,7 @@ #define PG_TYPE_VARCHAR 1043 #define PG_TYPE_DATE 1082 #define PG_TYPE_TIME 1083 -#define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */ +#define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */ #define PG_TYPE_DATETIME 1184 #define PG_TYPE_TIME_WITH_TMZONE 1266 /* since 7.1 */ #define PG_TYPE_TIMESTAMP 1296 /* deprecated since 7.0 */ @@ -96,4 +96,5 @@ char *pgtype_create_params(StatementClass *stmt, Int4 type); Int2 sqltype_to_default_ctype(Int2 sqltype); Int4 ctype_length(Int2 ctype); +#define USE_ZONE FALSE #endif diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 5317be3243..fc246b8b77 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.56 2001/11/05 17:46:38 momjian Exp $ + * $Id: psqlodbc.h,v 1.57 2002/02/18 03:16:11 inoue Exp $ * */ @@ -72,6 +72,8 @@ typedef UInt4 Oid; #ifndef WIN32 #define stricmp strcasecmp #define strnicmp strncasecmp +#else +#define snprintf _snprintf #endif /* Driver stuff */ @@ -85,7 +87,7 @@ typedef UInt4 Oid; #define DBMS_NAME "PostgreSQL" #endif /* ODBCVER */ -#define POSTGRESDRIVERVERSION "07.01.0009" +#define POSTGRESDRIVERVERSION "07.01.0010" #ifdef WIN32 #if (ODBCVER >= 0x0300) diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index 30a7ff7203..5b7b53583e 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -354,8 +354,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 7,1,0,9 - PRODUCTVERSION 7,1,0,9 + FILEVERSION 7,1,0,10 + PRODUCTVERSION 7,1,0,10 FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -377,14 +377,14 @@ BEGIN VALUE "CompanyName", "Insight Distribution Systems\0" #endif VALUE "FileDescription", "PostgreSQL Driver\0" - VALUE "FileVersion", " 07.01.0009\0" + VALUE "FileVersion", " 07.01.0010\0" VALUE "InternalName", "psqlodbc\0" VALUE "LegalCopyright", "\0" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0" VALUE "OriginalFilename", "psqlodbc.dll\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Microsoft Open Database Connectivity\0" - VALUE "ProductVersion", " 07.01.0009\0" + VALUE "ProductVersion", " 07.01.0010\0" VALUE "SpecialBuild", "\0" END END diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c index ca7a6dee9e..2cbe64f0b4 100644 --- a/src/interfaces/odbc/qresult.c +++ b/src/interfaces/odbc/qresult.c @@ -279,7 +279,11 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields); if (fetch_cursor) + { + if (self->cache_size <= 0) + self->cache_size = ci->drivers.fetch_max; tuple_size = self->cache_size; + } else tuple_size = TUPLE_MALLOC_INC; @@ -359,17 +363,12 @@ QR_close(QResultClass *self) { mylog("QResult: END transaction on conn=%u\n", self->conn); - res = CC_send_query(self->conn, "END", NULL); - - CC_set_no_trans(self->conn); - - if (res == NULL) + if (!CC_commit(self->conn)) { self->status = PGRES_FATAL_ERROR; QR_set_message(self, "Error ending transaction."); return FALSE; } - QR_Destructor(res); } } diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index 3bc5a5c0e1..4d221cacc6 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -1296,7 +1296,7 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count) if (rcnt == 1) { QR_set_position(qres, 0); - tuplen = res->tupleField; + tuplen = qres->tupleField; for (i = 0; i < res->num_fields; i++) { if (tupleo[i].value) @@ -1416,6 +1416,8 @@ SC_pos_update(StatementClass *stmt, RETCODE ret; char *tidval, *oidval; + UInt4 offset; + Int4 *used; mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, stmt->result->base, stmt->fi, stmt->ti); if (!(res = stmt->result)) @@ -1438,12 +1440,17 @@ SC_pos_update(StatementClass *stmt, sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name); num_cols = stmt->nfld; + if (stmt->options.row_offset_ptr) + offset = *stmt->options.row_offset_ptr; + else + offset = 0; for (i = upd_cols = 0; i < num_cols; i++) { - if (bindings[i].used) + if (used = bindings[i].used, used != NULL) { - mylog("%d used=%d\n", i, *bindings[i].used); - if (*bindings[i].used != SQL_IGNORE) + used += (offset >> 2); + mylog("%d used=%d\n", i, *used); + if (*used != SQL_IGNORE) { if (upd_cols) sprintf(updstr, "%s, \"%s\" = ?", updstr, stmt->fi[i]->name); @@ -1468,12 +1475,15 @@ SC_pos_update(StatementClass *stmt, if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS) return SQL_ERROR; qstmt = (StatementClass *) hstmt; + qstmt->options.param_bind_type = stmt->options.bind_size; + qstmt->options.param_offset_ptr = stmt->options.row_offset_ptr; for (i = j = 0; i < num_cols; i++) { - if (bindings[i].used) + if (used = bindings[i].used, used != NULL) { - mylog("%d used=%d\n", i, *bindings[i].used); - if (*bindings[i].used != SQL_IGNORE) + used += (offset >> 2); + mylog("%d used=%d\n", i, *used); + if (*used != SQL_IGNORE) { PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j, SQL_PARAM_INPUT, bindings[i].returntype, @@ -1486,6 +1496,7 @@ SC_pos_update(StatementClass *stmt, } } } + qstmt->exec_start_row = qstmt->exec_end_row = irow; ret = PGAPI_ExecDirect(hstmt, updstr, strlen(updstr)); if (ret == SQL_ERROR) { @@ -1533,6 +1544,19 @@ SC_pos_update(StatementClass *stmt, } else ret = SQL_SUCCESS_WITH_INFO; + if (stmt->options.rowStatusArray) + { + switch (ret) + { + 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; + } + } + return ret; } RETCODE SQL_API @@ -1606,6 +1630,18 @@ SC_pos_delete(StatementClass *stmt, } if (qres) QR_Destructor(qres); + if (stmt->options.rowStatusArray) + { + switch (ret) + { + 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; + } + } return ret; } RETCODE SQL_API @@ -1616,10 +1652,13 @@ SC_pos_add(StatementClass *stmt, add_cols, i; HSTMT hstmt; + StatementClass *qstmt; QResultClass *res; BindInfoClass *bindings = stmt->bindings; char addstr[4096]; RETCODE ret; + UInt4 offset; + Int4 *used; mylog("POS ADD fi=%x ti=%x\n", stmt->fi, stmt->ti); if (!(res = stmt->result)) @@ -1635,12 +1674,20 @@ SC_pos_add(StatementClass *stmt, sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name); if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS) return SQL_ERROR; + if (stmt->options.row_offset_ptr) + offset = *stmt->options.row_offset_ptr; + else + offset = 0; + qstmt = (StatementClass *) hstmt; + qstmt->options.param_bind_type = stmt->options.bind_size; + qstmt->options.param_offset_ptr = stmt->options.row_offset_ptr; for (i = add_cols = 0; i < num_cols; i++) { - if (bindings[i].used) + if (used = bindings[i].used, used != NULL) { - mylog("%d used=%d\n", i, *bindings[i].used); - if (*bindings[i].used != SQL_IGNORE) + used += (offset >> 2); + mylog("%d used=%d\n", i, *used); + if (*used != SQL_IGNORE) { if (add_cols) sprintf(addstr, "%s, \"%s\"", addstr, stmt->fi[i]->name); @@ -1661,7 +1708,6 @@ SC_pos_add(StatementClass *stmt, } if (add_cols > 0) { - StatementClass *qstmt = (StatementClass *) hstmt; sprintf(addstr, "%s) values (", addstr); for (i = 0; i < add_cols; i++) @@ -1673,6 +1719,7 @@ SC_pos_add(StatementClass *stmt, } strcat(addstr, ")"); mylog("addstr=%s\n", addstr); + qstmt->exec_start_row = qstmt->exec_end_row = irow; ret = PGAPI_ExecDirect(hstmt, addstr, strlen(addstr)); if (ret == SQL_NEED_DATA) /* must be fixed */ { @@ -1718,6 +1765,18 @@ SC_pos_add(StatementClass *stmt, else ret = SQL_SUCCESS_WITH_INFO; PGAPI_FreeStmt(hstmt, SQL_DROP); + if (stmt->options.rowStatusArray) + { + switch (ret) + { + 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; + } + } return ret; } diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index bfdb8a2fc0..2b2e5f0634 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -263,6 +263,9 @@ SC_Constructor(void) rv->data_at_exec = -1; rv->current_exec_param = -1; + rv->exec_start_row = -1; + rv->exec_end_row = -1; + rv->exec_current_row = -1; rv->put_data = FALSE; rv->lobj_fd = -1; @@ -404,6 +407,9 @@ SC_free_params(StatementClass *self, char option) free(self->parameters); self->parameters = NULL; self->parameters_allocated = 0; + self->exec_start_row = -1; + self->exec_end_row = -1; + self->exec_current_row = -1; } mylog("SC_free_params: EXIT\n"); @@ -778,10 +784,12 @@ SC_fetch(StatementClass *self) if (self->bookmark.buffer) { char buf[32]; + UInt4 offset = self->options.row_offset_ptr ? *self->options.row_offset_ptr : 0; sprintf(buf, "%ld", SC_get_bookmark(self)); result = copy_and_convert_field(self, 0, buf, - SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used); + SQL_C_ULONG, self->bookmark.buffer + offset, 0, + self->bookmark.used ? self->bookmark.used + (offset >> 2) : NULL); } #ifdef DRIVER_CURSOR_IMPLEMENT @@ -892,10 +900,7 @@ SC_execute(StatementClass *self) { static char *func = "SC_execute"; ConnectionClass *conn; - QResultClass *res; - char ok, - was_ok, - was_nonfatal; + char was_ok, was_nonfatal; Int2 oldstatus, numcols; QueryInfo qi; @@ -920,30 +925,13 @@ SC_execute(StatementClass *self) (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER))) { mylog(" about to begin a transaction on statement = %u\n", self); - res = CC_send_query(conn, "BEGIN", NULL); - if (QR_aborted(res)) + if (!CC_begin(conn)) { self->errormsg = "Could not begin a transaction"; self->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", self); return SQL_ERROR; } - - ok = QR_command_successful(res); - - mylog("SC_exec: begin ok = %d, status = %d\n", ok, QR_get_status(res)); - - QR_Destructor(res); - - if (!ok) - { - self->errormsg = "Could not begin a transaction"; - self->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", self); - return SQL_ERROR; - } - else - CC_set_in_trans(conn); } oldstatus = conn->status; @@ -1008,11 +996,7 @@ SC_execute(StatementClass *self) * transactions must be committed. (Hiroshi, 02/11/2001) */ if (!self->internal && CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) - { - res = CC_send_query(conn, "COMMIT", NULL); - QR_Destructor(res); - CC_set_no_trans(conn); - } + CC_commit(conn); } conn->status = oldstatus; diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 6cb0f6ccd3..534c8db4d8 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -75,6 +75,7 @@ typedef enum #define STMT_PROGRAM_TYPE_OUT_OF_RANGE 26 #define STMT_BAD_ERROR 27 #define STMT_INVALID_OPTION_IDENTIFIER 28 +#define STMT_RETURN_NULL_WITHOUT_INDICATOR 29 /* statement types */ enum @@ -207,6 +208,9 @@ struct StatementClass_ char *stmt_with_params; /* statement after parameter * substitution */ int stmt_size_limit; + int exec_start_row; + int exec_end_row; + int exec_current_row; char pre_executing; /* This statement is prematurely executing */ char inaccurate_result; /* Current status is PREMATURE but