/*-------- * 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 "notice.txt" for copyright and license information. *-------- */ #include "pgtypes.h" #include "dlg_specific.h" #include "statement.h" #include "connection.h" #include "qresult.h" Int4 getCharPrecision(StatementClass *stmt, Int4 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 */ /* Int4 pgtypes_defined[] = { PG_TYPE_CHAR, PG_TYPE_CHAR2, PG_TYPE_CHAR4, PG_TYPE_CHAR8, PG_TYPE_CHAR16, PG_TYPE_NAME, PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_DATE, PG_TYPE_TIME, PG_TYPE_DATETIME, PG_TYPE_ABSTIME, PG_TYPE_TIMESTAMP, PG_TYPE_TEXT, PG_TYPE_INT2, PG_TYPE_INT4, PG_TYPE_FLOAT4, PG_TYPE_FLOAT8, PG_TYPE_OID, PG_TYPE_MONEY, PG_TYPE_BOOL, PG_TYPE_BYTEA, PG_TYPE_LO, 0 }; */ /* These are NOW the SQL Types reported in SQLGetTypeInfo. */ Int2 sqlTypes[] = { SQL_BIGINT, /* SQL_BINARY, -- Commented out because VarBinary is more correct. */ SQL_BIT, SQL_CHAR, SQL_DATE, SQL_DECIMAL, SQL_DOUBLE, SQL_FLOAT, SQL_INTEGER, SQL_LONGVARBINARY, SQL_LONGVARCHAR, SQL_NUMERIC, SQL_REAL, SQL_SMALLINT, SQL_TIME, SQL_TIMESTAMP, SQL_TINYINT, SQL_VARBINARY, SQL_VARCHAR, 0 }; Int4 sqltype_to_pgtype(StatementClass *stmt, SWORD fSqlType) { Int4 pgType; ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); switch (fSqlType) { case SQL_BINARY: pgType = PG_TYPE_BYTEA; break; case SQL_CHAR: pgType = PG_TYPE_BPCHAR; break; case SQL_BIT: pgType = ci->drivers.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL; break; 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: pgType = PG_TYPE_LO; break; case SQL_LONGVARCHAR: pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; break; case SQL_REAL: pgType = PG_TYPE_FLOAT4; break; case SQL_SMALLINT: case SQL_TINYINT: pgType = PG_TYPE_INT2; break; case SQL_TIME: pgType = PG_TYPE_TIME; break; case SQL_TIMESTAMP: pgType = PG_TYPE_DATETIME; break; case SQL_VARBINARY: pgType = PG_TYPE_BYTEA; break; case SQL_VARCHAR: pgType = PG_TYPE_VARCHAR; break; default: pgType = 0; /* ??? */ break; } return pgType; } /* * 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. */ Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type) { ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); switch (type) { case PG_TYPE_CHAR: case PG_TYPE_CHAR2: case PG_TYPE_CHAR4: case PG_TYPE_CHAR8: case PG_TYPE_NAME: return SQL_CHAR; case PG_TYPE_BPCHAR: return SQL_CHAR; case PG_TYPE_VARCHAR: return SQL_VARCHAR; case PG_TYPE_TEXT: return ci->drivers.text_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR; case PG_TYPE_BYTEA: return SQL_VARBINARY; case PG_TYPE_LO: 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: return SQL_CHAR; case PG_TYPE_NUMERIC: return SQL_NUMERIC; case PG_TYPE_FLOAT4: return SQL_REAL; case PG_TYPE_FLOAT8: return SQL_FLOAT; case PG_TYPE_DATE: return SQL_DATE; case PG_TYPE_TIME: return SQL_TIME; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: return SQL_TIMESTAMP; case PG_TYPE_MONEY: return SQL_FLOAT; case PG_TYPE_BOOL: return ci->drivers.bools_as_char ? SQL_CHAR : SQL_BIT; 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 == stmt->hdbc->lobj_type) return SQL_LONGVARBINARY; return ci->drivers.unknowns_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR; } } Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type) { ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); switch (type) { case PG_TYPE_INT8: 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: 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: return SQL_C_DATE; case PG_TYPE_TIME: return SQL_C_TIME; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_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: return SQL_C_BINARY; default: /* hack until permanent type is available */ if (type == stmt->hdbc->lobj_type) return SQL_C_BINARY; return SQL_C_CHAR; } } char * pgtype_to_name(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_CHAR: return "char"; case PG_TYPE_CHAR2: return "char2"; case PG_TYPE_CHAR4: return "char4"; case PG_TYPE_CHAR8: return "char8"; case PG_TYPE_INT8: return "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_INT2: return "int2"; case PG_TYPE_OID: return "oid"; case PG_TYPE_INT4: return "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 "datetime"; 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_LO: return PG_TYPE_LO_NAME; default: /* hack until permanent type is available */ if (type == stmt->hdbc->lobj_type) return PG_TYPE_LO_NAME; /* * "unknown" can actually be used in alter table because it is * a real PG type! */ return "unknown"; } } static Int2 getNumericScale(StatementClass *stmt, Int4 type, int col) { Int4 atttypmod; QResultClass *result; ColumnInfoClass *flds; mylog("getNumericScale: type=%d, col=%d\n", type, col); if (col < 0) return PG_NUMERIC_MAX_SCALE; result = SC_get_Result(stmt); /* * Manual Result Sets -- use assigned column width (i.e., from * set_tuplefield_string) */ if (stmt->manual_result) { flds = result->fields; if (flds) return flds->adtsize[col]; else return PG_NUMERIC_MAX_SCALE; } atttypmod = QR_get_atttypmod(result, col); if (atttypmod > -1) return (atttypmod & 0xffff); else return (QR_get_display_size(result, col) ? QR_get_display_size(result, col) : PG_NUMERIC_MAX_SCALE); } static Int4 getNumericPrecision(StatementClass *stmt, Int4 type, int col) { Int4 atttypmod; QResultClass *result; ColumnInfoClass *flds; mylog("getNumericPrecision: type=%d, col=%d\n", type, col); if (col < 0) return PG_NUMERIC_MAX_PRECISION; result = SC_get_Result(stmt); /* * Manual Result Sets -- use assigned column width (i.e., from * set_tuplefield_string) */ if (stmt->manual_result) { flds = result->fields; if (flds) return flds->adtsize[col]; else return PG_NUMERIC_MAX_PRECISION; } atttypmod = QR_get_atttypmod(result, col); if (atttypmod > -1) return (atttypmod >> 16) & 0xffff; else return (QR_get_display_size(result, col) >= 0 ? QR_get_display_size(result, col) : PG_NUMERIC_MAX_PRECISION); } Int4 getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { int p = -1, maxsize; QResultClass *result; ColumnInfoClass *flds; ConnInfo *ci = &(SC_get_conn(stmt)->connInfo); mylog("getCharPrecision: type=%d, col=%d, unknown = %d\n", type, col, 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; } /* * Static Precision (i.e., the Maximum Precision of the datatype) This * has nothing to do with a result set. */ if (maxsize == TEXT_FIELD_SIZE + 1) /* magic length for testing */ { if (PG_VERSION_GE(SC_get_conn(stmt), 7.1)) maxsize = 0; else maxsize = TEXT_FIELD_SIZE; } if (col < 0) return maxsize; result = SC_get_Result(stmt); /* * Manual Result Sets -- use assigned column width (i.e., from * set_tuplefield_string) */ if (stmt->manual_result) { flds = result->fields; if (flds) return flds->adtsize[col]; else return maxsize; } /* Size is unknown -- handle according to parameter */ if (QR_get_atttypmod(result, col) > -1) return QR_get_atttypmod(result, col); if (type == PG_TYPE_BPCHAR || handle_unknown_size_as == UNKNOWNS_AS_LONGEST) { p = QR_get_display_size(result, col); mylog("getCharPrecision: LONGEST: p = %d\n", p); } if (p < 0 && handle_unknown_size_as == UNKNOWNS_AS_MAX) return maxsize; else return p; } /* * 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 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { switch (type) { case PG_TYPE_CHAR: return 1; case PG_TYPE_CHAR2: return 2; case PG_TYPE_CHAR4: return 4; case PG_TYPE_CHAR8: return 8; case PG_TYPE_NAME: return NAME_FIELD_SIZE; 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 getNumericPrecision(stmt, type, col); case PG_TYPE_FLOAT4: case PG_TYPE_MONEY: return 7; case PG_TYPE_FLOAT8: return 15; case PG_TYPE_DATE: return 10; case PG_TYPE_TIME: return 8; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: /*return 19;*/ return 21; case PG_TYPE_BOOL: return 1; case PG_TYPE_LO: return SQL_NO_TOTAL; default: if (type == stmt->hdbc->lobj_type) /* hack until permanent * type is available */ return SQL_NO_TOTAL; /* Handle Character types and unknown types */ return getCharPrecision(stmt, type, col, handle_unknown_size_as); } } Int4 pgtype_display_size(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { 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: return getNumericPrecision(stmt, type, col) + 2; case PG_TYPE_MONEY: return 15; /* ($9,999,999.99) */ case PG_TYPE_FLOAT4: return 13; case PG_TYPE_FLOAT8: return 22; /* Character types use regular precision */ default: return pgtype_precision(stmt, type, col, handle_unknown_size_as); } } /* * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, SQLColumns will * override this length with the atttypmod length from pg_attribute */ Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_size_as) { 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: return getNumericPrecision(stmt, type, col) + 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: return 6; case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: return 16; /* Character types (and NUMERIC) use the default precision */ case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: #ifdef MULTIBYTE /* after 7.2 */ if (PG_VERSION_GE(SC_get_conn(stmt), 7.2)) return 3 * pgtype_precision(stmt, type, col, handle_unknown_size_as); else #else /* CR -> CR/LF */ return 2 * pgtype_precision(stmt, type, col, handle_unknown_size_as); #endif /* MULTIBYTE */ default: return pgtype_precision(stmt, type, col, handle_unknown_size_as); } } Int2 pgtype_scale(StatementClass *stmt, Int4 type, int col) { 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_DATETIME: case PG_TYPE_TIMESTAMP: return 0; case PG_TYPE_NUMERIC: return getNumericScale(stmt, type, col); default: return -1; } } Int2 pgtype_radix(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_INT2: 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(StatementClass *stmt, Int4 type) { return SQL_NULLABLE; /* everything should be nullable */ } Int2 pgtype_auto_increment(StatementClass *stmt, Int4 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: case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: return FALSE; default: return -1; } } Int2 pgtype_case_sensitive(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_CHAR: case PG_TYPE_CHAR2: case PG_TYPE_CHAR4: case PG_TYPE_CHAR8: case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: return TRUE; default: return FALSE; } } Int2 pgtype_money(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_MONEY: return TRUE; default: return FALSE; } } Int2 pgtype_searchable(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_CHAR: case PG_TYPE_CHAR2: case PG_TYPE_CHAR4: case PG_TYPE_CHAR8: case PG_TYPE_VARCHAR: case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: return SQL_SEARCHABLE; default: return SQL_ALL_EXCEPT_LIKE; } } Int2 pgtype_unsigned(StatementClass *stmt, Int4 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; } } char * pgtype_literal_prefix(StatementClass *stmt, Int4 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 "'"; } } char * pgtype_literal_suffix(StatementClass *stmt, Int4 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 "'"; } } char * pgtype_create_params(StatementClass *stmt, Int4 type) { switch (type) { case PG_TYPE_CHAR: case PG_TYPE_VARCHAR:return "max. length"; default: return NULL; } } Int2 sqltype_to_default_ctype(Int2 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: case SQL_BIGINT: return SQL_C_CHAR; 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; default: /* should never happen */ return SQL_C_CHAR; } } Int4 ctype_length(Int2 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: return sizeof(DATE_STRUCT); case SQL_C_TIME: return sizeof(TIME_STRUCT); case SQL_C_TIMESTAMP: return sizeof(TIMESTAMP_STRUCT); case SQL_C_BINARY: case SQL_C_CHAR: return 0; default: /* should never happen */ return 0; } }