pgtypes.c (1,685 lines of code) (raw):

/*-------- * Module: pgtypes.c * * Description: This module contains routines for getting information * about the supported Postgres data types. Only the * function pgtype_to_sqltype() returns an unknown condition. * All other functions return a suitable default so that * even data types that are not directly supported can be * used (it is handled as char data). * * Classes: n/a * * API functions: none * * Comments: See "readme.txt" for copyright and license information. *-------- */ #include "pgtypes.h" #include "dlg_specific.h" #include "statement.h" #include "connection.h" #include "environ.h" #include "qresult.h" #define EXPERIMENTAL_CURRENTLY SQLSMALLINT ansi_to_wtype(const ConnectionClass *self, SQLSMALLINT ansitype) { #ifndef UNICODE_SUPPORT return ansitype; #else if (!ALLOW_WCHAR(self)) return ansitype; switch (ansitype) { case SQL_CHAR: return SQL_WCHAR; case SQL_VARCHAR: return SQL_WVARCHAR; case SQL_LONGVARCHAR: return SQL_WLONGVARCHAR; } return ansitype; #endif /* UNICODE_SUPPORT */ } Int4 getCharColumnSize(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* * these are the types we support. all of the pgtype_ functions should * return values for each one of these. * Even types not directly supported are handled as character types * so all types should work (points, etc.) */ /* * ALL THESE TYPES ARE NO LONGER REPORTED in SQLGetTypeInfo. Instead, all * the SQL TYPES are reported and mapped to a corresponding Postgres Type */ /* OID pgtypes_defined[][2] = { {PG_TYPE_CHAR, 0} ,{PG_TYPE_CHAR2, 0} ,{PG_TYPE_CHAR4, 0} ,{PG_TYPE_CHAR8, 0} ,{PG_TYPE_CHAR16, 0} ,{PG_TYPE_NAME, 0} ,{PG_TYPE_VARCHAR, 0} ,{PG_TYPE_BPCHAR, 0} ,{PG_TYPE_DATE, 0} ,{PG_TYPE_TIME, 0} ,{PG_TYPE_TIME_WITH_TMZONE, 0} ,{PG_TYPE_DATETIME, 0} ,{PG_TYPE_ABSTIME, 0} ,{PG_TYPE_TIMESTAMP_NO_TMZONE, 0} ,{PG_TYPE_TIMESTAMP, 0} ,{PG_TYPE_TEXT, 0} ,{PG_TYPE_INT2, 0} ,{PG_TYPE_INT4, 0} ,{PG_TYPE_FLOAT4, 0} ,{PG_TYPE_FLOAT8, 0} ,{PG_TYPE_OID, 0} ,{PG_TYPE_MONEY, 0} ,{PG_TYPE_BOOL, 0} ,{PG_TYPE_BYTEA, 0} ,{PG_TYPE_NUMERIC, 0} ,{PG_TYPE_XID, 0} ,{PG_TYPE_LO_UNDEFINED, 0} ,{0, 0} }; */ /* These are NOW the SQL Types reported in SQLGetTypeInfo. */ SQLSMALLINT sqlTypes[] = { SQL_BIGINT, /* SQL_BINARY, -- Commented out because VarBinary is more correct. */ SQL_BIT, SQL_CHAR, SQL_TYPE_DATE, SQL_DATE, SQL_DECIMAL, SQL_DOUBLE, SQL_FLOAT, SQL_INTEGER, SQL_LONGVARBINARY, SQL_LONGVARCHAR, SQL_NUMERIC, SQL_REAL, SQL_SMALLINT, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, SQL_TIME, SQL_TIMESTAMP, SQL_TINYINT, SQL_VARBINARY, SQL_VARCHAR, #ifdef UNICODE_SUPPORT SQL_WCHAR, SQL_WVARCHAR, SQL_WLONGVARCHAR, #endif /* UNICODE_SUPPORT */ SQL_GUID, /* AFAIK SQL_INTERVAL types cause troubles in some spplications */ #ifdef PG_INTERVAL_AS_SQL_INTERVAL SQL_INTERVAL_MONTH, SQL_INTERVAL_YEAR, SQL_INTERVAL_YEAR_TO_MONTH, SQL_INTERVAL_DAY, SQL_INTERVAL_HOUR, SQL_INTERVAL_MINUTE, SQL_INTERVAL_SECOND, SQL_INTERVAL_DAY_TO_HOUR, SQL_INTERVAL_DAY_TO_MINUTE, SQL_INTERVAL_DAY_TO_SECOND, SQL_INTERVAL_HOUR_TO_MINUTE, SQL_INTERVAL_HOUR_TO_SECOND, SQL_INTERVAL_MINUTE_TO_SECOND, #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ 0 }; #ifdef ODBCINT64 #define ALLOWED_C_BIGINT SQL_C_SBIGINT /* #define ALLOWED_C_BIGINT SQL_C_CHAR */ /* Delphi should be either ? */ #else #define ALLOWED_C_BIGINT SQL_C_CHAR #endif OID pg_true_type(const ConnectionClass *conn, OID type, OID basetype) { if (0 == basetype) return type; else if (0 == type) return basetype; else if (type == conn->lobj_type) return type; return basetype; } #define MONTH_BIT (1 << 17) #define YEAR_BIT (1 << 18) #define DAY_BIT (1 << 19) #define HOUR_BIT (1 << 26) #define MINUTE_BIT (1 << 27) #define SECOND_BIT (1 << 28) static SQLSMALLINT get_interval_type(Int4 atttypmod, const char **name) { MYLOG(MIN_LOG_LEVEL, "entering atttypmod=%x\n", atttypmod); if ((-1) == atttypmod) return 0; if (0 != (YEAR_BIT & atttypmod)) { if (0 != (MONTH_BIT & atttypmod)) { if (name) *name = "interval year to month"; return SQL_INTERVAL_YEAR_TO_MONTH; } if (name) *name = "interval year"; return SQL_INTERVAL_YEAR; } else if (0 != (MONTH_BIT & atttypmod)) { if (name) *name = "interval month"; return SQL_INTERVAL_MONTH; } else if (0 != (DAY_BIT & atttypmod)) { if (0 != (SECOND_BIT & atttypmod)) { if (name) *name = "interval day to second"; return SQL_INTERVAL_DAY_TO_SECOND; } else if (0 != (MINUTE_BIT & atttypmod)) { if (name) *name = "interval day to minute"; return SQL_INTERVAL_DAY_TO_MINUTE; } else if (0 != (HOUR_BIT & atttypmod)) { if (name) *name = "interval day to hour"; return SQL_INTERVAL_DAY_TO_HOUR; } if (name) *name = "interval day"; return SQL_INTERVAL_DAY; } else if (0 != (HOUR_BIT & atttypmod)) { if (0 != (SECOND_BIT & atttypmod)) { if (name) *name = "interval hour to second"; return SQL_INTERVAL_HOUR_TO_SECOND; } else if (0 != (MINUTE_BIT & atttypmod)) { if (name) *name = "interval hour to minute"; return SQL_INTERVAL_HOUR_TO_MINUTE; } if (name) *name = "interval hour"; return SQL_INTERVAL_HOUR; } else if (0 != (MINUTE_BIT & atttypmod)) { if (0 != (SECOND_BIT & atttypmod)) { if (name) *name = "interval minute to second"; return SQL_INTERVAL_MINUTE_TO_SECOND; } if (name) *name = "interval minute"; return SQL_INTERVAL_MINUTE; } else if (0 != (SECOND_BIT & atttypmod)) { if (name) *name = "interval second"; return SQL_INTERVAL_SECOND; } if (name) *name = "interval"; return 0; } static Int4 getCharColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { int p = -1, maxsize; const ConnInfo *ci = &(conn->connInfo); MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d, adtsize_or=%d, unknown = %d\n", type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); /* Assign Maximum size based on parameters */ switch (type) { case PG_TYPE_TEXT: if (ci->drivers.text_as_longvarchar) maxsize = ci->drivers.max_longvarchar_size; else maxsize = ci->drivers.max_varchar_size; break; case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: maxsize = ci->drivers.max_varchar_size; break; default: if (ci->drivers.unknowns_as_longvarchar) maxsize = ci->drivers.max_longvarchar_size; else maxsize = ci->drivers.max_varchar_size; break; } #ifdef UNICODE_SUPPORT if (CC_is_in_unicode_driver(conn) && isSqlServr() && maxsize > 4000) maxsize = 4000; #endif /* UNICODE_SUPPORT */ if (maxsize == TEXT_FIELD_SIZE + 1) /* magic length for testing */ maxsize = 0; /* * Static ColumnSize (i.e., the Maximum ColumnSize of the datatype) This * has nothing to do with a result set. */ MYLOG(DETAIL_LOG_LEVEL, "!!! atttypmod < 0 ?\n"); if (atttypmod < 0 && adtsize_or_longestlen < 0) return maxsize; MYLOG(DETAIL_LOG_LEVEL, "!!! adtsize_or_logngest=%d\n", adtsize_or_longestlen); p = adtsize_or_longestlen; /* longest */ /* * Catalog Result Sets -- use assigned column width (i.e., from * set_tuplefield_string) */ MYLOG(DETAIL_LOG_LEVEL, "!!! catalog_result=%d\n", handle_unknown_size_as); if (UNKNOWNS_AS_LONGEST == handle_unknown_size_as) { MYLOG(MIN_LOG_LEVEL, "LONGEST: p = %d\n", p); if (p > 0 && (atttypmod < 0 || atttypmod > p)) return p; } if (TYPE_MAY_BE_ARRAY(type)) { if (p > 0) return p; return maxsize; } /* Size is unknown -- handle according to parameter */ if (atttypmod > 0) /* maybe the length is known */ { return atttypmod; } /* The type is really unknown */ switch (handle_unknown_size_as) { case UNKNOWNS_AS_DONTKNOW: return -1; case UNKNOWNS_AS_LONGEST: case UNKNOWNS_AS_MAX: break; default: return -1; } if (maxsize <= 0) return maxsize; switch (type) { case PG_TYPE_BPCHAR: case PG_TYPE_VARCHAR: case PG_TYPE_TEXT: return maxsize; } if (p > maxsize) maxsize = p; return maxsize; } /* * Specify when handle_unknown_size_as parameter is unused */ #define UNUSED_HANDLE_UNKNOWN_SIZE_AS (-2) static SQLSMALLINT getNumericDecimalDigitsX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int UNUSED_handle_unknown_size_as) { Int4 default_decimal_digits = 6; MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d\n", type, atttypmod); if (atttypmod < 0 && adtsize_or_longest < 0) return default_decimal_digits; if (atttypmod > -1) return (atttypmod & 0xffff); if (adtsize_or_longest <= 0) return default_decimal_digits; adtsize_or_longest >>= 16; /* extract the scale part */ return adtsize_or_longest; } static Int4 /* PostgreSQL restriction */ getNumericColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) { Int4 default_column_size = 28; const ConnInfo *ci = &(conn->connInfo); MYLOG(MIN_LOG_LEVEL, "entering type=%d, typmod=%d\n", type, atttypmod); if (atttypmod > -1) return (atttypmod >> 16) & 0xffff; switch (ci->numeric_as) { case SQL_VARCHAR: return ci->drivers.max_varchar_size; case SQL_LONGVARCHAR: return ci->drivers.max_longvarchar_size; case SQL_DOUBLE: return PG_DOUBLE_DIGITS; } switch (handle_unknown_size_as) { case UNKNOWNS_AS_DONTKNOW: return SQL_NO_TOTAL; } if (adtsize_or_longest <= 0) return default_column_size; adtsize_or_longest %= (1 << 16); /* extract the precision part */ switch (handle_unknown_size_as) { case UNKNOWNS_AS_MAX: return adtsize_or_longest > default_column_size ? adtsize_or_longest : default_column_size; default: if (adtsize_or_longest < 10) adtsize_or_longest = 10; } return adtsize_or_longest; } static SQLSMALLINT getTimestampDecimalDigitsX(const ConnectionClass *conn, OID type, int atttypmod) { MYLOG(MIN_LOG_LEVEL, "type=%d, atttypmod=%d\n", type, atttypmod); return (atttypmod > -1 ? atttypmod : 6); } static SQLSMALLINT getTimestampColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod) { Int4 fixed, scale; MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d\n", type, atttypmod); switch (type) { case PG_TYPE_TIME: fixed = 8; break; case PG_TYPE_TIME_WITH_TMZONE: fixed = 11; break; case PG_TYPE_TIMESTAMP_NO_TMZONE: fixed = 19; break; default: if (USE_ZONE) fixed = 22; else fixed = 19; break; } scale = getTimestampDecimalDigitsX(conn, type, atttypmod); return (scale > 0) ? fixed + 1 + scale : fixed; } static SQLSMALLINT getIntervalDecimalDigits(OID type, int atttypmod) { Int4 prec; MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d\n", type, atttypmod); if ((atttypmod & SECOND_BIT) == 0) return 0; return (prec = atttypmod & 0xffff) == 0xffff ? 6 : prec ; } static SQLSMALLINT getIntervalColumnSize(OID type, int atttypmod) { Int4 ttl, leading_precision = 9, scale; MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d\n", type, atttypmod); ttl = leading_precision; switch (get_interval_type(atttypmod, NULL)) { case 0: ttl = 25; break; case SQL_INTERVAL_YEAR: ttl = 16; break; case SQL_INTERVAL_MONTH: ttl = 16; break; case SQL_INTERVAL_DAY: ttl = 16; break; case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_DAY_TO_MINUTE: case SQL_INTERVAL_DAY_TO_HOUR: ttl = 25; break; case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_HOUR_TO_MINUTE: case SQL_INTERVAL_HOUR: ttl = 17; break; case SQL_INTERVAL_MINUTE_TO_SECOND: case SQL_INTERVAL_MINUTE: ttl = 15; break; case SQL_INTERVAL_YEAR_TO_MONTH: ttl = 24; break; } scale = getIntervalDecimalDigits(type, atttypmod); return (scale > 0) ? ttl + 1 + scale : ttl; } SQLSMALLINT pgtype_attr_to_concise_type(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { const ConnInfo *ci = &(conn->connInfo); EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); #ifdef PG_INTERVAL_AS_SQL_INTERVAL SQLSMALLINT sqltype; #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ BOOL bLongVarchar, bFixed = FALSE; switch (type) { case PG_TYPE_CHAR: return ansi_to_wtype(conn, SQL_CHAR); case PG_TYPE_NAME: case PG_TYPE_REFCURSOR: return ansi_to_wtype(conn, SQL_VARCHAR); case PG_TYPE_BPCHAR: bFixed = TRUE; case PG_TYPE_VARCHAR: if (getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as) > ci->drivers.max_varchar_size) bLongVarchar = TRUE; else bLongVarchar = FALSE; return ansi_to_wtype(conn, bLongVarchar ? SQL_LONGVARCHAR : (bFixed ? SQL_CHAR : SQL_VARCHAR)); case PG_TYPE_TEXT: bLongVarchar = ci->drivers.text_as_longvarchar; if (bLongVarchar) { int column_size = getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); if (column_size > 0 && column_size <= ci->drivers.max_varchar_size) bLongVarchar = FALSE; } return ansi_to_wtype(conn, bLongVarchar ? SQL_LONGVARCHAR : SQL_VARCHAR); case PG_TYPE_BYTEA: if (ci->bytea_as_longvarbinary) return SQL_LONGVARBINARY; else return SQL_VARBINARY; case PG_TYPE_LO_UNDEFINED: return SQL_LONGVARBINARY; case PG_TYPE_INT2: return SQL_SMALLINT; case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: return SQL_INTEGER; /* Change this to SQL_BIGINT for ODBC v3 bjm 2001-01-23 */ case PG_TYPE_INT8: if (ci->int8_as != 0) return ci->int8_as; if (conn->ms_jet) return SQL_NUMERIC; /* maybe a little better than SQL_VARCHAR */ return SQL_BIGINT; case PG_TYPE_NUMERIC: if (-1 == atttypmod && DEFAULT_NUMERIC_AS != ci->numeric_as) return ci->numeric_as; return SQL_NUMERIC; case PG_TYPE_FLOAT4: return SQL_REAL; case PG_TYPE_FLOAT8: return SQL_FLOAT; case PG_TYPE_DATE: if (EN_is_odbc3(env)) return SQL_TYPE_DATE; return SQL_DATE; case PG_TYPE_TIME: if (EN_is_odbc3(env)) return SQL_TYPE_TIME; return SQL_TIME; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: case PG_TYPE_TIMESTAMP: if (EN_is_odbc3(env)) return SQL_TYPE_TIMESTAMP; return SQL_TIMESTAMP; case PG_TYPE_MONEY: return SQL_FLOAT; case PG_TYPE_BOOL: return ci->drivers.bools_as_char ? SQL_VARCHAR : SQL_BIT; case PG_TYPE_XML: return ansi_to_wtype(conn, SQL_LONGVARCHAR); case PG_TYPE_INET: case PG_TYPE_CIDR: case PG_TYPE_MACADDR: return ansi_to_wtype(conn, SQL_VARCHAR); case PG_TYPE_UUID: return SQL_GUID; case PG_TYPE_INTERVAL: #ifdef PG_INTERVAL_AS_SQL_INTERVAL if (sqltype = get_interval_type(atttypmod, NULL), 0 != sqltype) return sqltype; #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ return ansi_to_wtype(conn, SQL_VARCHAR); case PG_TYPE_BIT: if (1 == atttypmod) return SQL_BIT; else return SQL_VARCHAR; default: /* * first, check to see if 'type' is in list. If not, look up * with query. Add oid, name to list. If it's already in * list, just return. */ /* hack until permanent type is available */ if (type == conn->lobj_type) return SQL_LONGVARBINARY; bLongVarchar = ci->drivers.unknowns_as_longvarchar; if (bLongVarchar) { int column_size = getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); if (column_size > 0 && column_size <= ci->drivers.max_varchar_size) bLongVarchar = FALSE; } #ifdef EXPERIMENTAL_CURRENTLY return ansi_to_wtype(conn, bLongVarchar ? SQL_LONGVARCHAR : SQL_VARCHAR); #endif /* EXPERIMENTAL_CURRENTLY */ return bLongVarchar ? SQL_LONGVARCHAR : SQL_VARCHAR; } } SQLSMALLINT pgtype_attr_to_sqldesctype(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { SQLSMALLINT rettype; #ifdef PG_INTERVAL_AS_SQL_INTERVAL if (PG_TYPE_INTERVAL == type) return SQL_INTERVAL; #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ switch (rettype = pgtype_attr_to_concise_type(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as)) { case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: return SQL_DATETIME; } return rettype; } SQLSMALLINT pgtype_attr_to_datetime_sub(const ConnectionClass *conn, OID type, int atttypmod) { SQLSMALLINT rettype; switch (rettype = pgtype_attr_to_concise_type(conn, type, atttypmod, PG_ADT_UNSET, PG_UNKNOWNS_UNSET)) { case SQL_TYPE_DATE: return SQL_CODE_DATE; case SQL_TYPE_TIME: return SQL_CODE_TIME; case SQL_TYPE_TIMESTAMP: return SQL_CODE_TIMESTAMP; case SQL_INTERVAL_MONTH: case SQL_INTERVAL_YEAR: case SQL_INTERVAL_YEAR_TO_MONTH: case SQL_INTERVAL_DAY: case SQL_INTERVAL_HOUR: case SQL_INTERVAL_MINUTE: case SQL_INTERVAL_SECOND: case SQL_INTERVAL_DAY_TO_HOUR: case SQL_INTERVAL_DAY_TO_MINUTE: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_HOUR_TO_MINUTE: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_MINUTE_TO_SECOND: return rettype - 100; } return -1; } SQLSMALLINT pgtype_attr_to_ctype(const ConnectionClass *conn, OID type, int atttypmod) { const ConnInfo *ci = &(conn->connInfo); EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); #ifdef PG_INTERVAL_AS_SQL_INTERVAL SQLSMALLINT ctype; #endif /* PG_INTERVAL_A_SQL_INTERVAL */ switch (type) { case PG_TYPE_INT8: if (!conn->ms_jet) return ALLOWED_C_BIGINT; return SQL_C_CHAR; case PG_TYPE_NUMERIC: return SQL_C_CHAR; case PG_TYPE_INT2: 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: return SQL_C_FLOAT; case PG_TYPE_FLOAT8: return SQL_C_DOUBLE; case PG_TYPE_DATE: if (EN_is_odbc3(env)) return SQL_C_TYPE_DATE; return SQL_C_DATE; case PG_TYPE_TIME: if (EN_is_odbc3(env)) return SQL_C_TYPE_TIME; return SQL_C_TIME; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: case PG_TYPE_TIMESTAMP: if (EN_is_odbc3(env)) return SQL_C_TYPE_TIMESTAMP; return SQL_C_TIMESTAMP; case PG_TYPE_MONEY: return SQL_C_FLOAT; case PG_TYPE_BOOL: return ci->drivers.bools_as_char ? SQL_C_CHAR : SQL_C_BIT; case PG_TYPE_BYTEA: return SQL_C_BINARY; case PG_TYPE_LO_UNDEFINED: return SQL_C_BINARY; case PG_TYPE_BPCHAR: case PG_TYPE_VARCHAR: case PG_TYPE_TEXT: return ansi_to_wtype(conn, SQL_C_CHAR); case PG_TYPE_UUID: if (!conn->ms_jet) return SQL_C_GUID; return ansi_to_wtype(conn, SQL_C_CHAR); case PG_TYPE_INTERVAL: #ifdef PG_INTERVAL_AS_SQL_INTERVAL if (ctype = get_interval_type(atttypmod, NULL), 0 != ctype) return ctype; #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ return ansi_to_wtype(conn, SQL_CHAR); default: /* hack until permanent type is available */ if (type == conn->lobj_type) return SQL_C_BINARY; /* Experimental, Does this work ? */ #ifdef EXPERIMENTAL_CURRENTLY return ansi_to_wtype(conn, SQL_C_CHAR); #endif /* EXPERIMENTAL_CURRENTLY */ return SQL_C_CHAR; } } const char * pgtype_attr_to_name(const ConnectionClass *conn, OID type, int atttypmod, BOOL auto_increment) { const char *tname = NULL; switch (type) { case PG_TYPE_CHAR: return "char"; case PG_TYPE_INT8: return auto_increment ? "bigserial" : "int8"; case PG_TYPE_NUMERIC: return "numeric"; case PG_TYPE_VARCHAR: return "varchar"; case PG_TYPE_BPCHAR: return "char"; case PG_TYPE_TEXT: return "text"; case PG_TYPE_NAME: return "name"; case PG_TYPE_REFCURSOR: return "refcursor"; case PG_TYPE_INT2: return "int2"; case PG_TYPE_OID: return OID_NAME; case PG_TYPE_XID: return "xid"; case PG_TYPE_INT4: MYLOG(DETAIL_LOG_LEVEL, "pgtype_to_name int4\n"); return auto_increment ? "serial" : "int4"; case PG_TYPE_FLOAT4: return "float4"; case PG_TYPE_FLOAT8: return "float8"; case PG_TYPE_DATE: return "date"; case PG_TYPE_TIME: return "time"; case PG_TYPE_ABSTIME: return "abstime"; case PG_TYPE_DATETIME: return "timestamptz"; case PG_TYPE_TIMESTAMP_NO_TMZONE: return "timestamp without time zone"; case PG_TYPE_TIMESTAMP: return "timestamp"; case PG_TYPE_MONEY: return "money"; case PG_TYPE_BOOL: return "bool"; case PG_TYPE_BYTEA: return "bytea"; case PG_TYPE_XML: return "xml"; case PG_TYPE_MACADDR: return "macaddr"; case PG_TYPE_INET: return "inet"; case PG_TYPE_CIDR: return "cidr"; case PG_TYPE_UUID: return "uuid"; case PG_TYPE_VOID: return "void"; case PG_TYPE_INT2VECTOR: return "int2vector"; case PG_TYPE_OIDVECTOR: return "oidvector"; case PG_TYPE_ANY: return "any"; case PG_TYPE_INTERVAL: get_interval_type(atttypmod, &tname); return tname; case PG_TYPE_LO_UNDEFINED: return PG_TYPE_LO_NAME; default: /* hack until permanent type is available */ if (type == conn->lobj_type) return PG_TYPE_LO_NAME; /* * "unknown" can actually be used in alter table because it is * a real PG type! */ return "unknown"; } } Int4 /* PostgreSQL restriction */ pgtype_attr_column_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) { const ConnInfo *ci = &(conn->connInfo); MYLOG(MIN_LOG_LEVEL, "entering type=%d, atttypmod=%d, adtsize_or=%d, unknown = %d\n", type, atttypmod, adtsize_or_longest, handle_unknown_size_as); switch (type) { case PG_TYPE_CHAR: return 1; case PG_TYPE_NAME: case PG_TYPE_REFCURSOR: { int value = 0; if (PG_VERSION_GT(conn, 7.4)) { /* * 'conn' argument is marked as const, * because this function just reads * stuff from the already-filled in * fields in the struct. But this is * an exception: CC_get_max_idlen() can * send a SHOW query to the backend to * get the identifier length. Thus cast * away the const here. */ value = CC_get_max_idlen((ConnectionClass *) conn); } #ifdef NAME_FIELD_SIZE else value = NAME_FIELD_SIZE; #endif /* NAME_FIELD_SIZE */ if (0 == value) value = NAMEDATALEN_V73; return value; } case PG_TYPE_INT2: return 5; case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: return 10; case PG_TYPE_INT8: return 19; /* signed */ case PG_TYPE_NUMERIC: return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); case PG_TYPE_MONEY: return 10; case PG_TYPE_FLOAT4: return PG_REAL_DIGITS; case PG_TYPE_FLOAT8: return PG_DOUBLE_DIGITS; case PG_TYPE_DATE: return 10; case PG_TYPE_TIME: return 8; case PG_TYPE_ABSTIME: case PG_TYPE_TIMESTAMP: return 22; case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: /* return 22; */ return getTimestampColumnSizeX(conn, type, atttypmod); case PG_TYPE_BOOL: return ci->drivers.bools_as_char ? PG_WIDTH_OF_BOOLS_AS_CHAR : 1; case PG_TYPE_MACADDR: return 17; case PG_TYPE_INET: case PG_TYPE_CIDR: return sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128"); case PG_TYPE_UUID: return sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); case PG_TYPE_LO_UNDEFINED: return SQL_NO_TOTAL; case PG_TYPE_INTERVAL: return getIntervalColumnSize(type, atttypmod); default: if (type == conn->lobj_type) /* hack until permanent * type is available */ return SQL_NO_TOTAL; if (PG_TYPE_BYTEA == type && ci->bytea_as_longvarbinary) return SQL_NO_TOTAL; /* Handle Character types and unknown types */ return getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); } } SQLSMALLINT pgtype_attr_precision(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) { switch (type) { case PG_TYPE_NUMERIC: return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); case PG_TYPE_TIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: return getTimestampDecimalDigitsX(conn, type, atttypmod); #ifdef PG_INTERVAL_AS_SQL_INTERVAL case PG_TYPE_INTERVAL: return getIntervalDecimalDigits(type, atttypmod); #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ } return -1; } Int4 pgtype_attr_display_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { int dsize; switch (type) { case PG_TYPE_INT2: return 6; case PG_TYPE_OID: case PG_TYPE_XID: return 10; case PG_TYPE_INT4: return 11; case PG_TYPE_INT8: return 20; /* signed: 19 digits + sign */ case PG_TYPE_NUMERIC: dsize = getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); return dsize <= 0 ? dsize : dsize + 2; case PG_TYPE_MONEY: return 15; /* ($9,999,999.99) */ case PG_TYPE_FLOAT4: /* a sign, PG_REAL_DIGITS digits, a decimal point, the letter E, a sign, and 2 digits */ return (1 + PG_REAL_DIGITS + 1 + 1 + 3); case PG_TYPE_FLOAT8: /* a sign, PG_DOUBLE_DIGITS digits, a decimal point, the letter E, a sign, and 3 digits */ return (1 + PG_DOUBLE_DIGITS + 1 + 1 + 1 + 3); case PG_TYPE_MACADDR: return 17; case PG_TYPE_INET: case PG_TYPE_CIDR: return sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128"); case PG_TYPE_UUID: return 36; case PG_TYPE_INTERVAL: return 30; /* Character types use regular precision */ default: return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); } } Int4 pgtype_attr_buffer_length(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { int dsize; switch (type) { case PG_TYPE_INT2: return 2; /* sizeof(SQLSMALLINT) */ case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: return 4; /* sizeof(SQLINTEGER) */ case PG_TYPE_INT8: if (SQL_C_CHAR == pgtype_attr_to_ctype(conn, type, atttypmod)) return 20; /* signed: 19 digits + sign */ return 8; /* sizeof(SQLSBININT) */ case PG_TYPE_NUMERIC: dsize = getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); return dsize <= 0 ? dsize : dsize + 2; case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: return 4; /* sizeof(SQLREAL) */ case PG_TYPE_FLOAT8: return 8; /* sizeof(SQLFLOAT) */ case PG_TYPE_DATE: case PG_TYPE_TIME: return 6; /* sizeof(DATE(TIME)_STRUCT) */ case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: case PG_TYPE_TIMESTAMP_NO_TMZONE: return 16; /* sizeof(TIMESTAMP_STRUCT) */ case PG_TYPE_MACADDR: return 17; case PG_TYPE_INET: case PG_TYPE_CIDR: return sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128"); case PG_TYPE_UUID: return 16; /* sizeof(SQLGUID) */ /* Character types use the default precision */ case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: { int coef = 1; Int4 prec = pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as), maxvarc; if (SQL_NO_TOTAL == prec) return prec; #ifdef UNICODE_SUPPORT if (CC_is_in_unicode_driver(conn)) return prec * WCLEN; #endif /* UNICODE_SUPPORT */ coef = conn->mb_maxbyte_per_char; if (coef < 2 && (conn->connInfo).lf_conversion) /* CR -> CR/LF */ coef = 2; if (coef == 1) return prec; maxvarc = conn->connInfo.drivers.max_varchar_size; if (prec <= maxvarc && prec * coef > maxvarc) return maxvarc; return coef * prec; } #ifdef PG_INTERVAL_AS_SQL_INTERVAL case PG_TYPE_INTERVAL: return sizeof(SQL_INTERVAL_STRUCT); #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ default: return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); } } /* */ Int4 pgtype_attr_desclength(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) { int dsize; switch (type) { case PG_TYPE_INT2: return 2; case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: return 4; case PG_TYPE_INT8: return 20; /* signed: 19 digits + sign */ case PG_TYPE_NUMERIC: dsize = getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); return dsize <= 0 ? dsize : dsize + 2; case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: return 4; case PG_TYPE_FLOAT8: return 8; case PG_TYPE_DATE: case PG_TYPE_TIME: case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: case PG_TYPE_TIMESTAMP: case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); default: return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); } } Int2 pgtype_attr_decimal_digits(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int UNUSED_handle_unknown_size_as) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: case PG_TYPE_BOOL: /* * Number of digits to the right of the decimal point in * "yyyy-mm=dd hh:mm:ss[.f...]" */ case PG_TYPE_ABSTIME: case PG_TYPE_TIMESTAMP: return 0; case PG_TYPE_TIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: /* return 0; */ return getTimestampDecimalDigitsX(conn, type, atttypmod); case PG_TYPE_NUMERIC: return getNumericDecimalDigitsX(conn, type, atttypmod, adtsize_or_longestlen, UNUSED_handle_unknown_size_as); #ifdef PG_INTERVAL_AS_SQL_INTERVAL case PG_TYPE_INTERVAL: return getIntervalDecimalDigits(type, atttypmod); #endif /* PG_INTERVAL_AS_SQL_INTERVAL */ default: return -1; } } Int2 pgtype_attr_scale(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int UNUSED_handle_unknown_size_as) { switch (type) { case PG_TYPE_NUMERIC: return getNumericDecimalDigitsX(conn, type, atttypmod, adtsize_or_longestlen, UNUSED_handle_unknown_size_as); } return -1; } Int4 pgtype_attr_transfer_octet_length(const ConnectionClass *conn, OID type, int atttypmod, int handle_unknown_size_as) { int coef = 1; Int4 maxvarc, column_size; switch (type) { case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_UNKNOWN: column_size = pgtype_attr_column_size(conn, type, atttypmod, PG_ADT_UNSET, handle_unknown_size_as); if (SQL_NO_TOTAL == column_size) return column_size; #ifdef UNICODE_SUPPORT if (CC_is_in_unicode_driver(conn)) return column_size * WCLEN; #endif /* UNICODE_SUPPORT */ coef = conn->mb_maxbyte_per_char; if (coef < 2 && (conn->connInfo).lf_conversion) /* CR -> CR/LF */ coef = 2; if (coef == 1) return column_size; maxvarc = conn->connInfo.drivers.max_varchar_size; if (column_size <= maxvarc && column_size * coef > maxvarc) return maxvarc; return coef * column_size; case PG_TYPE_BYTEA: return pgtype_attr_column_size(conn, type, atttypmod, PG_ADT_UNSET, handle_unknown_size_as); default: if (type == conn->lobj_type) return pgtype_attr_column_size(conn, type, atttypmod, PG_ADT_UNSET, handle_unknown_size_as); } return -1; } /* * This was used when binding a query parameter, to decide which PostgreSQL * datatype to send to the server, depending on the SQL datatype that was * used in the SQLBindParameter call. * * However it is inflexible and rather harmful. * Now this function always returns 0. * We use cast instead to keep regression test results * in order to keep regression test results. */ OID sqltype_to_bind_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType) { OID pgType = 0; return pgType; } /* * Casting parameters e.g. ?::timestamp is much more flexible * than specifying parameter datatype oids determined by * sqltype_to_bind_pgtype() via parse message. */ const char * sqltype_to_pgcast(const ConnectionClass *conn, SQLSMALLINT fSqlType) { const char *pgCast = NULL_STRING; switch (fSqlType) { case SQL_BINARY: case SQL_VARBINARY: pgCast = "::bytea"; break; case SQL_TYPE_DATE: case SQL_DATE: pgCast = "::date"; break; case SQL_DECIMAL: case SQL_NUMERIC: pgCast = "::numeric"; break; case SQL_BIGINT: pgCast = "::int8"; break; case SQL_INTEGER: pgCast = "::int4"; break; case SQL_REAL: pgCast = "::float4"; break; case SQL_SMALLINT: case SQL_TINYINT: pgCast = "::int2"; break; case SQL_TIME: case SQL_TYPE_TIME: pgCast = "::time"; break; case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: pgCast = "::timestamp"; break; case SQL_GUID: if (PG_VERSION_GE(conn, 8.3)) pgCast = "::uuid"; break; case SQL_INTERVAL_MONTH: case SQL_INTERVAL_YEAR: case SQL_INTERVAL_YEAR_TO_MONTH: case SQL_INTERVAL_DAY: case SQL_INTERVAL_HOUR: case SQL_INTERVAL_MINUTE: case SQL_INTERVAL_SECOND: case SQL_INTERVAL_DAY_TO_HOUR: case SQL_INTERVAL_DAY_TO_MINUTE: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_HOUR_TO_MINUTE: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_MINUTE_TO_SECOND: pgCast = "::interval"; break; } return pgCast; } OID sqltype_to_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType) { OID pgType; const ConnInfo *ci = &(conn->connInfo); pgType = 0; /* ??? */ switch (fSqlType) { case SQL_BINARY: pgType = PG_TYPE_BYTEA; break; case SQL_CHAR: pgType = PG_TYPE_BPCHAR; break; #ifdef UNICODE_SUPPORT case SQL_WCHAR: pgType = PG_TYPE_BPCHAR; break; #endif /* UNICODE_SUPPORT */ case SQL_BIT: pgType = PG_TYPE_BOOL; break; case SQL_TYPE_DATE: case SQL_DATE: pgType = PG_TYPE_DATE; break; case SQL_DOUBLE: case SQL_FLOAT: pgType = PG_TYPE_FLOAT8; break; case SQL_DECIMAL: case SQL_NUMERIC: pgType = PG_TYPE_NUMERIC; break; case SQL_BIGINT: pgType = PG_TYPE_INT8; break; case SQL_INTEGER: pgType = PG_TYPE_INT4; break; case SQL_LONGVARBINARY: if (ci->bytea_as_longvarbinary) pgType = PG_TYPE_BYTEA; else pgType = conn->lobj_type; break; case SQL_LONGVARCHAR: pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; break; #ifdef UNICODE_SUPPORT case SQL_WLONGVARCHAR: pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; break; #endif /* UNICODE_SUPPORT */ case SQL_REAL: pgType = PG_TYPE_FLOAT4; break; case SQL_SMALLINT: case SQL_TINYINT: pgType = PG_TYPE_INT2; break; case SQL_TIME: case SQL_TYPE_TIME: pgType = PG_TYPE_TIME; break; case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: pgType = PG_TYPE_DATETIME; break; case SQL_VARBINARY: pgType = PG_TYPE_BYTEA; break; case SQL_VARCHAR: pgType = PG_TYPE_VARCHAR; break; #ifdef UNICODE_SUPPORT case SQL_WVARCHAR: pgType = PG_TYPE_VARCHAR; break; #endif /* UNICODE_SUPPORT */ case SQL_GUID: if (PG_VERSION_GE(conn, 8.3)) pgType = PG_TYPE_UUID; break; case SQL_INTERVAL_MONTH: case SQL_INTERVAL_YEAR: case SQL_INTERVAL_YEAR_TO_MONTH: case SQL_INTERVAL_DAY: case SQL_INTERVAL_HOUR: case SQL_INTERVAL_MINUTE: case SQL_INTERVAL_SECOND: case SQL_INTERVAL_DAY_TO_HOUR: case SQL_INTERVAL_DAY_TO_MINUTE: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_HOUR_TO_MINUTE: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_MINUTE_TO_SECOND: pgType = PG_TYPE_INTERVAL; break; } return pgType; } static int getAtttypmodEtc(const StatementClass *stmt, int col, int *adtsize_or_longestlen) { int atttypmod = -1; if (NULL != adtsize_or_longestlen) *adtsize_or_longestlen = PG_ADT_UNSET; if (col >= 0) { const QResultClass *res; if (res = SC_get_ExecdOrParsed(stmt), NULL != res) { atttypmod = QR_get_atttypmod(res, col); if (NULL != adtsize_or_longestlen) { if (stmt->catalog_result) *adtsize_or_longestlen = QR_get_fieldsize(res, col); else { *adtsize_or_longestlen = QR_get_display_size(res, col); if (PG_TYPE_NUMERIC == QR_get_field_type(res, col) && atttypmod < 0 && *adtsize_or_longestlen > 0) { SQLULEN i; size_t sval, maxscale = 0; const char *tval, *sptr; for (i = 0; i < res->num_cached_rows; i++) { tval = QR_get_value_backend_text(res, i, col); if (NULL != tval) { sptr = strchr(tval, '.'); if (NULL != sptr) { sval = strlen(tval) - (sptr + 1 - tval); if (sval > maxscale) maxscale = sval; } } } *adtsize_or_longestlen += (int) (maxscale << 16); } } } } } return atttypmod; } /* * There are two ways of calling this function: * * 1. When going through the supported PG types (SQLGetTypeInfo) * * 2. When taking any type id (SQLColumns, SQLGetData) * * The first type will always work because all the types defined are returned here. * The second type will return a default based on global parameter when it does not * know. This allows for supporting * types that are unknown. All other pg routines in here return a suitable default. */ SQLSMALLINT pgtype_to_concise_type(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_to_concise_type(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); } SQLSMALLINT pgtype_to_sqldesctype(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int adtsize_or_longestlen; int atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_to_sqldesctype(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); } SQLSMALLINT pgtype_to_datetime_sub(const StatementClass *stmt, OID type, int col) { int atttypmod = getAtttypmodEtc(stmt, col, NULL); return pgtype_attr_to_datetime_sub(SC_get_conn(stmt), type, atttypmod); } const char * pgtype_to_name(const StatementClass *stmt, OID type, int col, BOOL auto_increment) { int atttypmod = getAtttypmodEtc(stmt, col, NULL); return pgtype_attr_to_name(SC_get_conn(stmt), type, atttypmod, auto_increment); } /* * This corresponds to "precision" in ODBC 2.x. * * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will * override this length with the atttypmod length from pg_attribute . * * If col >= 0, then will attempt to get the info from the result set. * This is used for functions SQLDescribeCol and SQLColAttributes. */ Int4 /* PostgreSQL restriction */ pgtype_column_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_column_size(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_LONGEST : handle_unknown_size_as); } /* * precision in ODBC 3.x. */ SQLSMALLINT pgtype_precision(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_precision(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_LONGEST : handle_unknown_size_as); } Int4 pgtype_display_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_display_size(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_LONGEST : handle_unknown_size_as); } /* * The length in bytes of data transferred on an SQLGetData, SQLFetch, * or SQLFetchScroll operation if SQL_C_DEFAULT is specified. */ Int4 pgtype_buffer_length(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_buffer_length(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_LONGEST : handle_unknown_size_as); } /* */ Int4 pgtype_desclength(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_desclength(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_LONGEST : handle_unknown_size_as); } #ifdef NOT_USED /* * Transfer octet length. */ Int4 pgtype_transfer_octet_length(const StatementClass *stmt, OID type, int column_size) { ConnectionClass *conn = SC_get_conn(stmt); int coef = 1; Int4 maxvarc; switch (type) { case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: if (SQL_NO_TOTAL == column_size) return column_size; #ifdef UNICODE_SUPPORT if (CC_is_in_unicode_driver(conn)) return column_size * WCLEN; #endif /* UNICODE_SUPPORT */ coef = conn->mb_maxbyte_per_char; if (coef < 2 && (conn->connInfo).lf_conversion) /* CR -> CR/LF */ coef = 2; if (coef == 1) return column_size; maxvarc = conn->connInfo.drivers.max_varchar_size; if (column_size <= maxvarc && column_size * coef > maxvarc) return maxvarc; return coef * column_size; case PG_TYPE_BYTEA: return column_size; default: if (type == conn->lobj_type) return column_size; } return -1; } #endif /* NOT_USED */ /* * corresponds to "min_scale" in ODBC 2.x. */ Int2 pgtype_min_decimal_digits(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: case PG_TYPE_BOOL: case PG_TYPE_ABSTIME: case PG_TYPE_TIMESTAMP: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: case PG_TYPE_NUMERIC: return 0; default: return -1; } } /* * corresponds to "max_scale" in ODBC 2.x. */ Int2 pgtype_max_decimal_digits(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: case PG_TYPE_BOOL: case PG_TYPE_ABSTIME: case PG_TYPE_TIMESTAMP: return 0; case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: return 38; case PG_TYPE_NUMERIC: return getNumericDecimalDigitsX(conn, type, -1, -1, UNUSED_HANDLE_UNKNOWN_SIZE_AS); default: return -1; } } /* * corresponds to "scale" in ODBC 2.x. */ Int2 pgtype_decimal_digits(const StatementClass *stmt, OID type, int col) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_decimal_digits(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, UNUSED_HANDLE_UNKNOWN_SIZE_AS); } /* * "scale" in ODBC 3.x. */ Int2 pgtype_scale(const StatementClass *stmt, OID type, int col) { int atttypmod, adtsize_or_longestlen; atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); return pgtype_attr_scale(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, UNUSED_HANDLE_UNKNOWN_SIZE_AS); } Int2 pgtype_radix(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_XID: case PG_TYPE_OID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_NUMERIC: case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: case PG_TYPE_FLOAT8: return 10; default: return -1; } } Int2 pgtype_nullable(const ConnectionClass *conn, OID type) { return SQL_NULLABLE; /* everything should be nullable */ } Int2 pgtype_auto_increment(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: case PG_TYPE_BOOL: case PG_TYPE_FLOAT8: case PG_TYPE_INT8: case PG_TYPE_NUMERIC: case PG_TYPE_DATE: case PG_TYPE_TIME_WITH_TMZONE: case PG_TYPE_TIME: case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP_NO_TMZONE: case PG_TYPE_TIMESTAMP: return FALSE; default: return -1; } } Int2 pgtype_case_sensitive(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_CHAR: case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: case PG_TYPE_REFCURSOR: return TRUE; default: return FALSE; } } Int2 pgtype_money(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_MONEY: return TRUE; default: return FALSE; } } Int2 pgtype_searchable(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_CHAR: case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: case PG_TYPE_REFCURSOR: return SQL_SEARCHABLE; default: if (conn && type == conn->lobj_type) return SQL_UNSEARCHABLE; return SQL_ALL_EXCEPT_LIKE; } } Int2 pgtype_unsigned(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_OID: case PG_TYPE_XID: return TRUE; case PG_TYPE_INT2: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_NUMERIC: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: return FALSE; default: return -1; } } const char * pgtype_literal_prefix(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_NUMERIC: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: return NULL; default: return "'"; } } const char * pgtype_literal_suffix(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_INT2: case PG_TYPE_OID: case PG_TYPE_XID: case PG_TYPE_INT4: case PG_TYPE_INT8: case PG_TYPE_NUMERIC: case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_MONEY: return NULL; default: return "'"; } } const char * pgtype_create_params(const ConnectionClass *conn, OID type) { switch (type) { case PG_TYPE_BPCHAR: case PG_TYPE_VARCHAR: return "max. length"; case PG_TYPE_NUMERIC: return "precision, scale"; default: return NULL; } } SQLSMALLINT sqltype_to_default_ctype(const ConnectionClass *conn, SQLSMALLINT sqltype) { /* * from the table on page 623 of ODBC 2.0 Programmer's Reference * (Appendix D) */ switch (sqltype) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_DECIMAL: case SQL_NUMERIC: return SQL_C_CHAR; case SQL_BIGINT: return ALLOWED_C_BIGINT; #ifdef UNICODE_SUPPORT case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: return ansi_to_wtype(conn, SQL_C_CHAR); #endif /* UNICODE_SUPPORT */ case SQL_BIT: return SQL_C_BIT; case SQL_TINYINT: return SQL_C_STINYINT; case SQL_SMALLINT: return SQL_C_SSHORT; case SQL_INTEGER: return SQL_C_SLONG; case SQL_REAL: return SQL_C_FLOAT; case SQL_FLOAT: case SQL_DOUBLE: return SQL_C_DOUBLE; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return SQL_C_BINARY; case SQL_DATE: return SQL_C_DATE; case SQL_TIME: return SQL_C_TIME; case SQL_TIMESTAMP: return SQL_C_TIMESTAMP; case SQL_TYPE_DATE: return SQL_C_TYPE_DATE; case SQL_TYPE_TIME: return SQL_C_TYPE_TIME; case SQL_TYPE_TIMESTAMP: return SQL_C_TYPE_TIMESTAMP; case SQL_GUID: if (conn->ms_jet) return SQL_C_CHAR; else return SQL_C_GUID; default: /* should never happen */ return SQL_C_CHAR; } } Int4 ctype_length(SQLSMALLINT ctype) { switch (ctype) { case SQL_C_SSHORT: case SQL_C_SHORT: return sizeof(SWORD); case SQL_C_USHORT: return sizeof(UWORD); case SQL_C_SLONG: case SQL_C_LONG: return sizeof(SDWORD); case SQL_C_ULONG: return sizeof(UDWORD); case SQL_C_FLOAT: return sizeof(SFLOAT); case SQL_C_DOUBLE: return sizeof(SDOUBLE); case SQL_C_BIT: return sizeof(UCHAR); case SQL_C_STINYINT: case SQL_C_TINYINT: return sizeof(SCHAR); case SQL_C_UTINYINT: return sizeof(UCHAR); case SQL_C_DATE: case SQL_C_TYPE_DATE: return sizeof(DATE_STRUCT); case SQL_C_TIME: case SQL_C_TYPE_TIME: return sizeof(TIME_STRUCT); case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: return sizeof(TIMESTAMP_STRUCT); case SQL_C_GUID: return sizeof(SQLGUID); case SQL_C_INTERVAL_YEAR: case SQL_C_INTERVAL_MONTH: case SQL_C_INTERVAL_YEAR_TO_MONTH: case SQL_C_INTERVAL_DAY: case SQL_C_INTERVAL_HOUR: case SQL_C_INTERVAL_DAY_TO_HOUR: case SQL_C_INTERVAL_MINUTE: case SQL_C_INTERVAL_DAY_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_SECOND: case SQL_C_INTERVAL_DAY_TO_SECOND: case SQL_C_INTERVAL_HOUR_TO_SECOND: case SQL_C_INTERVAL_MINUTE_TO_SECOND: return sizeof(SQL_INTERVAL_STRUCT); case SQL_C_NUMERIC: return sizeof(SQL_NUMERIC_STRUCT); case SQL_C_SBIGINT: case SQL_C_UBIGINT: return sizeof(SQLBIGINT); case SQL_C_BINARY: case SQL_C_CHAR: #ifdef UNICODE_SUPPORT case SQL_C_WCHAR: #endif /* UNICODE_SUPPORT */ return 0; default: /* should never happen */ return 0; } }