From 56a20e9b121fd6833825d53a8404e9c9431c98ab Mon Sep 17 00:00:00 2001 From: Hiroshi Inoue Date: Mon, 12 Jun 2006 15:21:46 +0000 Subject: [PATCH] The version is now 8.2.0005. --- bind.c | 11 +- catfunc.h | 1 + columninfo.c | 85 ++---- columninfo.h | 29 +- connection.c | 49 ++-- connection.h | 17 +- convert.c | 8 +- descriptor.c | 1 + descriptor.h | 13 +- dlg_specific.h | 8 + info.c | 123 +++++---- info30.c | 8 +- inouealc.c | 29 ++ msdtc_enlist.cpp | 101 ++++--- odbcapi.c | 4 +- odbcapi25w.c | 2 +- odbcapi30.c | 6 +- odbcapiw.c | 21 +- options.c | 47 +++- parse.c | 671 +++++++++++++++++++++++++++++++++-------------- pgapi30.c | 5 +- pgapifunc.h | 6 +- pgtypes.c | 17 +- pgxalib.cpp | 326 ++++++++++++++--------- psqlodbc.h | 19 +- qresult.c | 4 +- qresult.h | 2 + results.c | 242 ++++++++++------- statement.c | 14 +- version.h | 6 +- 30 files changed, 1211 insertions(+), 664 deletions(-) diff --git a/bind.c b/bind.c index ddd703a..f9866df 100644 --- a/bind.c +++ b/bind.c @@ -304,6 +304,7 @@ PGAPI_DescribeParam( APDFields *apdopts; IPDFields *ipdopts; RETCODE ret = SQL_SUCCESS; + int num_params; mylog("%s: entering...%d\n", func, ipar); @@ -317,7 +318,15 @@ PGAPI_DescribeParam( apdopts = SC_get_APDF(stmt); ipdopts = SC_get_IPDF(stmt); /*if ((ipar < 1) || (ipar > ipdopts->allocated))*/ - if ((ipar < 1) || (ipar > stmt->num_params)) + num_params = stmt->num_params; + if (num_params < 0) + { + SQLSMALLINT num_p; + + PGAPI_NumParams(stmt, &num_p); + num_params = num_p; + } + if ((ipar < 1) || (ipar > num_params)) { inolog("num_params=%d\n", stmt->num_params); SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for PGAPI_DescribeParam.", func); diff --git a/catfunc.h b/catfunc.h index 87d3c05..ba74427 100644 --- a/catfunc.h +++ b/catfunc.h @@ -37,6 +37,7 @@ enum { ,COLUMNS_FIELD_TYPE ,COLUMNS_AUTO_INCREMENT ,COLUMNS_PHYSICAL_NUMBER + ,COLUMNS_TABLE_OID ,NUM_OF_COLUMNS_FIELDS }; /* SQLForeignKeys field position */ diff --git a/columninfo.c b/columninfo.c index c904046..0443e2f 100644 --- a/columninfo.c +++ b/columninfo.c @@ -31,13 +31,7 @@ CI_Constructor() if (rv) { rv->num_fields = 0; - rv->name = NULL; - rv->adtid = NULL; - rv->adtsize = NULL; - rv->display_size = NULL; - rv->atttypmod = NULL; - rv->relid = NULL; - rv->attid = NULL; + rv->coli_array = NULL; } return rv; @@ -137,51 +131,20 @@ CI_free_memory(ColumnInfoClass *self) register Int2 lf; int num_fields = self->num_fields; - if (self->name) + /* Safe to call even if null */ + self->num_fields = 0; + if (self->coli_array) { for (lf = 0; lf < num_fields; lf++) { - if (self->name[lf]) + if (self->coli_array[lf].name) { - free(self->name[lf]); - self->name[lf] = NULL; + free(self->coli_array[lf].name); + self->coli_array[lf].name = NULL; } } - free(self->name); - self->name = NULL; - } - /* Safe to call even if null */ - self->num_fields = 0; - - if (self->adtid) - { - free(self->adtid); - self->adtid = NULL; - } - if (self->adtsize) - { - free(self->adtsize); - self->adtsize = NULL; - } - if (self->display_size) - { - free(self->display_size); - self->display_size = NULL; - } - if (self->atttypmod) - { - free(self->atttypmod); - self->atttypmod = NULL; - } - if (self->relid) - { - free(self->relid); - self->relid = NULL; - } - if (self->attid) - { - free(self->attid); - self->attid = NULL; + free(self->coli_array); + self->coli_array = NULL; } } @@ -193,17 +156,7 @@ CI_set_num_fields(ColumnInfoClass *self, int new_num_fields, BOOL allocrelatt) self->num_fields = new_num_fields; - self->name = (char **) malloc(sizeof(char *) * self->num_fields); - memset(self->name, 0, sizeof(char *) * self->num_fields); - self->adtid = (Oid *) malloc(sizeof(Oid) * self->num_fields); - self->adtsize = (Int2 *) malloc(sizeof(Int2) * self->num_fields); - self->display_size = (Int4 *) malloc(sizeof(Int4) * self->num_fields); - self->atttypmod = (Int4 *) malloc(sizeof(Int4) * self->num_fields); - if (allocrelatt) - { - self->relid = (Oid *) malloc(sizeof(Oid) * self->num_fields); - self->attid = (Oid *) malloc(sizeof(Oid) * self->num_fields); - } + self->coli_array = (struct srvr_info *) calloc(sizeof(struct srvr_info), self->num_fields); } @@ -217,14 +170,12 @@ CI_set_field_info(ColumnInfoClass *self, int field_num, char *new_name, return; /* store the info */ - self->name[field_num] = strdup(new_name); - self->adtid[field_num] = new_adtid; - self->adtsize[field_num] = new_adtsize; - self->atttypmod[field_num] = new_atttypmod; - - self->display_size[field_num] = 0; - if (self->relid) - self->relid[field_num] = new_relid; - if (self->attid) - self->attid[field_num] = new_attid; + self->coli_array[field_num].name = strdup(new_name); + self->coli_array[field_num].adtid= new_adtid; + self->coli_array[field_num].adtsize = new_adtsize; + self->coli_array[field_num].atttypmod = new_atttypmod; + + self->coli_array[field_num].display_size = 0; + self->coli_array[field_num].relid = new_relid; + self->coli_array[field_num].attid = new_attid; } diff --git a/columninfo.h b/columninfo.h index f1da6e3..55c9af4 100644 --- a/columninfo.h +++ b/columninfo.h @@ -14,21 +14,26 @@ struct ColumnInfoClass_ { Int2 num_fields; - char **name; /* list of type names */ - Oid *adtid; /* list of type ids */ - Int2 *adtsize; /* list type sizes */ - Int4 *display_size; /* the display size (longest row) */ - Int4 *atttypmod; /* the length of bpchar/varchar */ - Oid *relid; /* list of relation ids */ - Oid *attid; /* list of attribute ids */ + struct srvr_info + { + char *name; /* type name */ + Oid adtid; /* type oid */ + Int2 adtsize; /* type size */ + Int4 display_size; /* the display size (longest row) */ + Int4 atttypmod; /* the length of bpchar/varchar */ + Oid relid; /* the relation id */ + Int2 attid; /* the attribute number */ + } *coli_array; }; #define CI_get_num_fields(self) (self->num_fields) -#define CI_get_oid(self, col) (self->adtid[col]) -#define CI_get_fieldname(self, col) (self->name[col]) -#define CI_get_fieldsize(self, col) (self->adtsize[col]) -#define CI_get_display_size(self, col) (self->display_size[col]) -#define CI_get_atttypmod(self, col) (self->atttypmod[col]) +#define CI_get_oid(self, col) (self->coli_array[col].adtid) +#define CI_get_fieldname(self, col) (self->coli_array[col].name) +#define CI_get_fieldsize(self, col) (self->coli_array[col].adtsize) +#define CI_get_display_size(self, col) (self->coli_array[col].display_size) +#define CI_get_atttypmod(self, col) (self->coli_array[col].atttypmod) +#define CI_get_relid(self, col) (self->coli_array[col].relid) +#define CI_get_attid(self, col) (self->coli_array[col].attid) ColumnInfoClass *CI_Constructor(void); void CI_Destructor(ColumnInfoClass *self); diff --git a/connection.c b/connection.c index df8627f..b18cf01 100644 --- a/connection.c +++ b/connection.c @@ -582,6 +582,7 @@ CC_cleanup(ConnectionClass *self) self->col_info = NULL; } self->ntables = 0; + self->coli_allocated = 0; if (self->num_discardp > 0 && self->discardp) { for (i = 0; i < self->num_discardp; i++) @@ -2090,16 +2091,21 @@ inolog("Discarded the first SAVEPOINT\n"); continue; /* discard the result */ } } + else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) + { + if (PROTOCOL_74(&(self->connInfo))) + CC_set_in_error_trans(self); /* mark the transaction error in case of manual rollback */ + else + CC_on_abort(self, NO_TRANS); + } else { - if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) - { - if (PROTOCOL_74(&(self->connInfo))) - CC_set_in_error_trans(self); /* mark the transaction error in case of manual rollback */ - else - CC_on_abort(self, NO_TRANS); - } - else if (!PROTOCOL_74(&(self->connInfo))) + ptr = strrchr(cmdbuffer, ' '); + if (ptr) + res->recent_processed_row_count = atoi(ptr + 1); + else + res->recent_processed_row_count = -1; + if (!PROTOCOL_74(&(self->connInfo))) { if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) CC_on_commit(self); @@ -2108,14 +2114,6 @@ inolog("Discarded the first SAVEPOINT\n"); else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) CC_on_abort(self, NO_TRANS); } - else - { - ptr = strrchr(cmdbuffer, ' '); - if (ptr) - res->recent_processed_row_count = atoi(ptr + 1); - else - res->recent_processed_row_count = -1; - } } if (QR_command_successful(res)) @@ -3220,3 +3218,22 @@ LIBPQ_send_cancel_request(const ConnectionClass *conn) else return FALSE; } + +const char *CurrCat(const ConnectionClass *conn) +{ + /* + if (conn->schema_support) + return conn->connInfo.database; + else + */ + return NULL; +} + +const char *CurrCatString(const ConnectionClass *conn) +{ + const char *cat = CurrCat(conn); + + if (!cat) + cat = NULL_STRING; + return cat; +} diff --git a/connection.h b/connection.h index c035b0e..2810d46 100644 --- a/connection.h +++ b/connection.h @@ -304,12 +304,13 @@ typedef struct /* Macro to determine is the connection using 6.3 protocol? */ #define PROTOCOL_63(conninfo_) (strncmp((conninfo_)->protocol, PG63, strlen(PG63)) == 0) -/* Macro to determine is the connection using 6.4 protocol? */ -#define PROTOCOL_74(conninfo_) (strncmp((conninfo_)->protocol, PG74, strlen(PG74)) == 0) - /* Macro to determine is the connection using 6.4 protocol? */ #define PROTOCOL_64(conninfo_) (strncmp((conninfo_)->protocol, PG64, strlen(PG64)) == 0) +/* Macro to determine is the connection using 7.4 protocol? */ +#define PROTOCOL_74(conninfo_) (strncmp((conninfo_)->protocol, PG74, strlen(PG74)) == 0) + +#define SUPPORT_DESCRIBE_PARAM(conninfo_) (PROTOCOL_74(conninfo_) && conninfo_->use_server_side_prepare) /* * Macros to compare the server's version with a specified version * 1st parameter: pointer to a ConnectionClass object @@ -352,8 +353,8 @@ struct col_info Int2 num_reserved_cols; QResultClass *result; pgNAME schema_name; - /**char table_name[TABLE_NAME_STORAGE_LEN + 1];**/ pgNAME table_name; + Oid table_oid; }; #define col_info_initialize(coli) (memset(coli, 0, sizeof(COL_INFO))) @@ -387,11 +388,12 @@ struct ConnectionClass_ CONN_Status status; ConnInfo connInfo; StatementClass **stmts; - int num_stmts; + Int2 num_stmts; + Int2 ncursors; SocketClass *sock; Int4 lobj_type; + Int2 coli_allocated; Int2 ntables; - Int2 ncursors; COL_INFO **col_info; long translation_option; HINSTANCE translation_handle; @@ -505,6 +507,9 @@ int CC_get_max_idlen(ConnectionClass *self); BOOL SendSyncRequest(ConnectionClass *self); +const char *CurrCat(const ConnectionClass *self); +const char *CurrCatString(const ConnectionClass *self); + /* CC_send_query options */ enum { IGNORE_ABORT_ON_CONN = 1L /* not set the error result even when */ diff --git a/convert.c b/convert.c index da99225..7bb1f0a 100644 --- a/convert.c +++ b/convert.c @@ -2175,6 +2175,7 @@ copy_statement_with_parameters(StatementClass *stmt, BOOL buildPrepareStatement) ConnInfo *ci = &(conn->connInfo); int current_row; +inolog("%s: enter prepared=%d\n", func, stmt->prepared); if (!stmt->statement) { SC_set_error(stmt, STMT_INTERNAL_ERROR, "No statement string", func); @@ -2689,8 +2690,6 @@ BOOL BuildBindRequest(StatementClass *stmt, const char *plan_name) PGAPI_NumParams(stmt, &num_p); num_params = num_p; } - if (0 == num_params) /* BindRequest Unnecessary */ - return TRUE; if (ipdopts->allocated < num_params) { SC_set_error(stmt, STMT_COUNT_FIELD_INCORRECT, "The # of binded parameters < the # of parameter markers", func); @@ -2728,7 +2727,8 @@ inolog("num_p=%d\n", num_p); net_one = htons(net_one); memcpy(bindreq + leng, &netnum_p, sizeof(netnum_p)); /* number of parameter format */ leng += sizeof(SWORD); - memset(bindreq + leng, 0, sizeof(SWORD) * num_p); /* initialize by text format */ + if (num_p > 0) + memset(bindreq + leng, 0, sizeof(SWORD) * num_p); /* initialize by text format */ for (i = stmt->proc_return, j = 0; i < num_params; i++) { inolog("%dth paramater type oid is %u\n", i, parameters[i].PGType); @@ -4333,7 +4333,7 @@ conv_to_octal(UCHAR val, char *octal, char escape_ch) if (escape_ch) octal[pos++] = escape_ch; - octal[pos + 1] = BYTEA_ESCAPE_CHAR; + octal[pos] = BYTEA_ESCAPE_CHAR; len = 4 + pos; octal[len] = '\0'; diff --git a/descriptor.c b/descriptor.c index 3ae28dc..4741df3 100644 --- a/descriptor.c +++ b/descriptor.c @@ -136,6 +136,7 @@ static void IRDFields_free(IRDFields * self) FI_Destructor(self->fi, self->nfields, TRUE); self->fi = NULL; } + self->allocated = 0; self->nfields = 0; } diff --git a/descriptor.h b/descriptor.h index 614be0a..fcd446c 100644 --- a/descriptor.h +++ b/descriptor.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: descriptor.h,v 1.14 2006/04/08 16:30:01 dpage Exp $ + * $Id: descriptor.h,v 1.15 2006/06/12 15:21:45 hinoue Exp $ * */ @@ -33,7 +33,7 @@ do { \ do { \ if ((the_name).name) \ free((the_name).name); \ - (the_name).name = strdup((str)); \ + (the_name).name = (str ? strdup((str)) : NULL); \ } while (0) #define NAME_TO_STR(str, the_name) \ do {\ @@ -65,6 +65,7 @@ enum { TI_UPDATABLE = 1L ,TI_HASOIDS_CHECKED = (1L << 1) ,TI_HASOIDS = (1L << 2) + ,TI_COLATTRIBUTE = (1L << 3) }; typedef struct { @@ -94,6 +95,7 @@ enum { ,FIELD_TEMP_SET = (1L << 1) ,FIELD_COL_ATTRIBUTE = (1L << 2) ,FIELD_PARSED_OK = (1L << 3) + ,FIELD_PARSED_INCOMPLETE = (1L << 4) }; typedef struct { @@ -111,7 +113,7 @@ typedef struct int decimal_digits; /* scale in 2.x */ int display_size; int length; - int type; + Oid type; char expr; char quote; char dquote; @@ -122,6 +124,7 @@ Int4 FI_precision(const FIELD_INFO *); Int4 FI_scale(const FIELD_INFO *); void FI_Constructor(FIELD_INFO *, BOOL reuse); void FI_Destructor(FIELD_INFO **, int, BOOL freeFI); +#define FI_is_applicable(fi) (NULL != fi && (fi->flag & (FIELD_PARSED_OK | FIELD_COL_ATTRIBUTE)) != 0) typedef struct DescriptorHeader_ { @@ -161,7 +164,7 @@ struct APDFields_ { SQLLEN paramset_size; int param_bind_type; /* size of each structure if using - * Row-wsie Parameter Binding */ + * Row-wise Parameter Binding */ UInt2 *param_operation_ptr; UInt4 *param_offset_ptr; ParameterInfoClass *bookmark; /* dummy item to fit APD to ARD */ @@ -176,6 +179,7 @@ struct IRDFields_ UInt4 *rowsFetched; UInt2 *rowStatusArray; UInt4 nfields; + UInt4 allocated; FIELD_INFO **fi; }; @@ -231,6 +235,7 @@ BindInfoClass *ARD_AllocBookmark(ARDFields *self); void ARD_unbind_cols(ARDFields *self, BOOL freeall); void APD_free_params(APDFields *self, char option); void IPD_free_params(IPDFields *self, char option); +BOOL getCOLIfromTI(const char *, StatementClass *, const Oid, TABLE_INFO **); #if (ODBCVER >= 0x0300) RETCODE DC_set_stmt(DescriptorClass *desc, StatementClass *stmt); void DC_clear_error(DescriptorClass *desc); diff --git a/dlg_specific.h b/dlg_specific.h index e38d379..dc909a2 100644 --- a/dlg_specific.h +++ b/dlg_specific.h @@ -33,7 +33,15 @@ #define ODBC_DATASOURCES "ODBC Data Sources" +#if (ODBCVER >= 0x0300) +#ifdef UNICODE_SUPPORT +#define INI_DSN "PostgreSQL30W" +#else +#define INI_DSN "PostgreSQL30" +#endif /* UNICODE_SUPPORT */ +#else #define INI_DSN DBMS_NAME +#endif /* ODBCVER */ #define INI_KDESC "Description" /* Data source * description */ diff --git a/info.c b/info.c index ab0cf89..a77cb2f 100644 --- a/info.c +++ b/info.c @@ -55,7 +55,6 @@ CSTR pubstr = "public"; CSTR likeop = "like"; CSTR eqop = "="; - RETCODE SQL_API PGAPI_GetInfo( HDBC hdbc, @@ -67,8 +66,8 @@ PGAPI_GetInfo( CSTR func = "PGAPI_GetInfo"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci; - char *p = NULL, - tmp[MAX_INFO_STRING]; + const char *p = NULL; + char tmp[MAX_INFO_STRING]; int len = 0, value = 0; RETCODE result; @@ -78,7 +77,7 @@ PGAPI_GetInfo( if (!conn) { - CC_log_error(func, "", NULL); + CC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -593,20 +592,32 @@ PGAPI_GetInfo( case SQL_QUALIFIER_LOCATION: /* ODBC 2.0 */ len = 2; - value = SQL_QL_START; + if (CurrCat(conn)) + value = SQL_QL_START; + else + value = 0; break; case SQL_QUALIFIER_NAME_SEPARATOR: /* ODBC 1.0 */ - p = ""; + if (CurrCat(conn)) + p = "."; + else + p = NULL_STRING; break; case SQL_QUALIFIER_TERM: /* ODBC 1.0 */ - p = ""; + if (CurrCat(conn)) + p = "catalog"; + else + p = NULL_STRING; break; case SQL_QUALIFIER_USAGE: /* ODBC 2.0 */ len = 4; - value = 0; + if (CurrCat(conn)) + value = SQL_CU_DML_STATEMENTS; + else + value = 0; break; case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */ @@ -646,7 +657,7 @@ PGAPI_GetInfo( if (PG_VERSION_GE(conn, 6.5)) p = "\\"; else - p = ""; + p = NULL_STRING; break; case SQL_SERVER_NAME: /* ODBC 1.0 */ @@ -1033,7 +1044,7 @@ PGAPI_GetFunctions( pfExists[SQL_API_SQLCOLUMNPRIVILEGES] = FALSE; pfExists[SQL_API_SQLDATASOURCES] = FALSE; /* only implemented by * DM */ - if (PROTOCOL_74(ci)) + if (SUPPORT_DESCRIBE_PARAM(ci)) pfExists[SQL_API_SQLDESCRIBEPARAM] = TRUE; else pfExists[SQL_API_SQLDESCRIBEPARAM] = FALSE; /* not properly @@ -1223,7 +1234,7 @@ PGAPI_GetFunctions( *pfExists = FALSE; break; /* only implemented by DM */ case SQL_API_SQLDESCRIBEPARAM: - if (PROTOCOL_74(ci)) + if (SUPPORT_DESCRIBE_PARAM(ci)) *pfExists = TRUE; else *pfExists = FALSE; @@ -1774,7 +1785,7 @@ retry_public_schema: tuple = QR_AddNew(res); /* set_tuplefield_null(&tuple[0]); */ - set_tuplefield_string(&tuple[0], ""); + set_tuplefield_string(&tuple[0], CurrCat(conn)); /* * I have to hide the table owner from Access, otherwise it @@ -1875,6 +1886,7 @@ PGAPI_Columns( ConnInfo *ci; ConnectionClass *conn; SQLSMALLINT internal_asis_type = SQL_C_CHAR, cbSchemaName; + SQLINTEGER greloid; const char *like_or_eq; const char *szSchemaName; @@ -1939,7 +1951,7 @@ retry_public_schema: strncpy(columns_query, "select n.nspname, c.relname, a.attname, a.atttypid" ", t.typname, a.attnum, a.attlen, a.atttypmod, a.attnotnull" - ", c.relhasrules, c.relkind, d.adsrc from (((pg_catalog.pg_class c" + ", c.relhasrules, c.relkind, c.oid, d.adsrc from (((pg_catalog.pg_class c" " inner join pg_catalog.pg_namespace n on n.oid = c.relnamespace", sizeof(columns_query)); if (search_by_ids) @@ -1951,9 +1963,14 @@ retry_public_schema: schema_strcat1(columns_query, " and n.nspname %s '%.*s'", like_or_eq, escSchemaName, SQL_NTS, szTableName, cbTableName, conn); } strcat(columns_query, ") inner join pg_catalog.pg_attribute a" - " on (not a.attisdropped) and a.attnum > 0"); + " on (not a.attisdropped)"); + if (0 == attnum && NULL == escColumnName) + strcat(columns_query, " and a.attnum > 0"); if (search_by_ids) - snprintf(columns_query, sizeof(columns_query), "%s and a.attnum = %d", columns_query, attnum); + { + if (attnum != 0) + snprintf(columns_query, sizeof(columns_query), "%s and a.attnum = %d", columns_query, attnum); + } else if (escColumnName) snprintf(columns_query, sizeof(columns_query), "%s and a.attname %s '%s'", columns_query, like_or_eq, escColumnName); strcat(columns_query, @@ -1967,7 +1984,7 @@ retry_public_schema: snprintf(columns_query, sizeof(columns_query), "select u.usename, c.relname, a.attname, a.atttypid" ", t.typname, a.attnum, a.attlen, %s, a.attnotnull" - ", c.relhasrules, c.relkind, NULL from pg_user u" + ", c.relhasrules, c.relkind, c.oid, NULL from pg_user u" ", pg_class c, pg_attribute a, pg_type t where" " u.usesysid = c.relowner and c.oid= a.attrelid" " and a.atttypid = t.oid and (a.attnum > 0)", @@ -2114,6 +2131,14 @@ retry_public_schema: goto cleanup; } + result = PGAPI_BindCol(hcol_stmt, 12, SQL_C_LONG, + &greloid, sizeof(greloid), NULL); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + SC_error_copy(stmt, col_stmt, TRUE); + goto cleanup; + } + if (res = QR_Constructor(), !res) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Couldn't allocate memory for PGAPI_Columns result.", func); @@ -2159,6 +2184,7 @@ retry_public_schema: QR_set_field_info_v(res, COLUMNS_FIELD_TYPE, "FIELD_TYPE", PG_TYPE_INT4, 4); QR_set_field_info_v(res, COLUMNS_AUTO_INCREMENT, "AUTO_INCREMENT", PG_TYPE_INT4, 4); QR_set_field_info_v(res, COLUMNS_PHYSICAL_NUMBER, "PHYSICAL NUMBER", PG_TYPE_INT2, 2); + QR_set_field_info_v(res, COLUMNS_TABLE_OID, "TABLE OID", PG_TYPE_INT4, 4); ordinal = 1; result = PGAPI_Fetch(hcol_stmt); @@ -2182,7 +2208,7 @@ retry_public_schema: /* For OID fields */ the_type = PG_TYPE_OID; tuple = QR_AddNew(res); - set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], ""); + set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], CurrCat(conn)); /* see note in SQLTables() */ if (conn->schema_support) set_tuplefield_string(&tuple[COLUMNS_SCHEMA_NAME], GET_SCHEMA_NAME(table_owner)); @@ -2205,7 +2231,7 @@ retry_public_schema: set_tuplefield_null(&tuple[COLUMNS_COLUMN_DEF]); set_tuplefield_int2(&tuple[COLUMNS_SQL_DATA_TYPE], sqltype); set_tuplefield_null(&tuple[COLUMNS_SQL_DATETIME_SUB]); - set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_null(&tuple[COLUMNS_CHAR_OCTET_LENGTH]); set_tuplefield_int4(&tuple[COLUMNS_ORDINAL_POSITION], ordinal); set_tuplefield_string(&tuple[COLUMNS_IS_NULLABLE], "No"); #endif /* ODBCVER */ @@ -2213,6 +2239,7 @@ retry_public_schema: set_tuplefield_int4(&tuple[COLUMNS_FIELD_TYPE], the_type); set_tuplefield_int4(&tuple[COLUMNS_AUTO_INCREMENT], TRUE); set_tuplefield_int2(&tuple[COLUMNS_PHYSICAL_NUMBER], OID_ATTNUM); + set_tuplefield_int4(&tuple[COLUMNS_TABLE_OID], greloid); ordinal++; } } @@ -2225,18 +2252,18 @@ retry_public_schema: attdef = NULL; PGAPI_SetPos(hcol_stmt, 1, SQL_POSITION, 0); - PGAPI_GetData(hcol_stmt, 12, internal_asis_type, NULL, 0, &len_needed); + PGAPI_GetData(hcol_stmt, 13, internal_asis_type, NULL, 0, &len_needed); if (len_needed > 0) { mylog("len_needed=%d\n", len_needed); attdef = malloc(len_needed + 1); - PGAPI_GetData(hcol_stmt, 12, internal_asis_type, attdef, len_needed + 1, &len_needed); + PGAPI_GetData(hcol_stmt, 13, internal_asis_type, attdef, len_needed + 1, &len_needed); mylog(" and the data=%s\n", attdef); } tuple = QR_AddNew(res); sqltype = SQL_TYPE_NULL; /* unspecified */ - set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], ""); + set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], CurrCat(conn)); /* see note in SQLTables() */ if (conn->schema_support) set_tuplefield_string(&tuple[COLUMNS_SCHEMA_NAME], GET_SCHEMA_NAME(table_owner)); @@ -2382,6 +2409,7 @@ mylog(" and the data=%s\n", attdef); } set_tuplefield_int4(&tuple[COLUMNS_AUTO_INCREMENT], auto_unique); set_tuplefield_int2(&tuple[COLUMNS_PHYSICAL_NUMBER], field_number); + set_tuplefield_int4(&tuple[COLUMNS_TABLE_OID], greloid); ordinal++; result = PGAPI_Fetch(hcol_stmt); @@ -2405,7 +2433,7 @@ mylog(" and the data=%s\n", attdef); tuple = QR_AddNew(res); - set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], ""); + set_tuplefield_string(&tuple[COLUMNS_CATALOG_NAME], CurrCat(conn)); if (conn->schema_support) set_tuplefield_string(&tuple[COLUMNS_SCHEMA_NAME], GET_SCHEMA_NAME(table_owner)); else @@ -2425,7 +2453,7 @@ mylog(" and the data=%s\n", attdef); set_tuplefield_null(&tuple[COLUMNS_COLUMN_DEF]); set_tuplefield_int2(&tuple[COLUMNS_SQL_DATA_TYPE], sqltype); set_tuplefield_null(&tuple[COLUMNS_SQL_DATETIME_SUB]); - set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_null(&tuple[COLUMNS_CHAR_OCTET_LENGTH]); set_tuplefield_int4(&tuple[COLUMNS_ORDINAL_POSITION], ordinal); set_tuplefield_string(&tuple[COLUMNS_IS_NULLABLE], "No"); #endif /* ODBCVER */ @@ -2433,6 +2461,7 @@ mylog(" and the data=%s\n", attdef); set_tuplefield_int4(&tuple[COLUMNS_FIELD_TYPE], the_type); set_tuplefield_int4(&tuple[COLUMNS_AUTO_INCREMENT], FALSE); set_tuplefield_int2(&tuple[COLUMNS_PHYSICAL_NUMBER], XMIN_ATTNUM); + set_tuplefield_int4(&tuple[COLUMNS_TABLE_OID], greloid); ordinal++; } result = SQL_SUCCESS; @@ -2648,7 +2677,6 @@ retry_public_schema: /* there's no oid for views */ if (fColType == SQL_BEST_ROWID) { - result = SQL_NO_DATA_FOUND; goto cleanup; } else if (fColType == SQL_ROWVER) @@ -2673,27 +2701,28 @@ inolog("Add ctid\n"); /* use the oid value for the rowid */ if (fColType == SQL_BEST_ROWID) { + Int2 the_type = PG_TYPE_OID; + if (relhasoids[0] != '1') { - result = SQL_NO_DATA_FOUND; goto cleanup; } tuple = QR_AddNew(res); set_tuplefield_int2(&tuple[0], SQL_SCOPE_SESSION); set_tuplefield_string(&tuple[1], "oid"); - set_tuplefield_int2(&tuple[2], pgtype_to_concise_type(stmt, PG_TYPE_OID, PG_STATIC)); - set_tuplefield_string(&tuple[3], "OID"); - set_tuplefield_int4(&tuple[4], pgtype_column_size(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); - set_tuplefield_int4(&tuple[5], pgtype_buffer_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); - set_tuplefield_int2(&tuple[6], pgtype_decimal_digits(stmt, PG_TYPE_OID, PG_STATIC)); + set_tuplefield_int2(&tuple[2], pgtype_to_concise_type(stmt, the_type, PG_STATIC)); + set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type)); + set_tuplefield_int4(&tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); set_tuplefield_int2(&tuple[7], SQL_PC_PSEUDO); } else if (fColType == SQL_ROWVER) { - Int2 the_type = PG_TYPE_INT4; + Int2 the_type = PG_TYPE_XID; - if (atoi(ci->row_versioning)) + /* if (atoi(ci->row_versioning)) */ { tuple = QR_AddNew(res); @@ -2854,8 +2883,8 @@ PGAPI_Statistics( /* * table_name parameter cannot contain a string search pattern. */ - result = PGAPI_Columns(hcol_stmt, "", 0, table_schemaname, SQL_NTS, - table_name, SQL_NTS, "", 0, PODBC_NOT_SEARCH_PATTERN | PODBC_SEARCH_PUBLIC_SCHEMA, 0, 0); + result = PGAPI_Columns(hcol_stmt, NULL, 0, table_schemaname, SQL_NTS, + table_name, SQL_NTS, NULL, 0, PODBC_NOT_SEARCH_PATTERN | PODBC_SEARCH_PUBLIC_SCHEMA, 0, 0); col_stmt->internal = FALSE; if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) @@ -3043,7 +3072,7 @@ PGAPI_Statistics( tuple = QR_AddNew(res); /* no table qualifier */ - set_tuplefield_string(&tuple[STATS_CATALOG_NAME], ""); + set_tuplefield_string(&tuple[STATS_CATALOG_NAME], CurrCat(conn)); /* don't set the table owner, else Access tries to use it */ set_tuplefield_string(&tuple[STATS_SCHEMA_NAME], GET_SCHEMA_NAME(table_schemaname)); set_tuplefield_string(&tuple[STATS_TABLE_NAME], table_name); @@ -3052,7 +3081,7 @@ PGAPI_Statistics( set_tuplefield_int2(&tuple[STATS_NON_UNIQUE], (Int2) (ci->drivers.unique_index ? FALSE : TRUE)); /* no index qualifier */ - set_tuplefield_string(&tuple[STATS_INDEX_QUALIFIER], ""); + set_tuplefield_string(&tuple[STATS_INDEX_QUALIFIER], CurrCat(conn)); snprintf(buf, sizeof(table_name), "%s_idx_fake_oid", table_name); set_tuplefield_string(&tuple[STATS_INDEX_NAME], buf); @@ -3083,7 +3112,7 @@ PGAPI_Statistics( tuple = QR_AddNew(res); /* no table qualifier */ - set_tuplefield_string(&tuple[STATS_CATALOG_NAME], ""); + set_tuplefield_string(&tuple[STATS_CATALOG_NAME], CurrCat(conn)); /* don't set the table owner, else Access tries to use it */ set_tuplefield_string(&tuple[STATS_SCHEMA_NAME], GET_SCHEMA_NAME(table_schemaname)); set_tuplefield_string(&tuple[STATS_TABLE_NAME], table_name); @@ -3095,7 +3124,7 @@ PGAPI_Statistics( set_tuplefield_int2(&tuple[STATS_NON_UNIQUE], TRUE); /* no index qualifier */ - set_tuplefield_string(&tuple[STATS_INDEX_QUALIFIER], ""); + set_tuplefield_string(&tuple[STATS_INDEX_QUALIFIER], CurrCat(conn)); set_tuplefield_string(&tuple[STATS_INDEX_NAME], index_name); /* @@ -3512,7 +3541,7 @@ retry_public_schema: { tuple = QR_AddNew(res); - set_tuplefield_null(&tuple[0]); + set_tuplefield_string(&tuple[0], CurrCat(conn)); /* * I have to hide the table owner from Access, otherwise it @@ -4097,7 +4126,7 @@ PGAPI_ForeignKeys( fkey_text = getClientColumnName(conn, relid1, fkey_ptr, &fkey_alloced); mylog("%s: pk_table = '%s', pkey_ptr = '%s'\n", func, pk_table_fetched, pkey_text); - set_tuplefield_null(&tuple[FKS_PKTABLE_CAT]); + set_tuplefield_string(&tuple[FKS_PKTABLE_CAT], CurrCat(conn)); set_tuplefield_string(&tuple[FKS_PKTABLE_SCHEM], GET_SCHEMA_NAME(schema_fetched)); set_tuplefield_string(&tuple[FKS_PKTABLE_NAME], pk_table_fetched); set_tuplefield_string(&tuple[FKS_PKCOLUMN_NAME], pkey_text); @@ -4388,7 +4417,7 @@ PGAPI_ForeignKeys( tuple = QR_AddNew(res); mylog("pk_table_needed = '%s', pkey_ptr = '%s'\n", pk_table_needed, pkey_text); - set_tuplefield_null(&tuple[FKS_PKTABLE_CAT]); + set_tuplefield_string(&tuple[FKS_PKTABLE_CAT], CurrCat(conn)); set_tuplefield_string(&tuple[FKS_PKTABLE_SCHEM], GET_SCHEMA_NAME(schema_needed)); set_tuplefield_string(&tuple[FKS_PKTABLE_NAME], pk_table_needed); set_tuplefield_string(&tuple[FKS_PKCOLUMN_NAME], pkey_text); @@ -4677,7 +4706,7 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); if (pgtype != 0 && pgtype != PG_TYPE_VOID && !atttypid && !proargmodes) { tuple = QR_AddNew(res); - set_tuplefield_null(&tuple[0]); + set_tuplefield_string(&tuple[0], CurrCat(conn)); set_nullfield_string(&tuple[1], schema_name); set_tuplefield_string(&tuple[2], procname); set_tuplefield_string(&tuple[3], ""); @@ -4775,7 +4804,7 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); } tuple = QR_AddNew(res); - set_tuplefield_null(&tuple[0]); + set_tuplefield_string(&tuple[0], CurrCat(conn)); set_nullfield_string(&tuple[1], schema_name); set_tuplefield_string(&tuple[2], procname); if (proargnames) @@ -4832,7 +4861,7 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); attname = QR_get_value_backend_row(tres, i, attname_pos); tuple = QR_AddNew(res); - set_tuplefield_null(&tuple[0]); + set_tuplefield_string(&tuple[0], CurrCat(conn)); set_nullfield_string(&tuple[1], schema_name); set_tuplefield_string(&tuple[2], procname); set_tuplefield_string(&tuple[3], attname); @@ -5038,7 +5067,7 @@ PGAPI_TablePrivileges( int tablecount, usercount, i, j, k; BOOL grpauth, sys, su; char (*useracl)[ACLMAX] = NULL, *acl, *user, *delim, *auth; - char *reln, *owner, *priv, *schnm = NULL; + const char *reln, *owner, *priv, *schnm = NULL; RETCODE result, ret = SQL_SUCCESS; const char *like_or_eq; const char *szSchemaName; @@ -5258,7 +5287,7 @@ mylog("guid=%s\n", uid); continue; } tuple = QR_AddNew(res); - set_tuplefield_string(&tuple[0], ""); + set_tuplefield_string(&tuple[0], CurrCat(conn)); if (conn->schema_support) set_tuplefield_string(&tuple[1], GET_SCHEMA_NAME(schnm)); else @@ -5288,7 +5317,7 @@ mylog("guid=%s\n", uid); priv = "REFERENCES"; break; default: - priv = ""; + priv = NULL_STRING; } set_tuplefield_string(&tuple[5], priv); /* The owner and the super user are grantable */ diff --git a/info30.c b/info30.c index 721758e..7cc0ba0 100644 --- a/info30.c +++ b/info30.c @@ -19,7 +19,7 @@ PGAPI_GetInfo30(HDBC hdbc, SQLUSMALLINT fInfoType, PTR rgbInfoValue, CSTR func = "PGAPI_GetInfo30"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci = &(conn->connInfo); - char *p = NULL; + const char *p = NULL; int len = 0, value = 0; RETCODE result; @@ -159,10 +159,10 @@ PGAPI_GetInfo30(HDBC hdbc, SQLUSMALLINT fInfoType, PTR rgbInfoValue, break; case SQL_CATALOG_NAME: len = 0; - if (PG_VERSION_LE(conn, 7.2)) - p = "N"; + if (CurrCat(conn)) + p = "Y"; else - p = "N"; /* hopefully */ + p = "N"; break; case SQL_COLLATION_SEQ: len = 0; diff --git a/inouealc.c b/inouealc.c index ee43b89..5a9c087 100644 --- a/inouealc.c +++ b/inouealc.c @@ -1,5 +1,7 @@ +#undef _MEMORY_DEBUG_ #include "psqlodbc.h" +/* #undef malloc #undef calloc #undef realloc @@ -7,7 +9,10 @@ #undef free #undef memcpy #undef strcpy +#undef strncpy +#undef strncpy_null #undef memset +*/ #include "misc.h" #include #include @@ -191,6 +196,30 @@ char *debug_strcpy(char *out, const char *in) out_check(out, strlen(in) + 1, "debug_strcpy"); return strcpy(out, in); } +char *debug_strncpy(char *out, const char *in, size_t len) +{ + CSTR func = "debug_strncpy"; + + if (!out || !in) + { + mylog("%s:%s null pointer out=%x,in=%x\n", ALCERR, func, out, in); + return NULL; + } + out_check(out, len, func); + return strncpy(out, in, len); +} +char *debug_strncpy_null(char *out, const char *in, size_t len) +{ + CSTR func = "debug_strncpy_null"; + + if (!out || !in) + { + mylog("%s:%s null pointer out=%x,in=%x\n", ALCERR, func, out, in); + return NULL; + } + out_check(out, len, func); + return strncpy_null(out, in, len); +} void *debug_memcpy(void *out, const void *in, size_t len) { diff --git a/msdtc_enlist.cpp b/msdtc_enlist.cpp index 19656ed..b88c642 100755 --- a/msdtc_enlist.cpp +++ b/msdtc_enlist.cpp @@ -32,11 +32,11 @@ #include "pgapifunc.h" -static CRITICAL_SECTION life_cs; -static CRITICAL_SECTION map_cs; static class INIT_CRIT { public: + CRITICAL_SECTION life_cs; + CRITICAL_SECTION map_cs; INIT_CRIT() { InitializeCriticalSection(&life_cs); InitializeCriticalSection(&map_cs); @@ -46,10 +46,10 @@ public: DeleteCriticalSection(&map_cs); } } init_crit; -#define LIFELOCK_ACQUIRE EnterCriticalSection(&life_cs) -#define LIFELOCK_RELEASE LeaveCriticalSection(&life_cs) -#define MLOCK_ACQUIRE EnterCriticalSection(&map_cs) -#define MLOCK_RELEASE LeaveCriticalSection(&map_cs) +#define LIFELOCK_ACQUIRE EnterCriticalSection(&init_crit.life_cs) +#define LIFELOCK_RELEASE LeaveCriticalSection(&init_crit.life_cs) +#define MLOCK_ACQUIRE EnterCriticalSection(&init_crit.map_cs) +#define MLOCK_RELEASE LeaveCriticalSection(&init_crit.map_cs) static const char *XidToText(const XID &xid, char *rtext) @@ -180,7 +180,6 @@ public: HRESULT STDMETHODCALLTYPE TMDown(void); IAsyncPG(); - ~IAsyncPG(); void SetHelper(IDtcToXaHelperSinglePipe *pHelper, DWORD dwRMCookie) {helper = pHelper; RMCookie = dwRMCookie;} HRESULT RequestExec(DWORD type, HRESULT res); @@ -188,6 +187,7 @@ public: void SetConnection(ConnectionClass *sconn) {SLOCK_ACQUIRE(); conn = sconn; SLOCK_RELEASE();} void SetXid(const XID *ixid) {SLOCK_ACQUIRE(); xid = *ixid; SLOCK_RELEASE();} private: + ~IAsyncPG(); #ifdef _LOCK_DEBUG_ void SLOCK_ACQUIRE() {forcelog("SLOCK_ACQUIRE %d\n", spin_cnt); EnterCriticalSection(&as_spin); spin_cnt++;} void SLOCK_RELEASE() {forcelog("SLOCK_RELEASE=%d\n", spin_cnt); LeaveCriticalSection(&as_spin); spin_cnt--;} @@ -250,6 +250,11 @@ IAsyncPG::IAsyncPG(void) : helper(NULL), RMCookie(0), enlist(NULL), conn(NULL), #endif /* _LOCK_DEBUG_ */ } +// +// invoked from *delete*. +// When entered ELOCK -> LIFELOCK -> SLOCK are acquired +// and they are released. +// IAsyncPG::~IAsyncPG(void) { ConnectionClass *fconn = NULL; @@ -286,6 +291,9 @@ forcelog("%x QueryInterface called\n", this); *ppvObject = NULL; return E_NOINTERFACE; } +// +// acquire/releases SLOCK. +// ULONG STDMETHODCALLTYPE IAsyncPG::AddRef(void) { mylog("%x->AddRef called\n", this); @@ -294,6 +302,9 @@ ULONG STDMETHODCALLTYPE IAsyncPG::AddRef(void) SLOCK_RELEASE(); return refcnt; } +// +// acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK. +// ULONG STDMETHODCALLTYPE IAsyncPG::Release(void) { mylog("%x->Release called refcnt=%d\n", this, refcnt); @@ -322,6 +333,9 @@ ULONG STDMETHODCALLTYPE IAsyncPG::Release(void) return refcnt; } +// +// Acquire/release [MLOCK -> ] SLOCK. +// void IAsyncPG::Wait_pThread(bool slock_hold) { mylog("Wait_pThread %d in\n", slock_hold); @@ -334,19 +348,6 @@ void IAsyncPG::Wait_pThread(bool slock_hold) { wThread = eThread[wait_idx]; SLOCK_RELEASE(); - /* - if (WAIT_OBJECT_0 == WaitForSingleObject(wThread, 2000)) - { - SLOCK_ACQUIRE(); - if (eThread[wait_idx]) - { - CloseHandle(wThread); - eThread[wait_idx] = NULL; - } - } - else - SLOCK_ACQUIRE(); - */ th_found = AsyncThreads::WaitThread(this, wait_idx, 2000); SLOCK_ACQUIRE(); if (th_found) @@ -357,6 +358,9 @@ void IAsyncPG::Wait_pThread(bool slock_hold) mylog("Wait_pThread out\n"); } +// +// Acquire/releases [MLOCK -> ] SLOCK. +// void IAsyncPG::Wait_cThread(bool slock_hold, bool once) { HANDLE wThread; @@ -374,19 +378,6 @@ void IAsyncPG::Wait_cThread(bool slock_hold, bool once) { wThread = eThread[wait_idx]; SLOCK_RELEASE(); - /* - if (WAIT_OBJECT_0 == WaitForSingleObject(wThread, 2000)) - { - SLOCK_ACQUIRE(); - if (cThread) - { - CloseHandle(wThread); - cThread = NULL; - } - } - else - SLOCK_ACQUIRE(); - */ th_found = AsyncThreads::WaitThread(this, wait_idx, 2000); SLOCK_ACQUIRE(); if (once || th_found) @@ -405,6 +396,10 @@ struct RequestPara { HRESULT res; } RequestPara; +// +// Acquire/releases LIFELOCK -> SLOCK. +// may acquire/release ELOCK. +// void IAsyncPG::SetDone(HRESULT res) { LIFELOCK_ACQUIRE; @@ -435,9 +430,11 @@ void IAsyncPG::SetDone(HRESULT res) SLOCK_RELEASE(); LIFELOCK_RELEASE; } - // Release(); /* unneccesary */ } +// +// Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK. +// ConnectionClass *IAsyncPG::generateXAConn(bool spinAcquired) { if (!spinAcquired) @@ -471,12 +468,16 @@ ConnectionClass *IAsyncPG::generateXAConn(bool spinAcquired) } // -// [in] +// [when entered] // ELOCK is acquired. // -// [out] +// Acquire/releases SLOCK. +// Try to acquire CONNLOCK also. +// +// [on exit] +// ELOCK is kept acquired. // If the return connection != NULL -// the lock for the connection is acquired. +// the CONNLOCK for the connection is acquired. // ConnectionClass *IAsyncPG::getLockedXAConn() { @@ -502,6 +503,9 @@ ConnectionClass *IAsyncPG::getLockedXAConn() return xaconn; } +// +// Acquire/release ELOCK [ -> MLOCK] -> SLOCK. +// HRESULT IAsyncPG::RequestExec(DWORD type, HRESULT res) { HRESULT ret; @@ -515,7 +519,6 @@ HRESULT IAsyncPG::RequestExec(DWORD type, HRESULT res) #ifdef _SLEEP_FOR_TEST_ /*Sleep(2000);*/ #endif /* _SLEEP_FOR_TEST_ */ - /* AddRef(); unnecessary */ ELOCK_ACQUIRE(); switch (type) { @@ -584,11 +587,14 @@ HRESULT IAsyncPG::RequestExec(DWORD type, HRESULT res) enlist->Release(); } ELOCK_RELEASE(); - /* Release(); unnecessary */ mylog("%x->Done ret=%d\n", this, ret); return ret; } +// +// Acquire/releses [MLOCK -> ] SLOCK +// or [ELOCK -> LIFELOCK -> ] SLOCK. +// HRESULT IAsyncPG::ReleaseConnection(void) { mylog("%x->ReleaseConnection\n", this); @@ -625,6 +631,9 @@ HRESULT IAsyncPG::ReleaseConnection(void) return SQL_SUCCESS; } +// +// Acquire/release [ELOCK -> ] [MLOCK -> ] SLOCK. +// EXTERN_C static unsigned WINAPI DtcRequestExec(LPVOID para); HRESULT STDMETHODCALLTYPE IAsyncPG::PrepareRequest(BOOL fRetaining, DWORD grfRM, BOOL fWantMoniker, BOOL fSinglePhase) @@ -671,6 +680,9 @@ HRESULT STDMETHODCALLTYPE IAsyncPG::PrepareRequest(BOOL fRetaining, DWORD grfRM, Release(); return ret; } +// +// Acquire/release [ELOCK -> ] [MLOCK -> ] SLOCK. +// HRESULT STDMETHODCALLTYPE IAsyncPG::CommitRequest(DWORD grfRM, XACTUOW * pNewUOW) { HRESULT res = S_OK, ret = S_OK; @@ -712,6 +724,9 @@ HRESULT STDMETHODCALLTYPE IAsyncPG::CommitRequest(DWORD grfRM, XACTUOW * pNewUOW Release(); return ret; } +// +// Acquire/release [ELOCK -> ] [MLOCK -> ] SLOCK. +// HRESULT STDMETHODCALLTYPE IAsyncPG::AbortRequest(BOID * pboidReason, BOOL fRetaining, XACTUOW * pNewUOW) { @@ -752,9 +767,8 @@ forcelog("%x TMDown called\n", this); } // +// Acquire/releases MLOCK -> SLOCK. // -// - std::map AsyncThreads::th_list; void AsyncThreads::insert(HANDLE th, IAsyncPG *obj, DWORD type) { @@ -767,6 +781,9 @@ void AsyncThreads::insert(HANDLE th, IAsyncPG *obj, DWORD type) MLOCK_RELEASE; } +// +// Acquire/releases MLOCK -> SLOCK. +// bool AsyncThreads::WaitThread(IAsyncPG *obj, DWORD type, DWORD millisecond) { HANDLE th = NULL; @@ -938,7 +955,7 @@ mylog("dllname=%s dsn=%s\n", xalibname, conn->connInfo.dsn); res = 0; errset = false; ConnInfo *ci = &(conn->connInfo); char dtcname[1024]; - snprintf(dtcname, sizeof(dtcname), "DRIVER={%s};SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s;" ABBR_SSLMODE "=%s;" ABBR_DEBUG "=%d", + snprintf(dtcname, sizeof(dtcname), "DRIVER={%s};SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s;" ABBR_SSLMODE "=%s", ci->drivername, ci->server, ci->port, ci->database, ci->username, ci->password, ci->sslmode, ci->drivers.debug); do { res = pHelper->XARMCreate(dtcname, (char *) GetXaLibName(), &dwRMCookie); diff --git a/odbcapi.c b/odbcapi.c index 3745424..ed42cb5 100644 --- a/odbcapi.c +++ b/odbcapi.c @@ -455,7 +455,7 @@ SQLGetConnectOption(HDBC ConnectionHandle, mylog("[SQLGetConnectOption]"); ENTER_CONN_CS(conn); CC_clear_error(conn); - ret = PGAPI_GetConnectOption(ConnectionHandle, Option, Value); + ret = PGAPI_GetConnectOption(ConnectionHandle, Option, Value, NULL, 64); LEAVE_CONN_CS(conn); return ret; } @@ -571,7 +571,7 @@ SQLGetStmtOption(HSTMT StatementHandle, ENTER_STMT_CS(stmt); SC_clear_error(stmt); StartRollbackState(stmt); - ret = PGAPI_GetStmtOption(StatementHandle, Option, Value); + ret = PGAPI_GetStmtOption(StatementHandle, Option, Value, NULL, 64); ret = DiscardRollbackState(stmt, ret, FALSE); LEAVE_STMT_CS(stmt); return ret; diff --git a/odbcapi25w.c b/odbcapi25w.c index 0228e0c..89500c0 100644 --- a/odbcapi25w.c +++ b/odbcapi25w.c @@ -54,7 +54,7 @@ RETCODE SQL_API SQLGetConnectOptionW(HDBC ConnectionHandle, { mylog("[SQLGetConnectOptionW]"); CC_set_in_unicode_driver((ConnectionClass *) ConnectionHandle); - return PGAPI_GetConnectOption(ConnectionHandle, Option, Value); + return PGAPI_GetConnectOption(ConnectionHandle, Option, Value, NULL, 64); } RETCODE SQL_API SQLSetConnectOptionW(HDBC ConnectionHandle, diff --git a/odbcapi30.c b/odbcapi30.c index 3e148ca..8616044 100644 --- a/odbcapi30.c +++ b/odbcapi30.c @@ -582,10 +582,10 @@ inolog("lie=%d\n", ci->drivers.lie); SQL_FUNC_ESET(pfExists, SQL_API_SQLTABLES); /* 54 */ if (ci->drivers.lie) SQL_FUNC_ESET(pfExists, SQL_API_SQLBROWSECONNECT); /* 55 */ - /* if (PG_VERSION_GE(conn, 7.4) || ci->drivers.lie) - SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); */ /* 56 */ + if (ci->drivers.lie) + SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLDATASOURCES); /* 57 */ - if ((PROTOCOL_74(ci) && ci->use_server_side_prepare) || ci->drivers.lie) + if (SUPPORT_DESCRIBE_PARAM(ci) || ci->drivers.lie) SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLEXTENDEDFETCH); /* 59 deprecated ? */ diff --git a/odbcapiw.c b/odbcapiw.c index 4dfa119..f14fa97 100644 --- a/odbcapiw.c +++ b/odbcapiw.c @@ -223,11 +223,16 @@ RETCODE SQL_API SQLDescribeColW(HSTMT StatementHandle, RETCODE ret; StatementClass *stmt = (StatementClass *) StatementHandle; SQLSMALLINT buflen, nmlen; - char *clName; + char *clName = NULL; mylog("[%s]", func); - buflen = BufferLength * 3 + 1; - clName = malloc(buflen); + buflen = 0; + if (BufferLength > 0) + buflen = BufferLength * 3; + else if (NameLength) + buflen = 32; + if (buflen > 0) + clName = malloc(buflen); ENTER_STMT_CS(stmt); SC_clear_error(stmt); StartRollbackState(stmt); @@ -245,7 +250,7 @@ RETCODE SQL_API SQLDescribeColW(HSTMT StatementHandle, if (nmlen < buflen) nmcount = utf8_to_ucs2(clName, nmlen, ColumnName, BufferLength); - if (SQL_SUCCESS == ret && nmcount > (UInt4) BufferLength) + if (SQL_SUCCESS == ret && BufferLength > 0 && nmcount > (UInt4) BufferLength) { ret = SQL_SUCCESS_WITH_INFO; SC_set_error(stmt, STMT_TRUNCATED, "Column name too large", func); @@ -255,7 +260,8 @@ RETCODE SQL_API SQLDescribeColW(HSTMT StatementHandle, } ret = DiscardStatementSvp(stmt, ret, FALSE); LEAVE_STMT_CS(stmt); - free(clName); + if (clName) + free(clName); return ret; } @@ -298,7 +304,10 @@ RETCODE SQL_API SQLGetCursorNameW(HSTMT StatementHandle, SQLSMALLINT clen, buflen; mylog("[%s]", func); - buflen = BufferLength * 3 + 1; + if (BufferLength > 0) + buflen = BufferLength * 3; + else + buflen = 32; crName = malloc(buflen); ENTER_STMT_CS(stmt); SC_clear_error(stmt); diff --git a/options.c b/options.c index ace6011..cdc5b13 100644 --- a/options.c +++ b/options.c @@ -495,11 +495,16 @@ RETCODE SQL_API PGAPI_GetConnectOption( HDBC hdbc, SQLUSMALLINT fOption, - PTR pvParam) + PTR pvParam, + SQLINTEGER *StringLength, + SQLINTEGER BufferLength) { CSTR func = "PGAPI_GetConnectOption"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci = &(conn->connInfo); + const char *p = NULL; + SQLINTEGER len = sizeof(SQLINTEGER); + SQLRETURN result = SQL_SUCCESS; mylog("%s: entering...\n", func); @@ -521,9 +526,8 @@ PGAPI_GetConnectOption( break; case SQL_CURRENT_QUALIFIER: /* don't use qualifiers */ - if (pvParam) - ((char *) pvParam)[0] = ((char *) pvParam)[1] = '\0'; - + len = 0; + p = CurrCatString(conn); break; case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */ @@ -580,7 +584,33 @@ PGAPI_GetConnectOption( } } - return SQL_SUCCESS; + if (NULL != p && 0 == len) + { + /* char/binary data */ + len = strlen(p); + + if (pvParam) + { +#ifdef UNICODE_SUPPORT + if (CC_is_in_unicode_driver(conn)) + { + len = utf8_to_ucs2(p, len, (SQLWCHAR *) pvParam , BufferLength / WCLEN); + len *= WCLEN; + } + else +#endif /* UNICODE_SUPPORT */ + strncpy_null((char *) pvParam, p, (size_t) BufferLength); + + if (len >= BufferLength) + { + result = SQL_SUCCESS_WITH_INFO; + CC_set_error(conn, CONN_TRUNCATED, "The buffer was too small for the pvParam.", func); + } + } + } + if (StringLength) + *StringLength = len; + return result; } @@ -619,13 +649,16 @@ RETCODE SQL_API PGAPI_GetStmtOption( HSTMT hstmt, SQLUSMALLINT fOption, - PTR pvParam) + PTR pvParam, + SQLINTEGER *StringLength, + SQLINTEGER BufferLength) { CSTR func = "PGAPI_GetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); int ridx; + SQLINTEGER len = sizeof(SQLINTEGER); mylog("%s: entering...\n", func); @@ -748,6 +781,8 @@ PGAPI_GetStmtOption( return SQL_ERROR; } } + if (StringLength) + *StringLength = len; return SQL_SUCCESS; } diff --git a/parse.c b/parse.c index 30c856d..b66d4ff 100644 --- a/parse.c +++ b/parse.c @@ -39,7 +39,7 @@ #define FLD_INCR 32 #define TAB_INCR 8 -#define COL_INCR 16 +#define COLI_INCR 16 static char *getNextToken(int ccsc, char escape_in_literal, char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric); static void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k); @@ -259,12 +259,13 @@ getNextToken( return &s[i]; } -void +static void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k) { char *str; inolog("getColInfo non-manual result\n"); + fi->dquote = TRUE; STR_TO_NAME(fi->column_name, QR_get_value_backend_row(col_info->result, k, COLUMNS_COLUMN_NAME)); fi->type = atoi(QR_get_value_backend_row(col_info->result, k, COLUMNS_FIELD_TYPE)); @@ -280,19 +281,31 @@ inolog("getColInfo non-manual result\n"); } -char +static char searchColInfo(COL_INFO *col_info, FIELD_INFO *fi) { int k, - cmp; + cmp, attnum; char *col; inolog("searchColInfo %d\n", QR_get_num_cached_tuples(col_info->result)); + if (fi->attnum < 0) + return FALSE; for (k = 0; k < QR_get_num_cached_tuples(col_info->result); k++) { + attnum = atoi(QR_get_value_backend_row(col_info->result, k, COLUMNS_PHYSICAL_NUMBER)); col = QR_get_value_backend_row(col_info->result, k, COLUMNS_COLUMN_NAME); inolog("searchColInfo %d col=%s\n", k, col); - if (NAME_IS_VALID(fi->column_name)) + if (0 != attnum) + { + if (fi->attnum == attnum) + { + getColInfo(col_info, fi, k); + mylog("PARSE: searchColInfo by attnum\n"); + return TRUE; + } + } + else if (NAME_IS_VALID(fi->column_name)) { if (fi->dquote) cmp = strcmp(col, GET_NAME(fi->column_name)); @@ -403,6 +416,433 @@ static BOOL CheckHasOids(StatementClass * stmt) return TRUE; } +static BOOL increaseNtab(StatementClass *stmt, const char *func) +{ + TABLE_INFO **ti = stmt->ti, *wti; + + if (!(stmt->ntab % TAB_INCR)) + { + ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *)); + if (!ti) + { + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for TABLE_INFO.", func); + return FALSE; + } + stmt->ti = ti; + } + wti = ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO)); + if (wti == NULL) + { + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for TABLE_INFO(2).", func); + return FALSE; + } + + TI_Constructor(wti, SC_get_conn(stmt)); + stmt->ntab++; + return TRUE; +} + +static void xxxxx(FIELD_INFO *fi, QResultClass *res, int i) +{ + STR_TO_NAME(fi->column_alias, QR_get_fieldname(res, i)); + fi->type = QR_get_field_type(res, i); + if (fi->attnum < 0) + { + fi->nullable = FALSE; + fi->updatable = FALSE; + } + else if (fi->attnum > 0) + fi->nullable = TRUE; /* probably ? */ + + if (NAME_IS_NULL(fi->column_name)) + { + switch (fi->attnum) + { + case CTID_ATTNUM: + STR_TO_NAME(fi->column_name, "ctid"); + break; + case OID_ATTNUM: + STR_TO_NAME(fi->column_name, OID_NAME); + break; + case XMIN_ATTNUM: + STR_TO_NAME(fi->column_name, "xmin"); + break; + } + } +} +/* + * SQLColAttribute tries to set the FIELD_INFO (using protocol 3). + */ +static BOOL ColAttSet(StatementClass *stmt, TABLE_INFO *rti) +{ + QResultClass *res = SC_get_Curres(stmt); + IRDFields *irdflds = SC_get_IRDF(stmt); + COL_INFO *col_info = NULL; + FIELD_INFO **fi, *wfi; + Oid reloid = 0; + Int2 attid; + int i, num_fields; + BOOL fi_reuse, updatable; + + if (rti) + { + if (reloid = rti->table_oid, 0 == reloid) + return FALSE; + if (0 != (rti->flags & TI_COLATTRIBUTE)) + return TRUE; + col_info = rti->col_info; + } + if (!QR_command_maybe_successful(res)) + return FALSE; + if (num_fields = QR_NumPublicResultCols(res), num_fields <= 0) + return FALSE; + fi = irdflds->fi; + if (num_fields > (int) irdflds->allocated) + { + fi = (FIELD_INFO **) realloc(fi, num_fields * sizeof(FIELD_INFO *)); + if (!fi) + { + irdflds->fi = NULL; + irdflds->allocated = irdflds->nfields = 0; + return FALSE; + } + irdflds->fi = fi; + irdflds->allocated = num_fields; + for (i = irdflds->nfields; i < num_fields; i++) + fi[i] = NULL; + irdflds->nfields = num_fields; + } + updatable = rti ? TI_is_updatable(rti) : FALSE; + if (updatable) + { + if (1 != stmt->ntab) + updatable = FALSE; + else + { + Oid greloid; + + for (i = 0; i < num_fields; i++) + { + greloid = QR_get_relid(res, i); + if (0 != greloid && reloid != greloid) + { + updatable = FALSE; + break; + } + } + } + } + for (i = 0; i < num_fields; i++) + { + if (reloid == (Oid) QR_get_relid(res, i)) + { + if (wfi = fi[i], NULL == wfi) + { + wfi = (FIELD_INFO *) malloc(sizeof(FIELD_INFO)); + fi_reuse = FALSE; + fi[i] = wfi; + } + else if (FI_is_applicable(wfi)) + continue; + else + fi_reuse = TRUE; + FI_Constructor(wfi, fi_reuse); + attid = (Int2) QR_get_attid(res, i); + wfi->attnum = attid; + if (searchColInfo(col_info, wfi)) + wfi->updatable = updatable; + else + { + xxxxx(wfi, res, i); + } + wfi->ti = rti; + wfi->flag |= FIELD_COL_ATTRIBUTE; + } + } + if (rti) + rti->flags |= TI_COLATTRIBUTE; + return TRUE; +} + +BOOL getCOLIfromTI(const char *func, StatementClass *stmt, const Oid reloid, TABLE_INFO **pti) +{ + BOOL colatt = FALSE, found = FALSE; + ConnectionClass *conn = SC_get_conn(stmt); + Oid greloid = reloid; + int colidx; + char token[256]; + TABLE_INFO *wti = *pti; + HSTMT hcol_stmt = NULL; + QResultClass *res; + +inolog("getCOLIfromTI reloid=%u ti=%x\n", reloid, wti); + if (!wti) /* SQLColAttribute case */ + { + int i; + if (0 == greloid) + return FALSE; + colatt = TRUE; + for (i = 0; i < stmt->ntab; i++) + { + if (stmt->ti[i]->table_oid == greloid) + { + wti = stmt->ti[i]; + break; + } + } + if (!wti) + { +inolog("before increaseNtab\n"); + if (!increaseNtab(stmt, func)) + return FALSE; + wti = stmt->ti[stmt->ntab - 1]; + wti->table_oid = greloid; + } + *pti = wti; + } +inolog("fi=%x greloid=%d col_info=%x\n", wti, greloid, wti->col_info); + if (0 == greloid) + greloid = wti->table_oid; + if (NULL != wti->col_info) + { + found = TRUE; + goto cleanup; + } + if (greloid != 0) + { + for (colidx = 0; colidx < conn->ntables; colidx++) + { + if (conn->col_info[colidx]->table_oid == greloid) + { + mylog("FOUND col_info table=%ul\n", greloid); + found = TRUE; + break; + } + } + } + else + { + if (conn->schema_support) + { + if (NAME_IS_NULL(wti->schema_name)) + { + const char *curschema = CC_get_current_schema(conn); + /* + * Though current_schema() doesn't have + * much sense in PostgreSQL, we first + * check the current_schema() when no + * explicit schema name was specified. + */ + for (colidx = 0; colidx < conn->ntables; colidx++) + { + if (!NAMEICMP(conn->col_info[colidx]->table_name, wti->table_name) && + !stricmp(SAFE_NAME(conn->col_info[colidx]->schema_name), curschema)) + { + mylog("FOUND col_info table='%s' current schema='%s'\n", PRINT_NAME(wti->table_name), curschema); + found = TRUE; + STR_TO_NAME(wti->schema_name, curschema); + break; + } + } + if (!found) + { + QResultClass *res; + BOOL tblFound = FALSE; + + /* + * We also have to check as follows. + */ + sprintf(token, "select nspname from pg_namespace n, pg_class c" + " where c.relnamespace=n.oid and c.oid='\"%s\"'::regclass", SAFE_NAME(wti->table_name)); + res = CC_send_query(conn, token, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + if (QR_command_maybe_successful(res)) + { + if (QR_get_num_total_tuples(res) == 1) + { + tblFound = TRUE; + STR_TO_NAME(wti->schema_name, QR_get_value_backend_row(res, 0, 0)); + } + } + QR_Destructor(res); + if (!tblFound) + { + SC_set_parse_status(stmt, STMT_PARSE_FATAL); + SC_set_error(stmt, STMT_EXEC_ERROR, "Table not found", func); + stmt->updatable = FALSE; + return FALSE; + } + } + } + if (!found && NAME_IS_VALID(wti->schema_name)) + { + for (colidx = 0; colidx < conn->ntables; colidx++) + { + if (!NAMEICMP(conn->col_info[colidx]->table_name, wti->table_name) && + !NAMEICMP(conn->col_info[colidx]->schema_name, wti->schema_name)) + { + mylog("FOUND col_info table='%s' schema='%s'\n", PRINT_NAME(wti->table_name), PRINT_NAME(wti->schema_name)); + found = TRUE; + break; + } + } + } + } + else + { + for (colidx = 0; colidx < conn->ntables; colidx++) + { + if (!NAMEICMP(conn->col_info[colidx]->table_name, wti->table_name)) + { + mylog("FOUND col_info table='%s'\n", wti->table_name); + found = TRUE; + break; + } + } + } + } + if (found) + { + wti->col_info = conn->col_info[colidx]; + goto cleanup; + } + else + { + RETCODE result; + StatementClass *col_stmt; + + mylog("PARSE: Getting PG_Columns for table='%s'\n", wti->table_name); + + result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for columns.", func); + goto cleanup; + } + + col_stmt = (StatementClass *) hcol_stmt; + col_stmt->internal = TRUE; + + if (greloid) + result = PGAPI_Columns(hcol_stmt, NULL, 0, + NULL, 0, NULL, 0, NULL, 0, + PODBC_SEARCH_BY_IDS, greloid, 0); + else + result = PGAPI_Columns(hcol_stmt, NULL, 0, SAFE_NAME(wti->schema_name), + SQL_NTS, SAFE_NAME(wti->table_name), SQL_NTS, NULL, 0, PODBC_NOT_SEARCH_PATTERN, 0, 0); + + mylog(" Past PG_Columns\n"); + res = SC_get_Curres(col_stmt); + if ((SQL_SUCCESS == result || SQL_SUCCESS_WITH_INFO == result) + && res && QR_get_num_cached_tuples(res) > 0) + { + COL_INFO *coli; + + mylog(" Success\n"); + if (conn->ntables >= conn->coli_allocated) + { + Int2 new_alloc; + + new_alloc = conn->coli_allocated * 2; + if (new_alloc <= conn->ntables) + new_alloc = COLI_INCR; + mylog("PARSE: Allocating col_info at ntables=%d\n", conn->ntables); + + conn->col_info = (COL_INFO **) realloc(conn->col_info, new_alloc * sizeof(COL_INFO *)); + if (!conn->col_info) + { + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for col_info.", func); + goto cleanup; + } + conn->coli_allocated = new_alloc; + } + + mylog("PARSE: malloc at conn->col_info[%d]\n", conn->ntables); + coli = conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO)); + if (!coli) + { + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for col_info(2).", func); + goto cleanup; + } + col_info_initialize(coli); + + coli->result = res; + if (res && QR_get_num_cached_tuples(res) > 0) + { + if (!greloid) + greloid = strtoul(QR_get_value_backend_row(res, 0, COLUMNS_TABLE_OID), NULL, 10); + if (!wti->table_oid) + wti->table_oid = greloid; + if (NAME_IS_NULL(wti->schema_name)) + STR_TO_NAME(wti->schema_name, + QR_get_value_backend_row(res, 0, COLUMNS_SCHEMA_NAME)); + if (NAME_IS_NULL(wti->table_name)) + STR_TO_NAME(wti->table_name, + QR_get_value_backend_row(res, 0, COLUMNS_TABLE_NAME)); + } +inolog("#2 %x->table_name=%s(%u)\n", wti, PRINT_NAME(wti->table_name), wti->table_oid); + /* + * Store the table name and the SQLColumns result + * structure + */ + if (NAME_IS_VALID(wti->schema_name)) + { + NAME_TO_NAME(coli->schema_name, wti->schema_name); + } + else + NULL_THE_NAME(coli->schema_name); + NAME_TO_NAME(coli->table_name, wti->table_name); + coli->table_oid = wti->table_oid; + + /* + * The connection will now free the result structures, so + * make sure that the statement doesn't free it + */ + SC_init_Result(col_stmt); + + conn->ntables++; + +if (res && QR_get_num_cached_tuples(res) > 0) +inolog("oid item == %s\n", QR_get_value_backend_row(res, 0, 3)); + + mylog("Created col_info table='%s', ntables=%d\n", PRINT_NAME(wti->table_name), conn->ntables); + /* Associate a table from the statement with a SQLColumn info */ + found = TRUE; + wti->col_info = coli; + } + } +cleanup: + if (hcol_stmt) + PGAPI_FreeStmt(hcol_stmt, SQL_DROP); + if (found) + { + QResultClass *res = wti->col_info->result; + + if (res && QR_get_num_cached_tuples(res) > 0) + { + if (!greloid) + greloid = strtoul(QR_get_value_backend_row(res, 0, COLUMNS_TABLE_OID), NULL, 10); + if (!wti->table_oid) + wti->table_oid = greloid; + if (NAME_IS_NULL(wti->schema_name)) + STR_TO_NAME(wti->schema_name, + QR_get_value_backend_row(res, 0, COLUMNS_SCHEMA_NAME)); + if (NAME_IS_NULL(wti->table_name)) + STR_TO_NAME(wti->table_name, + QR_get_value_backend_row(res, 0, COLUMNS_TABLE_NAME)); + } +inolog("#1 %x->table_name=%s(%u)\n", wti, PRINT_NAME(wti->table_name), wti->table_oid); + if (colatt /* SQLColAttribute case */ + && 0 == (wti->flags & TI_COLATTRIBUTE)) + { + ColAttSet(stmt, wti); + } + } + else if (!colatt) + SC_set_parse_status(stmt, STMT_PARSE_FATAL); +inolog("getCOLIfromTI returns %d\n", found); + return found; +} + char parse_statement(StatementClass *stmt, BOOL check_hasoids) { @@ -436,11 +876,8 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) FIELD_INFO **fi, *wfi; TABLE_INFO **ti, *wti; char parse; - ConnectionClass *conn = stmt->hdbc; - HSTMT hcol_stmt; - StatementClass *col_stmt; + ConnectionClass *conn = SC_get_conn(stmt); IRDFields *irdflds = SC_get_IRDF(stmt); - RETCODE result; BOOL updatable = TRUE; mylog("%s: entering...\n", func); @@ -456,12 +893,16 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) fi = irdflds->fi; ti = stmt->ti; - allocated_size = 0; + allocated_size = irdflds->allocated; nfields_old = irdflds->nfields; irdflds->nfields = 0; - if (nfields_old > 0) - allocated_size = ((nfields_old - 1) / FLD_INCR + 1) * FLD_INCR; - stmt->ntab = 0; + if (stmt->ntab > 0) + { + TI_Destructor(stmt->ti, stmt->ntab); + free(stmt->ti); + stmt->ti = NULL; + stmt->ntab = 0; + } stmt->from_pos = -1; stmt->where_pos = -1; @@ -642,10 +1083,12 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) if (!fi) { SC_set_parse_status(stmt, STMT_PARSE_FATAL); + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for FIELD_INFO.", func); return FALSE; } allocated_size = new_size; irdflds->fi = fi; + irdflds->allocated = allocated_size; } wfi = NULL; @@ -658,6 +1101,7 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) if (wfi == NULL) { SC_set_parse_status(stmt, STMT_PARSE_FATAL); + SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for FIELD_INFO(2).", func); return FALSE; } @@ -684,7 +1128,7 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) mylog("got EXPRESSION\n"); wfi->expr = TRUE; in_expr = TRUE; - continue; + /* continue; */ } else { @@ -796,24 +1240,15 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) token[0] == ')') continue; } - if (!(stmt->ntab % TAB_INCR)) - { - ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *)); - if (!ti) - { - SC_set_parse_status(stmt, STMT_PARSE_FATAL); - return FALSE; - } - stmt->ti = ti; - } - wti = ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO)); - if (wti == NULL) + + if (!increaseNtab(stmt, func)) { SC_set_parse_status(stmt, STMT_PARSE_FATAL); return FALSE; } - TI_Constructor(wti, conn); + ti = stmt->ti; + wti = ti[stmt->ntab - 1]; STR_TO_NAME(wti->table_name, token); lower_the_name(GET_NAME(wti->table_name), conn, dquote); mylog("got table = '%s'\n", PRINT_NAME(wti->table_name)); @@ -828,7 +1263,6 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) out_table = FALSE; in_table = TRUE; } - stmt->ntab++; in_dot = FALSE; continue; } @@ -895,7 +1329,7 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) if (wfi->func || wfi->expr || wfi->numeric) { wfi->ti = NULL; - wfi->type = -1; + wfi->type = (Oid) 0; parse = FALSE; continue; } @@ -1007,168 +1441,12 @@ parse_statement(StatementClass *stmt, BOOL check_hasoids) char found = FALSE; wti = ti[i]; - if (conn->schema_support) - { - if (NAME_IS_NULL(wti->schema_name)) - { - const char *curschema = CC_get_current_schema(conn); - /* - * Though current_schema() doesn't have - * much sense in PostgreSQL, we first - * check the current_schema() when no - * explicit schema name was specified. - */ - for (k = 0; k < conn->ntables; k++) - { - if (!NAMEICMP(conn->col_info[k]->table_name, wti->table_name) && - !stricmp(SAFE_NAME(conn->col_info[k]->schema_name), curschema)) - { - mylog("FOUND col_info table='%s' current schema='%s'\n", PRINT_NAME(wti->table_name), curschema); - found = TRUE; - STR_TO_NAME(wti->schema_name, curschema); - break; - } - } - if (!found) - { - QResultClass *res; - BOOL tblFound = FALSE; - - /* - * We also have to check as follows. - */ - sprintf(token, "select nspname from pg_namespace n, pg_class c" - " where c.relnamespace=n.oid and c.oid='\"%s\"'::regclass", SAFE_NAME(wti->table_name)); - res = CC_send_query(conn, token, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); - if (QR_command_maybe_successful(res)) - { - if (QR_get_num_total_tuples(res) == 1) - { - tblFound = TRUE; - STR_TO_NAME(wti->schema_name, QR_get_value_backend_row(res, 0, 0)); - } - } - QR_Destructor(res); - if (!tblFound) - { - SC_set_parse_status(stmt, STMT_PARSE_FATAL); - SC_set_error(stmt, STMT_EXEC_ERROR, "Table not found", func); - stmt->updatable = FALSE; - return FALSE; - } - } - } - if (!found && NAME_IS_VALID(wti->schema_name)) - { - for (k = 0; k < conn->ntables; k++) - { - if (!NAMEICMP(conn->col_info[k]->table_name, wti->table_name) && - !NAMEICMP(conn->col_info[k]->schema_name, wti->schema_name)) - { - mylog("FOUND col_info table='%s' schema='%s'\n", PRINT_NAME(wti->table_name), PRINT_NAME(wti->schema_name)); - found = TRUE; - break; - } - } - } - } - else - { - for (k = 0; k < conn->ntables; k++) - { - if (!NAMEICMP(conn->col_info[k]->table_name, wti->table_name)) - { - mylog("FOUND col_info table='%s'\n", wti->table_name); - found = TRUE; - break; - } - } - } - - if (!found) - { - QResultClass *res; - mylog("PARSE: Getting PG_Columns for table[%d]='%s'\n", i, wti->table_name); - - result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - { - SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for columns.", func); - SC_set_parse_status(stmt, STMT_PARSE_FATAL); - return FALSE; - } - col_stmt = (StatementClass *) hcol_stmt; - col_stmt->internal = TRUE; - - result = PGAPI_Columns(hcol_stmt, "", 0, SAFE_NAME(wti->schema_name), - SQL_NTS, SAFE_NAME(wti->table_name), SQL_NTS, "", 0, PODBC_NOT_SEARCH_PATTERN, 0, 0); - - mylog(" Past PG_Columns\n"); - res = SC_get_Curres(col_stmt); - if (result == SQL_SUCCESS) - { - COL_INFO *coli; - - mylog(" Success\n"); - if (!(conn->ntables % COL_INCR)) - { - mylog("PARSE: Allocating col_info at ntables=%d\n", conn->ntables); - - conn->col_info = (COL_INFO **) realloc(conn->col_info, (conn->ntables + COL_INCR) * sizeof(COL_INFO *)); - if (!conn->col_info) - { - SC_set_parse_status(stmt, STMT_PARSE_FATAL); - return FALSE; - } - } - - mylog("PARSE: malloc at conn->col_info[%d]\n", conn->ntables); - coli = conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO)); - if (!coli) - { - SC_set_parse_status(stmt, STMT_PARSE_FATAL); - return FALSE; - } - col_info_initialize(coli); - - /* - * Store the table name and the SQLColumns result - * structure - */ - if (NAME_IS_VALID(wti->schema_name)) - { - NAME_TO_NAME(coli->schema_name, wti->schema_name); - } - else - NULL_THE_NAME(coli->schema_name); - NAME_TO_NAME(coli->table_name, wti->table_name); - coli->result = res; - - /* - * The connection will now free the result structures, so - * make sure that the statement doesn't free it - */ - SC_init_Result(col_stmt); - - conn->ntables++; - - PGAPI_FreeStmt(hcol_stmt, SQL_DROP); -if (res && QR_get_num_cached_tuples(res) > 0) -inolog("oid item == %s\n", QR_get_value_backend_row(res, 0, 3)); - mylog("Created col_info table='%s', ntables=%d\n", PRINT_NAME(wti->table_name), conn->ntables); - } - else - { - PGAPI_FreeStmt(hcol_stmt, SQL_DROP); - break; - } - } - - /* Associate a table from the statement with a SQLColumn info */ - wti->col_info = conn->col_info[k]; - mylog("associate col_info: i=%d, k=%d\n", i, k); + if (!getCOLIfromTI(func, stmt, 0, &wti)) + break; } + if (STMT_PARSE_FATAL == SC_parsed_status(stmt)) + return FALSE; mylog("Done PG_Columns\n"); @@ -1232,6 +1510,7 @@ inolog("oid item == %s\n", QR_get_value_backend_row(res, 0, 3)); } allocated_size = new_alloc; irdflds->fi = fi; + irdflds->allocated = allocated_size; } /* @@ -1333,17 +1612,13 @@ inolog("oid item == %s\n", QR_get_value_backend_row(res, 0, 3)); if (check_hasoids && updatable) CheckHasOids(stmt); - if (!parse) - SC_set_parse_status(stmt, STMT_PARSE_INCOMPLETE); - else + SC_set_parse_status(stmt, parse ? STMT_PARSE_COMPLETE : STMT_PARSE_INCOMPLETE); + for (i = 0; i < (int) irdflds->nfields; i++) { - SC_set_parse_status(stmt, STMT_PARSE_COMPLETE); - for (i = 0; i < (int) irdflds->nfields; i++) - { - wfi = fi[i]; - wfi->flag &= ~FIELD_PARSING; + wfi = fi[i]; + wfi->flag &= ~FIELD_PARSING; + if (0 != wfi->type) wfi->flag |= FIELD_PARSED_OK; - } } stmt->updatable = updatable; diff --git a/pgapi30.c b/pgapi30.c index 6e03810..775fd53 100644 --- a/pgapi30.c +++ b/pgapi30.c @@ -415,7 +415,7 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle, *((SQLUINTEGER *) Value) = conn->stmtOptions.metadata_id; break; default: - ret = PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value); + ret = PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value, &len, BufferLength); } if (StringLength) *StringLength = len; @@ -1549,8 +1549,7 @@ PGAPI_GetStmtAttr(HSTMT StatementHandle, SC_set_error(stmt, DESC_INVALID_OPTION_IDENTIFIER, "Unsupported statement option (Get)", func); return SQL_ERROR; default: - len = 4; - ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value); + ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value, &len, BufferLength); } if (ret == SQL_SUCCESS && StringLength) *StringLength = len; diff --git a/pgapifunc.h b/pgapifunc.h index db9636e..3a43308 100644 --- a/pgapifunc.h +++ b/pgapifunc.h @@ -93,7 +93,8 @@ RETCODE SQL_API PGAPI_FreeEnv(HENV EnvironmentHandle); RETCODE SQL_API PGAPI_FreeStmt(HSTMT StatementHandle, SQLUSMALLINT Option); RETCODE SQL_API PGAPI_GetConnectOption(HDBC ConnectionHandle, - SQLUSMALLINT Option, PTR Value); + SQLUSMALLINT Option, PTR Value, + SQLINTEGER *StringLength, SQLINTEGER BufferLength); RETCODE SQL_API PGAPI_GetCursorName(HSTMT StatementHandle, SQLCHAR *CursorName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLength); @@ -112,7 +113,8 @@ RETCODE SQL_API PGAPI_GetInfo30(HDBC ConnectionHandle, SQLUSMALLINT InfoType, PTR InfoValue, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength); RETCODE SQL_API PGAPI_GetStmtOption(HSTMT StatementHandle, - SQLUSMALLINT Option, PTR Value); + SQLUSMALLINT Option, PTR Value, + SQLINTEGER *StringLength, SQLINTEGER BufferLength); RETCODE SQL_API PGAPI_GetTypeInfo(HSTMT StatementHandle, SQLSMALLINT DataType); RETCODE SQL_API PGAPI_NumResultCols(HSTMT StatementHandle, diff --git a/pgtypes.c b/pgtypes.c index 9f3aed6..cdb3c66 100644 --- a/pgtypes.c +++ b/pgtypes.c @@ -68,6 +68,7 @@ Int4 pgtypes_defined[] = { PG_TYPE_BOOL, PG_TYPE_BYTEA, PG_TYPE_NUMERIC, + PG_TYPE_XID, PG_TYPE_LO_UNDEFINED, 0 }; */ @@ -435,6 +436,7 @@ pgtype_to_ctype(StatementClass *stmt, Int4 type) return SQL_C_SSHORT; case PG_TYPE_OID: case PG_TYPE_XID: + return SQL_C_ULONG; case PG_TYPE_INT4: return SQL_C_SLONG; case PG_TYPE_FLOAT4: @@ -529,6 +531,8 @@ pgtype_to_name(StatementClass *stmt, Int4 type) return "int2"; case PG_TYPE_OID: return "oid"; + case PG_TYPE_XID: + return "xid"; case PG_TYPE_INT4: inolog("pgtype_to_name int4\n"); return "int4"; @@ -603,8 +607,9 @@ getNumericDecimalDigits(StatementClass *stmt, Int4 type, int col) flds = result->fields; if (flds) { - if (flds->adtsize[col] > 0) - return flds->adtsize[col]; + int fsize = CI_get_fieldsize(flds, col); + if (fsize > 0) + return fsize; } return default_decimal_digits; } @@ -646,8 +651,9 @@ getNumericColumnSize(StatementClass *stmt, Int4 type, int col) flds = result->fields; if (flds) { - if (flds->adtsize[col] > 0) - return 2 * flds->adtsize[col]; + int fsize = CI_get_fieldsize(flds, col); + if (fsize > 0) + return 2 * fsize; } return default_column_size; } @@ -1270,6 +1276,7 @@ pgtype_radix(StatementClass *stmt, Int4 type) switch (type) { case PG_TYPE_INT2: + case PG_TYPE_XID: case PG_TYPE_OID: case PG_TYPE_INT4: case PG_TYPE_INT8: @@ -1375,6 +1382,8 @@ pgtype_searchable(StatementClass *stmt, Int4 type) return SQL_SEARCHABLE; default: + if (stmt && type == SC_get_conn(stmt)->lobj_type) + return SQL_UNSEARCHABLE; return SQL_ALL_EXCEPT_LIKE; } } diff --git a/pgxalib.cpp b/pgxalib.cpp index a39fb47..a1609da 100755 --- a/pgxalib.cpp +++ b/pgxalib.cpp @@ -14,6 +14,7 @@ #include /*#define _SLEEP_FOR_TEST_*/ #include +#include #include #include #include @@ -25,6 +26,130 @@ #include +EXTERN_C static void mylog(const char *fmt,...); + +using namespace std; + +class XAConnection +{ +private: + string connstr; + HDBC xaconn; + vector qvec; + int pos; +public: + XAConnection(LPCTSTR str) : connstr(str), xaconn(NULL), pos(-1) {} + ~XAConnection(); + HDBC ActivateConnection(void); + void SetPos(int spos) {pos = spos;} + HDBC GetConnection(void) const {return xaconn;} + vector &GetResultVec(void) {return qvec;} + int GetPos(void) {return pos;} + const string &GetConnstr(void) {return connstr;} +}; + +static class INIT_CRIT +{ +private: +public: + bool cs_init; + CRITICAL_SECTION map_cs; + CRITICAL_SECTION mylog_cs; + map xatab; + FILE *LOGFP; + HENV env; + + INIT_CRIT() : LOGFP(NULL), env(NULL) + { + InitializeCriticalSection(&map_cs); + InitializeCriticalSection(&mylog_cs); + cs_init = true; + } + ~INIT_CRIT() + { +// mylog("Leaving INIT_CRIT\n"); + if (cs_init) + { + xatab.clear(); + FreeEnv(); + if (LOGFP) + fclose(LOGFP); + DeleteCriticalSection(&mylog_cs); + DeleteCriticalSection(&map_cs); + } + } + void finalize() + { + if (cs_init) + { + xatab.clear(); + FreeEnv(); + if (LOGFP) + fclose(LOGFP); + LOGFP = NULL; + DeleteCriticalSection(&mylog_cs); + DeleteCriticalSection(&map_cs); + cs_init = false; + } + } + void FreeEnv() + { + if (env) + { + SQLFreeHandle(SQL_HANDLE_ENV, env); + env = NULL; + } + } +} init_crit; +#define MLOCK_ACQUIRE EnterCriticalSection(&init_crit.map_cs) +#define MLOCK_RELEASE LeaveCriticalSection(&init_crit.map_cs) + +static int dtclog = 0; + +XAConnection::~XAConnection() +{ + qvec.clear(); + if (xaconn) + SQLFreeHandle(SQL_HANDLE_DBC, xaconn); +} + +HDBC XAConnection::ActivateConnection(void) +{ + RETCODE ret; + + MLOCK_ACQUIRE; + if (!init_crit.env) + { + ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &init_crit.env); + if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret) + return NULL; + } + MLOCK_RELEASE; + if (!xaconn) + { + ret = SQLSetEnvAttr(init_crit.env, SQL_ATTR_ODBC_VERSION, (PTR) SQL_OV_ODBC3, 0); + ret = SQLAllocHandle(SQL_HANDLE_DBC, init_crit.env, &xaconn); + if (SQL_SUCCESS == ret || SQL_SUCCESS_WITH_INFO) + { + string cstr = connstr; + if (dtclog) + { + cstr += ";B2="; + cstr += dtclog; + } + ret = SQLDriverConnect(xaconn, NULL, + (SQLCHAR *) cstr.c_str(), SQL_NTS, NULL, SQL_NULL_DATA, NULL, SQL_DRIVER_COMPLETE); + if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret) + { + mylog("SQLDriverConnect return=%d\n", ret); + SQLFreeHandle(SQL_HANDLE_DBC, xaconn); + xaconn = NULL; + } + } + } + return xaconn; +} + #define _BUILD_DLL_ #ifdef _BUILD_DLL_ @@ -52,17 +177,39 @@ generate_filename(const char *dirname, const char *prefix, char *filename) return; } -static HENV env = NULL; static void FreeEnv() { - if (env) - { - SQLFreeHandle(SQL_HANDLE_ENV, env); - env = NULL; - } + init_crit.FreeEnv(); +} + +#define DTCLOGDIR "c:\\pgdtclog" +#include + +static const char * const DBMSNAME = "PostgreSQL"; +static const char * const KEY_NAME = "MsdtcLog"; +static const char * const ODBCINST_INI = "ODBCINST.INI"; +INT_PTR FAR WINAPI GetMsdtclog() +{ + char temp[16]; + + SQLGetPrivateProfileString(DBMSNAME, KEY_NAME, "", temp, sizeof(temp), ODBCINST_INI); + dtclog = atoi(temp); + return dtclog; +} +INT_PTR FAR WINAPI SetMsdtclog(int dtclog) +{ + char temp[16]; + + sprintf(temp, "%d", dtclog); + SQLWritePrivateProfileString(DBMSNAME, KEY_NAME, temp, ODBCINST_INI); + return dtclog; +} + +static BOOL +output_mylog() +{ + return (0 != dtclog); } -static FILE *LOGFP = NULL; -static CRITICAL_SECTION mylog_cs; static void mylog(const char *fmt,...) @@ -70,27 +217,40 @@ mylog(const char *fmt,...) va_list args; char filebuf[80]; static BOOL init = TRUE; + FILE *logfp = init_crit.LOGFP; - EnterCriticalSection(&mylog_cs); + if (!output_mylog()) + return; + EnterCriticalSection(&init_crit.mylog_cs); va_start(args, fmt); if (init) { - if (!LOGFP) + if (!logfp) { generate_filename(MYLOGDIR, MYLOGFILE, filebuf); - LOGFP = fopen(filebuf, PG_BINARY_A); + logfp = fopen(filebuf, PG_BINARY_A); + } + if (!logfp) + { + generate_filename(DTCLOGDIR, MYLOGFILE, filebuf); + logfp = fopen(filebuf, PG_BINARY_A); +#ifdef WIN32 + if (NULL == logfp) + { + if (0 == _mkdir(DTCLOGDIR)) + logfp = fopen(filebuf, PG_BINARY_A); + } +#endif /* WIN32 */ } - if (!LOGFP) + if (logfp) { - generate_filename("C:\\podbclog", MYLOGFILE, filebuf); - LOGFP = fopen(filebuf, PG_BINARY_A); + setbuf(logfp, NULL); + init_crit.LOGFP = logfp; } - if (LOGFP) - setbuf(LOGFP, NULL); } init = FALSE; - if (LOGFP) + if (logfp) { time_t ntime; char ctim[128]; @@ -98,20 +258,18 @@ mylog(const char *fmt,...) time(&ntime); strcpy(ctim, ctime(&ntime)); ctim[strlen(ctim) - 1] = '\0'; - fprintf(LOGFP, "[%d.%d(%s)]", GetCurrentProcessId(), GetCurrentThreadId(), ctim); - vfprintf(LOGFP, fmt, args); + fprintf(logfp, "[%d.%d(%s)]", GetCurrentProcessId(), GetCurrentThreadId(), ctim); + vfprintf(logfp, fmt, args); } va_end(args); - LeaveCriticalSection(&mylog_cs); + LeaveCriticalSection(&init_crit.mylog_cs); } static int initialize_globals(void) { static int init = 1; if (!init) - return 0; - init = 0; - InitializeCriticalSection(&mylog_cs); + init = 0; return 0; } @@ -119,13 +277,9 @@ static int initialize_globals(void) static void XatabClear(void); static void finalize_globals(void) { - XatabClear(); - FreeEnv(); /* my(q)log is unavailable from here */ mylog("DETACHING PROCESS\n"); - DeleteCriticalSection(&mylog_cs); - fclose(LOGFP); - LOGFP = NULL; + init_crit.finalize(); } HINSTANCE s_hModule; /* Saved module handle. */ @@ -178,86 +332,9 @@ DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) #endif /* _BUILD_DLL_ */ -using namespace std; - -static CRITICAL_SECTION map_cs; -#define MLOCK_ACQUIRE EnterCriticalSection(&map_cs) -#define MLOCK_RELEASE LeaveCriticalSection(&map_cs) - -class XAConnection -{ -private: - string connstr; - HDBC xaconn; - vector qvec; - int pos; -public: - XAConnection(LPCTSTR str) : connstr(str), xaconn(NULL), pos(-1) {} - ~XAConnection(); - HDBC ActivateConnection(void); - void SetPos(int spos) {pos = spos;} - HDBC GetConnection(void) const {return xaconn;} - vector &GetResultVec(void) {return qvec;} - int GetPos(void) {return pos;} - string GetConnstr(void) {return connstr;} -}; - -XAConnection::~XAConnection() -{ - qvec.clear(); - if (xaconn) - SQLFreeHandle(SQL_HANDLE_DBC, xaconn); -} - -HDBC XAConnection::ActivateConnection(void) -{ - RETCODE ret; - - MLOCK_ACQUIRE; - if (!env) - { - ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); - if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret) - return NULL; - } - MLOCK_RELEASE; - if (!xaconn) - { - ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (PTR) SQL_OV_ODBC3, 0); - ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &xaconn); - if (SQL_SUCCESS == ret || SQL_SUCCESS_WITH_INFO) - { - ret = SQLDriverConnect(xaconn, NULL, (SQLCHAR *) connstr.c_str(), SQL_NTS, NULL, SQL_NULL_DATA, NULL, SQL_DRIVER_COMPLETE); - if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret) - { - mylog("SQLDriverConnect return=%d\n", ret); - SQLFreeHandle(SQL_HANDLE_DBC, xaconn); - xaconn = NULL; - } - } - } - return xaconn; -} - -static map xatab; - -static class INIT_CRIT -{ -private: -public: - INIT_CRIT() {InitializeCriticalSection(&map_cs);} - ~INIT_CRIT() - { -mylog("Leaving INIT_CRIT\n"); - FreeEnv(); - xatab.clear(); - DeleteCriticalSection(&map_cs); - } -} init_crit; - static void XatabClear(void) { - xatab.clear(); + init_crit.xatab.clear(); } static const char *XidToText(const XID &xid, char *rtext) @@ -333,8 +410,9 @@ static int TextToXid(XID &xid, const char *rtext) EXTERN_C static int __cdecl xa_open(char *xa_info, int rmid, long flags) { mylog("xa_open %s rmid=%d flags=%ld\n", xa_info, rmid, flags); + GetMsdtclog(); MLOCK_ACQUIRE; - xatab.insert(pair(rmid, XAConnection(xa_info))); + init_crit.xatab.insert(pair(rmid, XAConnection(xa_info))); MLOCK_RELEASE; return S_OK; } @@ -342,15 +420,16 @@ EXTERN_C static int __cdecl xa_close(char *xa_info, int rmid, long flags) { mylog("xa_close rmid=%d flags=%ld\n", rmid, flags); MLOCK_ACQUIRE; - xatab.erase(rmid); - if (xatab.size() == 0) + init_crit.xatab.erase(rmid); + if (init_crit.xatab.size() == 0) FreeEnv(); + GetMsdtclog(); MLOCK_RELEASE; return XA_OK; } // -// Dummy implmentation +// Dummy implementation (not called from MSDTC). // EXTERN_C static int __cdecl xa_start(XID *xid, int rmid, long flags) { @@ -358,11 +437,11 @@ EXTERN_C static int __cdecl xa_start(XID *xid, int rmid, long flags) XidToText(*xid, pgxid); mylog("xa_start %s rmid=%d flags=%ld\n", pgxid, rmid, flags); - xatab.find(rmid)->second.ActivateConnection(); + init_crit.xatab.find(rmid)->second.ActivateConnection(); return XA_OK; } // -// Dummy implementation +// Dummy implementation (not called from MSDTC). // EXTERN_C static int __cdecl xa_end(XID *xid, int rmid, long flags) { @@ -381,8 +460,8 @@ EXTERN_C static int __cdecl xa_rollback(XID *xid, int rmid, long flags) XidToText(*xid, pgxid); mylog("xa_rollback %s rmid=%d flags=%ld\n", pgxid, rmid, flags); map::iterator p; - p = xatab.find(rmid); - if (p != xatab.end()) + p = init_crit.xatab.find(rmid); + if (p != init_crit.xatab.end()) { HDBC conn = p->second.ActivateConnection(); if (conn) @@ -420,8 +499,8 @@ EXTERN_C static int __cdecl xa_rollback(XID *xid, int rmid, long flags) return rmcode; } // -// Dummy implementation -// It's almost impossible to implement this routine properly. +// Dummy implementation (not called from MSDTC). +// ANway it's almost impossible to implement this routine properly. // EXTERN_C static int __cdecl xa_prepare(XID *xid, int rmid, long flags) { @@ -433,8 +512,8 @@ EXTERN_C static int __cdecl xa_prepare(XID *xid, int rmid, long flags) Sleep(2000); #endif /* _SLEEP_FOR_TEST_ */ map::iterator p; - p = xatab.find(rmid); - if (p != xatab.end()) + p = init_crit.xatab.find(rmid); + if (p != init_crit.xatab.end()) { HDBC conn = p->second.GetConnection(); if (conn) @@ -454,8 +533,8 @@ EXTERN_C static int __cdecl xa_commit(XID *xid, int rmid, long flags) Sleep(2000); #endif /* _SLEEP_FOR_TEST_ */ map::iterator p; - p = xatab.find(rmid); - if (p != xatab.end()) + p = init_crit.xatab.find(rmid); + if (p != init_crit.xatab.end()) { HDBC conn = p->second.ActivateConnection(); if (conn) @@ -492,8 +571,8 @@ EXTERN_C static int __cdecl xa_recover(XID *xids, long count, int rmid, long fla mylog("xa_recover rmid=%d count=%d flags=%ld\n", rmid, count, flags); map::iterator p; - p = xatab.find(rmid); - if (p == xatab.end()) + p = init_crit.xatab.find(rmid); + if (p == init_crit.xatab.end()) return rmcode; HDBC conn = p->second.ActivateConnection(); if (!conn) @@ -556,7 +635,7 @@ EXTERN_C static int __cdecl xa_forget(XID *xid, int rmid, long flags) return XA_OK; } // -// I'm not sure if this invoked from MSDTC. +// I'm not sure if this can be invoked from MSDTC. // EXTERN_C static int __cdecl xa_complete(int *handle, int *retval, int rmid, long flags) { @@ -574,6 +653,7 @@ EXTERN_C HRESULT __cdecl GetXaSwitch (XA_SWITCH_FLAGS XaSwitchFlags, { mylog("GetXaSwitch called\n"); + GetMsdtclog(); *ppXaSwitch = &xapsw; return S_OK; } diff --git a/psqlodbc.h b/psqlodbc.h index 34cff7f..bf9ad0e 100644 --- a/psqlodbc.h +++ b/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.100 2006/04/27 14:49:04 hinoue Exp $ + * $Id: psqlodbc.h,v 1.101 2006/06/12 15:21:45 hinoue Exp $ * */ @@ -47,8 +47,8 @@ #if defined(WIN32) || defined(WITH_UNIXODBC) || defined(WITH_IODBC) #include #include -#if defined(WIN32) && (_MSC_VER < 1300) /* In the case of below VC6 */ -#define SQLLEN SQLINTEGER +#if defined(WIN32) && (_MSC_VER < 1300) /* in case of VC6 or under */ +#define SQLLEN SQLINTEGER #define SQLULEN SQLUINTEGER #define SQLSETPOSIROW SQLUSMALLINT #endif @@ -74,7 +74,7 @@ extern "C" { #endif #ifndef WIN32 -#define Int4 long int +#define Int4 int #define UInt4 unsigned int #define Int2 short #define UInt2 unsigned short @@ -109,7 +109,6 @@ typedef double SDOUBLE; #define strdup _strdup #define strnicmp _strnicmp #define stricmp _stricmp -#define strdup _strdup #endif #ifndef SQL_ATTR_APP_ROW_DESC @@ -340,6 +339,7 @@ void logs_on_off(int cnopen, int, int); #define PG_TYPE_LO_UNDEFINED (-999) /* hack until permanent * type available */ #define PG_TYPE_LO_NAME "lo" +#define CTID_ATTNUM (-1) /* the attnum of ctid */ #define OID_ATTNUM (-2) /* the attnum of oid */ #define XMIN_ATTNUM (-3) /* the attnum of xmin */ @@ -383,15 +383,24 @@ char *debug_strdup(const char *); void *debug_memcpy(void *, const void *, size_t); void *debug_memset(void *, int c, size_t); char *debug_strcpy(char *, const char *); +char *debug_strncpy(char *, const char *, size_t); +char *debug_strncpy_null(char *, const char *, size_t); void debug_free(void *); void debug_memory_check(void); +#ifdef WIN32 +#undef strdup +#undef strnicmp +#undef stricmp +#endif /* WIN32 */ #define malloc debug_alloc #define realloc debug_realloc #define calloc debug_calloc #define strdup debug_strdup #define free debug_free #define strcpy debug_strcpy +#define strncpy debug_strncpy +/* #define strncpy_null debug_strncpy_null */ #define memcpy debug_memcpy #define memset debug_memset #endif /* _MEMORY_DEBUG_ */ diff --git a/qresult.c b/qresult.c index 6117ae8..fb130b9 100644 --- a/qresult.c +++ b/qresult.c @@ -1478,8 +1478,8 @@ inolog("QR_read_a_tuple_from_db len=%d\n", len); * row! */ - if (flds && flds->display_size && flds->display_size[field_lf] < len) - flds->display_size[field_lf] = len; + if (flds && flds->coli_array && CI_get_display_size(flds, field_lf) < len) + CI_get_display_size(flds, field_lf) = len; } } diff --git a/qresult.h b/qresult.h index 76a8f19..b5db09a 100644 --- a/qresult.h +++ b/qresult.h @@ -121,6 +121,8 @@ enum { #define QR_get_display_size(self, fieldno_) (CI_get_display_size(self->fields, fieldno_)) #define QR_get_atttypmod(self, fieldno_) (CI_get_atttypmod(self->fields, fieldno_)) #define QR_get_field_type(self, fieldno_) (CI_get_oid(self->fields, fieldno_)) +#define QR_get_relid(self, fieldno_) (CI_get_relid(self->fields, fieldno_)) +#define QR_get_attid(self, fieldno_) (CI_get_attid(self->fields, fieldno_)) /* These functions are used only for manual result sets */ #define QR_get_num_total_tuples(self) (QR_once_reached_eof(self) ? (self->num_total_read + self->ad_count) : self->num_total_read) diff --git a/results.c b/results.c index 191639d..659cb75 100644 --- a/results.c +++ b/results.c @@ -48,7 +48,7 @@ PGAPI_RowCount( mylog("%s: entering...\n", func); if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } ci = &(SC_get_conn(stmt)->connInfo); @@ -89,6 +89,51 @@ inolog("returning RowCount=%d\n", *pcrow); return SQL_SUCCESS; } +static BOOL SC_pre_execute_ok(StatementClass *stmt, BOOL build_fi, int col_idx, const char *func) +{ + Int4 num_fields = SC_pre_execute(stmt); + QResultClass *result = SC_get_Curres(stmt); + BOOL exec_ok = TRUE; + + mylog("%s: result = %x, status = %d, numcols = %d\n", func, result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1); + /****if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) ****/ + if (!QR_command_maybe_successful(result) || num_fields < 0) + { + /* no query has been executed on this statement */ + SC_set_error(stmt, STMT_EXEC_ERROR, "No query has been executed with that handle", func); + exec_ok = FALSE; + } + else if (col_idx >= 0 && col_idx < num_fields) + { + Oid reloid = QR_get_relid(result, col_idx); + IRDFields *irdflds = SC_get_IRDF(stmt); + FIELD_INFO *fi; + TABLE_INFO *ti = NULL; + +inolog("build_fi=%d reloid=%u\n", build_fi, reloid); + if (build_fi && 0 != QR_get_attid(result, col_idx)) + getCOLIfromTI(func, stmt, reloid, &ti); +inolog("nfields=%d\n", irdflds->nfields); + if (irdflds->fi && col_idx < (int) irdflds->nfields) + { + fi = irdflds->fi[col_idx]; + if (fi) + { + if (ti) + { + if (NULL == fi->ti) + fi->ti = ti; + if (!FI_is_applicable(fi) + && 0 != (ti->flags & TI_COLATTRIBUTE)) + fi->flag |= FIELD_COL_ATTRIBUTE; + } + if (0 == fi->type) + fi->type = QR_get_field_type(result, col_idx); + } + } + } + return exec_ok; +} /* * This returns the number of columns associated with the database @@ -109,7 +154,7 @@ PGAPI_NumResultCols( mylog("%s: entering...\n", func); if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } ci = &(SC_get_conn(stmt)->connInfo); @@ -142,26 +187,13 @@ PGAPI_NumResultCols( if (!parse_ok) { - Int4 num_fields = SC_pre_execute(stmt); - result = SC_get_Curres(stmt); - - mylog("PGAPI_NumResultCols: result = %x, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1); - /****if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) ****/ - if (!result || num_fields < 0) - { - /* no query has been executed on this statement */ - SC_set_error(stmt, STMT_EXEC_ERROR, "No query has been executed with that handle", func); - ret = SQL_ERROR; - goto cleanup; - } - else if (!QR_command_maybe_successful(result)) + if (!SC_pre_execute_ok(stmt, FALSE, -1, func)) { - SC_set_errornumber(stmt, STMT_EXEC_ERROR); - SC_log_error(func, "", stmt); ret = SQL_ERROR; goto cleanup; } + result = SC_get_Curres(stmt); *pccol = QR_NumPublicResultCols(result); } @@ -201,7 +233,6 @@ PGAPI_DescribeCol( SQLLEN column_size = 0; SQLINTEGER decimal_digits = 0; ConnInfo *ci; - char parse_ok; FIELD_INFO *fi; char buf[255]; int len = 0; @@ -211,7 +242,7 @@ PGAPI_DescribeCol( if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -221,7 +252,6 @@ PGAPI_DescribeCol( SC_clear_error(stmt); #define return DONT_CALL_RETURN_FROM_HERE??? - /* StartRollbackState(stmt); */ irdflds = SC_get_IRDF(stmt); #if (ODBCVER >= 0x0300) if (0 == icol) /* bookmark column */ @@ -252,19 +282,14 @@ inolog("answering bookmark info\n"); icol--; /* use zero based column numbers */ - parse_ok = FALSE; fi = NULL; if (icol < irdflds->nfields && irdflds->fi) - { fi = irdflds->fi[icol]; - if (fi && 0 == (fi->flag & (FIELD_PARSED_OK | FIELD_COL_ATTRIBUTE))) - fi = NULL; - } - if (!fi && !stmt->catalog_result && ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT) + if (!FI_is_applicable(fi) && !stmt->catalog_result && ci->drivers.parse && STMT_TYPE_SELECT == stmt->statement_type) { if (SC_parsed_status(stmt) == STMT_PARSE_NONE) { - mylog("PGAPI_DescribeCol: calling parse_statement on stmt=%x\n", stmt); + mylog("%s: calling parse_statement on stmt=%x\n", func, stmt); parse_statement(stmt, FALSE); } @@ -284,37 +309,22 @@ inolog("answering bookmark info\n"); } } - if (fi && 0 == fi->type) - fi = NULL; - - /* - * If couldn't parse it OR the field being described was not parsed - * (i.e., because it was a function or expression, etc, then do it the - * old fashioned way. - */ - if (!fi) + if (!FI_is_applicable(fi)) { - Int4 num_fields = SC_pre_execute(stmt); - - res = SC_get_Curres(stmt); - - mylog("**** PGAPI_DescribeCol: res = %x, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE); - /**** if ((NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) ****/ - if ((NULL == res) || num_fields < 0) - { - /* no query has been executed on this statement */ - SC_set_error(stmt, STMT_EXEC_ERROR, "No query has been assigned to this statement.", func); - result = SQL_ERROR; - goto cleanup; - } - else if (!QR_command_maybe_successful(res)) + /* + * If couldn't parse it OR the field being described was not parsed + * (i.e., because it was a function or expression, etc, then do it the + * old fashioned way. + */ + BOOL build_fi = PROTOCOL_74(ci) && (NULL != pfNullable || NULL != pfSqlType); + fi = NULL; + if (!SC_pre_execute_ok(stmt, build_fi, icol, func)) { - SC_set_errornumber(stmt, STMT_EXEC_ERROR); - SC_log_error(func, "", stmt); result = SQL_ERROR; goto cleanup; } + res = SC_get_Curres(stmt); if (icol >= QR_NumPublicResultCols(res)) { SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.", NULL); @@ -323,8 +333,10 @@ inolog("answering bookmark info\n"); result = SQL_ERROR; goto cleanup; } + if (icol < irdflds->nfields && irdflds->fi) + fi = irdflds->fi[icol]; } - if (fi) + if (FI_is_applicable(fi)) { fieldtype = fi->type; if (NAME_IS_VALID(fi->column_alias)) @@ -411,7 +423,7 @@ inolog("answering bookmark info\n"); */ if (pfNullable) { - *pfNullable = (parse_ok) ? irdflds->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype); + *pfNullable = fi ? fi->nullable : pgtype_nullable(stmt, fieldtype); mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable); } @@ -443,20 +455,20 @@ PGAPI_ColAttributes( ConnInfo *ci; int unknown_sizes; int cols = 0; - char parse_ok; RETCODE result; const char *p = NULL; int len = 0, value = 0; const FIELD_INFO *fi = NULL; const TABLE_INFO *ti = NULL; + QResultClass *res; mylog("%s: entering..col=%d %d len=%d.\n", func, icol, fDescType, cbDescMax); if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -472,6 +484,7 @@ PGAPI_ColAttributes( * is ignored anyway, so it may be 0. */ + res = SC_get_Curres(stmt); #if (ODBCVER >= 0x0300) if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */ { @@ -499,7 +512,6 @@ inolog("answering bookmark info\n"); if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) unknown_sizes = UNKNOWNS_AS_MAX; - parse_ok = FALSE; if (!stmt->catalog_result && ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT) { if (SC_parsed_status(stmt) == STMT_PARSE_NONE) @@ -533,33 +545,44 @@ inolog("answering bookmark info\n"); SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in ColAttributes.", func); return SQL_ERROR; } - if (irdflds->fi[col_idx]) - { - field_type = irdflds->fi[col_idx]->type; - if (field_type > 0) - parse_ok = TRUE; - } } } if (col_idx < irdflds->nfields && irdflds->fi) fi = irdflds->fi[col_idx]; - if (fi && 0 != (fi->flag & (FIELD_COL_ATTRIBUTE | FIELD_PARSED_OK))) - ; + if (FI_is_applicable(fi)) + field_type = fi->type; else { - fi = NULL; - SC_pre_execute(stmt); + BOOL build_fi = FALSE; - mylog("**** PGAPI_ColAtt: result = %x, status = %d, numcols = %d\n", SC_get_Curres(stmt), stmt->status, SC_get_Curres(stmt) != NULL ? QR_NumResultCols(SC_get_Curres(stmt)) : -1); - - if ((NULL == SC_get_Curres(stmt)) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) + fi = NULL; + if (PROTOCOL_74(ci)) { - SC_set_error(stmt, STMT_EXEC_ERROR, "Can't get column attributes: no result found.", func); - return SQL_ERROR; + switch (fDescType) + { + case SQL_COLUMN_OWNER_NAME: + case SQL_COLUMN_TABLE_NAME: + case SQL_COLUMN_TYPE: + case SQL_COLUMN_TYPE_NAME: + case SQL_COLUMN_AUTO_INCREMENT: +#if (ODBCVER >= 0x0300) + case SQL_DESC_NULLABLE: + case SQL_DESC_BASE_TABLE_NAME: + case SQL_DESC_BASE_COLUMN_NAME: +#else + case SQL_COLUMN_NULLABLE: +#endif /* ODBCVER */ + case SQL_COLUMN_UPDATABLE: + build_fi = TRUE; + break; + } } + if (!SC_pre_execute_ok(stmt, build_fi, col_idx, func)) + return SQL_ERROR; - cols = QR_NumPublicResultCols(SC_get_Curres(stmt)); + res = SC_get_Curres(stmt); + cols = QR_NumPublicResultCols(res); /* * Column Count is a special case. The Column number is ignored @@ -583,17 +606,17 @@ inolog("answering bookmark info\n"); return SQL_ERROR; } - field_type = QR_get_field_type(SC_get_Curres(stmt), col_idx); - if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi && col_idx < irdflds->nfields) + field_type = QR_get_field_type(res, col_idx); + if (col_idx < irdflds->nfields && irdflds->fi) fi = irdflds->fi[col_idx]; } - if (fi) + if (FI_is_applicable(fi)) { ti = fi->ti; field_type = fi->type; } - mylog("colAttr: col %d field_type = %d\n", col_idx, field_type); + mylog("colAttr: col %d field_type=%d fi,ti=%x,%x\n", col_idx, field_type, fi, ti); switch (fDescType) { @@ -604,7 +627,7 @@ inolog("answering bookmark info\n"); value = pgtype_auto_increment(stmt, field_type); if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */ value = FALSE; -inolog("AUTO_INCREMENT=%d\n", value); + mylog("AUTO_INCREMENT=%d\n", value); break; @@ -642,7 +665,7 @@ inolog("AUTO_INCREMENT=%d\n", value); inolog("fi=%x", fi); if (fi) inolog(" (%s,%s)", PRINT_NAME(fi->column_alias), PRINT_NAME(fi->column_name)); - p = fi ? (NAME_IS_NULL(fi->column_alias) ? SAFE_NAME(fi->column_name) : GET_NAME(fi->column_alias)) : QR_get_fieldname(SC_get_Curres(stmt), col_idx); + p = fi ? (NAME_IS_NULL(fi->column_alias) ? SAFE_NAME(fi->column_name) : GET_NAME(fi->column_alias)) : QR_get_fieldname(res, col_idx); mylog("%s: COLUMN_NAME = '%s'\n", func, p); break; @@ -671,6 +694,7 @@ inolog("COLUMN_NULLABLE=%d\n", value); case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */ p = ti ? SAFE_NAME(ti->schema_name) : NULL_STRING; + mylog("schema_name=%s\n", p); break; case SQL_COLUMN_PRECISION: /* in 2.x */ @@ -682,7 +706,7 @@ inolog("COLUMN_NULLABLE=%d\n", value); break; case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */ - p = ""; + p = ti ? CurrCatString(conn) : NULL_STRING; /* empty string means *not supported* */ break; case SQL_COLUMN_SCALE: /* in 2.x */ @@ -699,12 +723,12 @@ inolog("COLUMN_SCALE=%d\n", value); case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */ p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING; - mylog("%sr: TABLE_NAME = '%s'\n", func, p); + mylog("%s: TABLE_NAME = '%s'\n", func, p); break; case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */ value = pgtype_to_concise_type(stmt, field_type, col_idx); -inolog("COLUMN_TYPE=%d\n", value); + mylog("COLUMN_TYPE=%d\n", value); break; case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */ @@ -714,7 +738,7 @@ inolog("COLUMN_TYPE=%d\n", value); case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */ value = pgtype_unsigned(stmt, field_type); if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */ - value = TRUE; + value = SQL_TRUE; break; @@ -726,10 +750,10 @@ inolog("COLUMN_TYPE=%d\n", value); * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY; * else */ - value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN; + value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : (QR_get_attid(res, col_idx) > 0 ? SQL_ATTR_WRITE : (PROTOCOL_74(ci) ? SQL_ATTR_READONLY : SQL_ATTR_READWRITE_UNKNOWN)); if (SQL_ATTR_READONLY != value) { - const char *name = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx); + const char *name = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res, col_idx); if (stricmp(name, OID_NAME) == 0 || stricmp(name, "ctid") == 0 || stricmp(name, "xmin") == 0) @@ -741,7 +765,7 @@ inolog("COLUMN_TYPE=%d\n", value); #if (ODBCVER >= 0x0300) case SQL_DESC_BASE_COLUMN_NAME: - p = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx); + p = fi ? SAFE_NAME(fi->column_name) : QR_get_fieldname(res, col_idx); mylog("%s: BASE_COLUMN_NAME = '%s'\n", func, p); break; @@ -854,12 +878,13 @@ PGAPI_GetData( RETCODE result = SQL_SUCCESS; char get_bookmark = FALSE; ConnInfo *ci; + SQLSMALLINT target_type; mylog("%s: enter, stmt=%x\n", func, stmt); if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } ci = &(SC_get_conn(stmt)->connInfo); @@ -877,6 +902,29 @@ PGAPI_GetData( return SQL_ERROR; } + if (SQL_ARD_TYPE == fCType) + { + ARDFields *opts; + BindInfoClass *binfo = NULL; + + opts = SC_get_ARDF(stmt); + if (0 == icol) + binfo = opts->bookmark; + else if (icol <= opts->allocated && opts->bindings) + binfo = &opts->bindings[icol - 1]; + if (binfo) + { + target_type = binfo->returntype; + mylog("SQL_ARD_TYPE=%d\n", target_type); + } + else + { + SC_set_error(stmt, STMT_STATUS_ERROR, "GetData can't determine the type via ARD", func); + return SQL_ERROR; + } + } + else + target_type = fCType; if (icol == 0) { if (stmt->options.use_bookmarks == SQL_UB_OFF) @@ -886,7 +934,7 @@ PGAPI_GetData( } /* Make sure it is the bookmark data type */ - switch (fCType) + switch (target_type) { case SQL_C_BOOKMARK: #if (ODBCVER >= 0x0300) @@ -894,7 +942,7 @@ PGAPI_GetData( #endif /* ODBCVER */ break; default: -inolog("GetData Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); +inolog("GetData Column 0 is type %d not of type SQL_C_BOOKMARK", target_type); SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Column 0 is not of type SQL_C_BOOKMARK", func); return SQL_ERROR; } @@ -963,7 +1011,7 @@ inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple, QR_get_rowstart_in_cache if (rgbValue) { - if (SQL_C_BOOKMARK == fCType || 4 <= cbValueMax) + if (SQL_C_BOOKMARK == target_type || 4 <= cbValueMax) { contents_get = TRUE; *((SQLULEN *) rgbValue) = SC_get_bookmark(stmt); @@ -984,12 +1032,12 @@ inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple, QR_get_rowstart_in_cache field_type = QR_get_field_type(res, icol); - mylog("**** %s: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", func, icol, fCType, field_type, value ? value : "(null)"); + mylog("**** %s: icol = %d, target_type = %d, field_type = %d, value = '%s'\n", func, icol, target_type, field_type, value ? value : "(null)"); SC_set_current_col(stmt, icol); result = copy_and_convert_field(stmt, field_type, value, - fCType, rgbValue, cbValueMax, pcbValue); + target_type, rgbValue, cbValueMax, pcbValue); switch (result) { @@ -1054,7 +1102,7 @@ PGAPI_Fetch( if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -1331,7 +1379,7 @@ PGAPI_ExtendedFetch( if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } ci = &(SC_get_conn(stmt)->connInfo); @@ -4385,7 +4433,7 @@ PGAPI_SetPos( s.stmt = (StatementClass *) hstmt; if (!s.stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -4568,7 +4616,7 @@ PGAPI_SetCursorName( if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } @@ -4596,7 +4644,7 @@ PGAPI_GetCursorName( if (!stmt) { - SC_log_error(func, "", NULL); + SC_log_error(func, NULL_STRING, NULL); return SQL_INVALID_HANDLE; } result = SQL_SUCCESS; diff --git a/statement.c b/statement.c index 7d60661..9883fd0 100644 --- a/statement.c +++ b/statement.c @@ -672,9 +672,9 @@ BOOL SC_opencheck(StatementClass *self, const char *func) mylog("SC_opencheck: self->prepare && self->status == STMT_PREMATURE\n"); return FALSE; } - if (res = SC_get_Result(self), NULL != res) + if (res = SC_get_Curres(self), NULL != res) { - if (res->backend_tuples) + if (QR_command_maybe_successful(res) && res->backend_tuples) { SC_set_error(self, STMT_SEQUENCE_ERROR, "The cursor is open.", func); return TRUE; @@ -844,7 +844,7 @@ SC_pre_execute(StatementClass *self) char old_pre_executing = self->pre_executing; decideHowToPrepare(self); - self->inaccurate_result = TRUE; + self->inaccurate_result = FALSE; switch (SC_get_prepare_method(self)) { case USING_PARSE_REQUEST: @@ -855,10 +855,10 @@ SC_pre_execute(StatementClass *self) if (SQL_SUCCESS != prepareParameters(self)) return num_fields; self->status = STMT_PREMATURE; + self->inaccurate_result = TRUE; break; default: self->pre_executing = TRUE; - self->inaccurate_result = FALSE; PGAPI_Execute(self, 0); self->pre_executing = old_pre_executing; @@ -1761,6 +1761,8 @@ inolog("!!%x->SC_is_concat_pre=%x res=%x\n", self, self->miscinfo, res); tres = res->next; inolog("res->next=%x\n", tres); res->next = NULL; + if (res != SC_get_Result(self)) + QR_Destructor(res); SC_set_Result(self, tres); res = tres; SC_set_prepared(self, PREPARED_PERMANENTLY); @@ -2167,8 +2169,8 @@ inolog("num_params=%d info=%d\n", stmt->num_params, num_p); if (SQL_PARAM_OUTPUT == paramType || SQL_PARAM_INPUT_OUTPUT == paramType) { -inolog("!![%d].PGType %u->%u\n", i, ipdopts->parameters[i].PGType, res->fields->adtid[cidx]); - ipdopts->parameters[i].PGType = res->fields->adtid[cidx]; +inolog("!![%d].PGType %u->%u\n", i, ipdopts->parameters[i].PGType, CI_get_oid(res->fields, cidx)); + ipdopts->parameters[i].PGType = CI_get_oid(res->fields, cidx); cidx++; } } diff --git a/version.h b/version.h index 2b4bd9b..1709db8 100644 --- a/version.h +++ b/version.h @@ -9,8 +9,8 @@ #ifndef __VERSION_H__ #define __VERSION_H__ -#define POSTGRESDRIVERVERSION "08.02.0004" -#define POSTGRES_RESOURCE_VERSION "08.02.0004\0" -#define PG_DRVFILE_VERSION 8,2,0,04 +#define POSTGRESDRIVERVERSION "08.02.0005" +#define POSTGRES_RESOURCE_VERSION "08.02.0005\0" +#define PG_DRVFILE_VERSION 8,2,0,05 #endif -- 2.39.5