Index: fe-connect.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.180 diff -c -p -r1.180 fe-connect.c *** fe-connect.c 2001/11/05 17:46:37 1.180 --- fe-connect.c 2001/11/07 19:00:35 *************** makeEmptyPGconn(void) *** 1849,1854 **** --- 1849,1855 ---- #ifdef USE_SSL conn->allow_ssl_try = TRUE; #endif + conn->typecache = NULL; /* * The output buffer size is set to 8K, which is the usual size of *************** freePGconn(PGconn *conn) *** 1891,1896 **** --- 1892,1898 ---- if (!conn) return; pqClearAsyncResult(conn); /* deallocate result and curTuple */ + pqTypeCacheClear(conn); /* free all type cache entries */ #ifdef USE_SSL if (conn->ssl) SSL_free(conn->ssl); Index: fe-exec.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v retrieving revision 1.113 diff -c -p -r1.113 fe-exec.c *** fe-exec.c 2001/10/25 05:50:13 1.113 --- fe-exec.c 2001/11/07 19:00:35 *************** char *const pgresStatus[] = { *** 48,53 **** --- 48,54 ---- static void pqCatenateResultError(PGresult *res, const char *msg); static void saveErrorResult(PGconn *conn); static PGresult *prepareAsyncResult(PGconn *conn); + static PGresult *pqExec(PGconn *conn, const char *query, int metadata); static int addTuple(PGresult *res, PGresAttValue * tup); static void parseInput(PGconn *conn); static void handleSendFailure(PGconn *conn); *************** static int getRowDescriptions(PGconn *co *** 55,60 **** --- 56,63 ---- static int getAnotherTuple(PGconn *conn, int binary); static int getNotify(PGconn *conn); static int getNotice(PGconn *conn); + static char *pqTypeCacheGet(PGconn *conn, Oid typenum); + static void pqTypeCachePut(PGconn *conn, Oid typenum, char *typename); /* --------------- * Escaping arbitrary strings to get valid SQL strings/identifiers. *************** addTuple(PGresult *res, PGresAttValue * *** 609,614 **** --- 612,678 ---- return TRUE; } + /* Cache of the correspondence between type Oids and + * type names. Without it too many queries can be made to + * retrieve this same information from the catalog over and over. + */ + + static char * + pqTypeCacheGet(PGconn *conn, Oid typenum) + { + char *typename = NULL; + PGtypecache *tc = conn->typecache; + + /* Look for type Oid. */ + while (tc != NULL) + { + if (tc->typenum == typenum) + { + typename = tc->typename; + break; + } + else + tc = tc->next; + } + return typename; + } + + static void + pqTypeCachePut(PGconn *conn, Oid typenum, char *typename) + { + PGtypecache *typetocache; + + typetocache = (PGtypecache *) malloc(sizeof(PGtypecache)); + if (typetocache == NULL) + { + fprintf(stderr, "pqTypeCachePut: malloc failed.\n"); + return; + } + + typetocache->typenum = typenum; + typetocache->typename = strdup(typename); + typetocache->next = conn->typecache; + conn->typecache = typetocache; + } + + void + pqTypeCacheClear(PGconn *conn) + { + PGtypecache *tc; + PGtypecache *ntc; + + /* Free all tcache entries (and typenames). */ + tc = conn->typecache; + conn->typecache = NULL; + while (tc != NULL) + { + if (tc->typename) + free(tc->typename); + ntc = tc->next; + free(tc); + tc = ntc; + } + } /* * PQsendQuery *************** PQgetResult(PGconn *conn) *** 1277,1301 **** return res; } /* ! * PQexec * send a query to the backend and package up the result in a PGresult * * If the query was not even sent, return NULL; conn->errorMessage is set to * a relevant message. * If the query was sent, a new PGresult is returned (which could indicate * either success or failure). * The user is responsible for freeing the PGresult via PQclear() * when done with it. */ ! PGresult * ! PQexec(PGconn *conn, const char *query) { PGresult *result; PGresult *lastResult; bool savedblocking; /* * we assume anyone calling PQexec wants blocking behaviour, we force --- 1341,1381 ---- return res; } + PGresult * + PQexec(PGconn *conn, const char *query) + { + /* Don't get metadata. */ + return pqExec (conn, query, 0 /* no metadata */); + } + PGresult * + PQexecIncludeMetadata(PGconn *conn, const char *query) + { + /* Get metadata as well. */ + return pqExec (conn, query, 1 /* with metadata */); + } + /* ! * pqExec * send a query to the backend and package up the result in a PGresult * * If the query was not even sent, return NULL; conn->errorMessage is set to * a relevant message. * If the query was sent, a new PGresult is returned (which could indicate * either success or failure). + * If it is called with metadata == 1, the metadata about the column + * results will be obtained and saved in the PGresult. * The user is responsible for freeing the PGresult via PQclear() * when done with it. */ ! static PGresult * ! pqExec(PGconn *conn, const char *query, int metadata) { PGresult *result; PGresult *lastResult; bool savedblocking; + int i; /* * we assume anyone calling PQexec wants blocking behaviour, we force *************** PQexec(PGconn *conn, const char *query) *** 1363,1368 **** --- 1443,1501 ---- if (PQsetnonblocking(conn, savedblocking) == -1) return NULL; + + /* + * If metadata is requested and everything is well, loop through + * the result fields grabing the required information. + */ + + if (metadata && (lastResult->numAttributes > 0)) + for (i = 0; i < lastResult->numAttributes; i++) + { + Oid typenum; + PGresult *result; + char *tempname; + static char query[] = "select typname from pg_type where oid = %lu"; + char *fullquery; + + if ((typenum = lastResult->attDescs[i].typid) == 0) + continue; + + /* Look up the cache for the type name. */ + tempname = pqTypeCacheGet(conn, typenum); + + /* If it is a type that we still don't know the name, + query for the type name and store it in the cache. */ + if (tempname == NULL) + { + fullquery = malloc (sizeof(query) + + 20 /* if Oids become 64 bits */); + if (fullquery == NULL) + { + fprintf(stderr, "pqExec: malloc failed.\n"); + return NULL; + } + /* If the typename was not in the cache, query the catalog + and add it to the cache */ + snprintf(fullquery, sizeof(query) + 20, query, typenum); + result = PQexec(conn, fullquery); + free(fullquery); + if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) + { + PQclear(result); + continue; + } + if (PQntuples(result) != 1 || PQnfields(result) != 1) { + PQclear(result); + continue; + } + pqTypeCachePut(conn, typenum, PQgetvalue(result, 0, 0)); + tempname = pqTypeCacheGet(conn, typenum); + } + + lastResult->attDescs[i].atttypname = strdup(tempname); + } + return lastResult; errout: *************** PQftype(const PGresult *res, int field_n *** 2104,2109 **** --- 2237,2253 ---- return InvalidOid; } + char * + PQftypeName(const PGresult *res, int field_num) + { + if (!check_field_number(res, field_num)) + return NULL; + if (res->attDescs) + return res->attDescs[field_num].atttypname; + else + return NULL; + } + int PQfsize(const PGresult *res, int field_num) { *************** PQfmod(const PGresult *res, int field_nu *** 2124,2129 **** --- 2268,2330 ---- return res->attDescs[field_num].atttypmod; else return 0; + } + + int + PQfprecision(const PGresult *res, int field_num) + { + int mod; + char *type; + + if ((type = PQftypeName(res, field_num)) == NULL) + return 0; + mod = PQfmod(res, field_num); + + if (strcmp(type, "numeric") == 0) + return ((0xFFFF0000) & mod) >> 16; + else if (strcmp(type, "int2") == 0) + return 5; + else if (strcmp(type, "int4") == 0) + return 10; + else if (strcmp(type, "int8") == 0) + return 19; /* It would be 20 if it was unsigned. */ + else if (strcmp(type, "float4") == 0) + return 6; + else if (strcmp(type, "float8") == 0) + return 15; + else if (strcmp(type, "varchar") == 0 || + strcmp(type, "bpchar") == 0 || + strcmp(type, "char") == 0) + return mod - 4; + else if (strcmp(type, "varbit") == 0 || + strcmp(type, "bit") == 0) + return mod; + + return -1; + } + + int + PQfscale(const PGresult *res, int field_num) + { + int mod; + char *type; + + if ((type = PQftypeName(res, field_num)) == NULL) + return 0; + mod = PQfmod(res, field_num); + + if (strcmp(type, "numeric") == 0) + return ((0x0000FFFF) & mod) - 4; + else if (strcmp(type, "int2") == 0 || + strcmp(type, "int4") == 0 || + strcmp(type, "int8") == 0) + return 0; + else if (strcmp(type, "float4") == 0) + return -1; + else if (strcmp(type, "float8") == 0) + return -1; + + return -1; } char * Index: fe-misc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v retrieving revision 1.60 diff -c -p -r1.60 fe-misc.c *** fe-misc.c 2001/11/05 17:46:37 1.60 --- fe-misc.c 2001/11/07 19:00:35 *************** WSSE_GOODEXIT: *** 896,898 **** --- 896,974 ---- } #endif + + char * + PQinternal2common(const char *intname) + { + static char *typename; + + if (intname == NULL) + return NULL; + + if (strcmp(intname, "int8") == 0) + typename = "bigint"; + else if (strcmp(intname, "bit") == 0) + typename = "bit"; + else if (strcmp(intname, "varbit") == 0) + typename = "varbit"; /* bit varying */ + else if (strcmp(intname, "bool") == 0) + typename = "boolean"; + else if (strcmp(intname, "box") == 0) + typename = "box"; + else if (strcmp(intname, "bpchar") == 0) + typename = "char"; /* character */ + else if (strcmp(intname, "varchar") == 0) + typename = "varchar"; /* character varying */ + else if (strcmp(intname, "cidr") == 0) + typename = "cidr"; + else if (strcmp(intname, "circle") == 0) + typename = "circle"; + else if (strcmp(intname, "date") == 0) + typename = "date"; + else if (strcmp(intname, "float8") == 0) + typename = "double precision"; + else if (strcmp(intname, "inet") == 0) + typename = "inet"; + else if (strcmp(intname, "int4") == 0) + typename = "integer"; + else if (strcmp(intname, "interval") == 0) + typename = "interval"; + else if (strcmp(intname, "line") == 0) + typename = "line"; + else if (strcmp(intname, "lseg") == 0) + typename = "lseg"; + else if (strcmp(intname, "macaddr") == 0) + typename = "macaddr"; + else if (strcmp(intname, "decimal") == 0) + typename = "numeric"; + else if (strcmp(intname, "numeric") == 0) + typename = "numeric"; + else if (strcmp(intname, "oid") == 0) + typename = "oid"; + else if (strcmp(intname, "path") == 0) + typename = "path"; + else if (strcmp(intname, "point") == 0) + typename = "point"; + else if (strcmp(intname, "polygon") == 0) + typename = "polygon"; + else if (strcmp(intname, "float4") == 0) + typename = "real"; + else if (strcmp(intname, "int2") == 0) + typename = "smallint"; + else if (strcmp(intname, "serial") == 0) + typename = "serial"; + else if (strcmp(intname, "text") == 0) + typename = "text"; + else if (strcmp(intname, "time") == 0) + typename = "time"; + else if (strcmp(intname, "time with time zone") == 0) + typename = "time with time zone"; + else if (strcmp(intname, "timestamp") == 0) + typename = "timestamp"; + else if (strcmp(intname, "timestamp with time zone") == 0) + typename = "timestamp with time zone"; + else + typename = NULL; + + return typename; + } Index: libpq-fe.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v retrieving revision 1.79 diff -c -p -r1.79 libpq-fe.h *** libpq-fe.h 2001/11/05 17:46:37 1.79 --- libpq-fe.h 2001/11/07 19:00:35 *************** extern "C" *** 256,261 **** --- 256,262 ---- /* Simple synchronous query */ extern PGresult *PQexec(PGconn *conn, const char *query); + extern PGresult *PQexecIncludeMetadata(PGconn *conn, const char *query); extern PGnotify *PQnotifies(PGconn *conn); extern void PQfreeNotify(PGnotify *notify); *************** extern "C" *** 303,315 **** extern char *PQfname(const PGresult *res, int field_num); extern int PQfnumber(const PGresult *res, const char *field_name); extern Oid PQftype(const PGresult *res, int field_num); extern int PQfsize(const PGresult *res, int field_num); extern int PQfmod(const PGresult *res, int field_num); extern char *PQcmdStatus(PGresult *res); extern char *PQoidStatus(const PGresult *res); /* old and ugly */ extern Oid PQoidValue(const PGresult *res); /* new and improved */ ! extern char *PQcmdTuples(PGresult *res); ! extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); extern int PQgetlength(const PGresult *res, int tup_num, int field_num); extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); --- 304,319 ---- extern char *PQfname(const PGresult *res, int field_num); extern int PQfnumber(const PGresult *res, const char *field_name); extern Oid PQftype(const PGresult *res, int field_num); + extern char *PQftypeName(const PGresult *res, int field_num); extern int PQfsize(const PGresult *res, int field_num); extern int PQfmod(const PGresult *res, int field_num); + extern int PQfprecision(const PGresult *res, int field_num); + extern int PQfscale(const PGresult *res, int field_num); extern char *PQcmdStatus(PGresult *res); extern char *PQoidStatus(const PGresult *res); /* old and ugly */ extern Oid PQoidValue(const PGresult *res); /* new and improved */ ! extern char *PQcmdTuples(PGresult *res); ! extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); extern int PQgetlength(const PGresult *res, int tup_num, int field_num); extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); *************** extern "C" *** 371,376 **** --- 375,383 ---- /* Get encoding id from environment variable PGCLIENTENCODING */ extern int PQenv2encoding(void); + /* Convert internal type name to common type name */ + extern char *PQinternal2common(const char *intname); + #ifdef __cplusplus } #endif Index: libpq-int.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.44 diff -c -p -r1.44 libpq-int.h *** libpq-int.h 2001/11/05 17:46:38 1.44 --- libpq-int.h 2001/11/07 19:00:35 *************** union pgresult_data *** 75,88 **** char space[1]; /* dummy for accessing block as bytes */ }; ! /* Data about a single attribute (column) of a query result */ typedef struct pgresAttDesc { ! char *name; /* type name */ Oid typid; /* type id */ int typlen; /* type size */ int atttypmod; /* type-specific modifier info */ } PGresAttDesc; /* Data for a single attribute of a single tuple */ --- 75,91 ---- char space[1]; /* dummy for accessing block as bytes */ }; ! /* Data about a single attribute (column) of a query result. ! * The type name is only available if PQexecIncludeMetadata() was used. ! */ typedef struct pgresAttDesc { ! char *name; /* column name */ Oid typid; /* type id */ int typlen; /* type size */ int atttypmod; /* type-specific modifier info */ + char *atttypname; /* type name */ } PGresAttDesc; /* Data for a single attribute of a single tuple */ *************** typedef struct pgLobjfuncs *** 191,196 **** --- 194,208 ---- Oid fn_lo_write; /* OID of backend function LOwrite */ } PGlobjfuncs; + /* Entry in the cache of the correspondence between type Oids and type names. + */ + typedef struct pgTypeCache + { + Oid typenum; /* OID of type */ + char *typename; /* name of type */ + struct pgTypeCache *next; /* name of type */ + } PGtypecache; + /* PGconn stores all the state data associated with a single connection * to a backend. */ *************** struct pg_conn *** 240,245 **** --- 252,258 ---- char cryptSalt[2]; /* password salt received from backend */ PGlobjfuncs *lobjfuncs; /* private state for large-object access * fns */ + PGtypecache *typecache; /* cached types for this connection. */ /* Buffer for data received from backend and not yet processed */ char *inBuffer; /* currently allocated buffer */ *************** extern void pqSetResultError(PGresult *r *** 305,310 **** --- 318,324 ---- extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); extern char *pqResultStrdup(PGresult *res, const char *str); extern void pqClearAsyncResult(PGconn *conn); + extern void pqTypeCacheClear(PGconn *conn); /* === in fe-misc.c === */