Re: Libpq support for precision and scale - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Libpq support for precision and scale
Date
Msg-id 200202222005.g1MK50g14262@candle.pha.pa.us
Whole thread Raw
In response to Libpq support for precision and scale  (Fernando Nasser <fnasser@redhat.com>)
List pgsql-patches
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Fernando Nasser wrote:
> Some programs like utilities, IDEs, etc., frequently need to know the
> precision and scale of the result fields (columns).  Unfortunately
> libpq does not have such routines yet (JDBC does).
>
> Liam and I created a few ones that do the trick, as inspired by the
> JDBC code. The functions are:
>
> char *PQftypename(const PGresult *res, int field_num);
>
> Returns the type name (not the name of the column, as PQfname do).
>
>
> int PQfprecision(const PGresult *res, int field_num);
> int PQfscale(const PGresult *res, int field_num);
>
> Return Scale and Precision of the type respectively.
>
>
> Most programs won't need this information and may not be willing
> to pay the overhead for metadata retrieval. Thus, we added
> an alternative function to be used instead of PQexec if one
> wishes extra metadata to be retrieved along with the query
> results:
>
> PGresult *PQexecIncludeMetadata(PGconn *conn, const char *query);
>
> It provides the same functionality and it is used in exactly the
> same way as PQexec but it includes extra metadata about the result
> fields.  After this cal, it is possible to obtain the precision,
> scale and type name for each result field.
>
>
> The PQftypename function returns the internal PostgreSQL type name.
> As some programs may prefer something more user friendly than the
> internal type names, we've thrown in a conversion routine as well:
>
> char *PQtypeint2ext(const char *intname);
>
> This routine converts from the internal type name to a more user
> friendly type name convention.
>
>
> More details are in the patch to the SGML documentation that is
> part of the patch (attached).
>
>
> --
> Liam Stewart <liams@redhat.com>
> Fernando Nasser <fnasser@redhat.com>

> 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 === */
>

> Index: libpq.sgml
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
> retrieving revision 1.72
> diff -c -p -r1.72 libpq.sgml
> *** libpq.sgml    2001/09/13 15:55:23    1.72
> --- libpq.sgml    2001/11/07 19:06:52
> *************** PGresult *PQexec(PGconn *conn,
> *** 728,733 ****
> --- 728,748 ----
>         <function>PQerrorMessage</function> to get more information about the error.
>   </para>
>   </listitem>
> +
> + <listitem>
> + <para>
> + <function>PQexecIncludeMetadata</function>
> +           Submit a query to the server and wait for the result;
> +           include extra metadata about the result fields.
> +       This makes available information such as the type name,
> +       precision and scale for each field in the result.
> + <synopsis>
> + PGresult *PQexecIncludeMetadata(PGconn *conn,
> +                  const char *query);
> + </synopsis>
> +           Used the same way as PQexec().
> + </para>
> + </listitem>
>   </itemizedlist>
>
>   <para>
> *************** You can query the system table <literal>
> *** 964,969 ****
> --- 979,986 ----
>   the name and properties of the various data types. The <acronym>OID</acronym>s
>   of the built-in data types are defined in <filename>src/include/catalog/pg_type.h</filename>
>   in the source tree.
> + The function <function>PQftypename</function> can be used to retrieve the
> + type name if the result was obtained via <function>PQexecIncludeMetadata</function>.
>   </para>
>   </listitem>
>
> *************** extracts data from a <acronym>BINARY</ac
> *** 1010,1015 ****
> --- 1027,1126 ----
>   </para>
>   </listitem>
>   </itemizedlist>
> +
> + <para>
> + The following functions only produce meaningful results if
> + <function>PQexecIncludeMetadata</function> was used
> + (as opposed to <function>PQexec</function>).
> + </para>
> +
> + <itemizedlist>
> +
> + <listitem>
> + <para>
> + <function>PQftypename</function>
> +           Returns the name of the column type as a string.
> +           Field indices start at 0.
> + <synopsis>
> + char *PQftypename(const PGresult *res,
> +             int field_index);
> + </synopsis>
> +       Returns the name of the column type as a string.
> +           Copy the string if needed -- do not modify, free()
> +           or assume its persistence.  The internal type name is
> +           returned; use PQtypeint2ext() to convert to a more SQL-ish style.
> +       NULL is returned if the field type name is not availble.
> + </para>
> + </listitem>
> +
> + <listitem>
> + <para>
> + <function>PQfprecision</function>
> +           Returns  the  precision of the field
> +           associated with the given field index.
> +           Field indices start at 0.
> + <synopsis>
> + int PQfprecision(const PGresult *res,
> +             int field_index);
> + </synopsis>
> +       Returns the precision of the field
> +           associated with the given field index.
> +       For numeric types (INTEGER, FLOAT, etc.), PQfprecision returns the
> +       number of decimal digits in the specified field. For character and bit
> +       string types, such as VARCHAR and BIT, PQfprecision returns the
> +       maximum number of characters/bits allowed in the specified field.
> +       PQfprecision returns 0 if precision information is not available and
> +       -1 if precision is not applicable to the field in question. The latter
> +       will be the case if the type of the field is POINT, for example.
> + </para>
> + </listitem>
> +
> + <listitem>
> + <para>
> + <function>PQfscale</function>
> +           Returns  the  scale of the field
> +           associated with the given field index.
> +           Field indices start at 0.
> + <synopsis>
> + int PQfscale(const PGresult *res,
> +             int field_index);
> + </synopsis>
> +       Returns the scale of the field
> +           associated with the given field index.
> +       PQfscale returns the scale of the field associated with the given
> +       field index. Scale is the number of digits after the decimal point,
> +       so this function is useful only with fields that are of a numeric
> +       type (INTEGER, FLOAT, NUMERIC, etc.). -1 is returned if scale is not
> +       applicable to the field type. 0 is returned if scale information is
> +       not available.
> + </para>
> + </listitem>
> + </itemizedlist>
> +
> + <para>
> + Use the function below to convert internal type names (like the
> + ones returned by <function>PQftypename</function>) into something
> + more user-friendly.
> + </para>
> +
> + <itemizedlist>
> + <listitem>
> + <para>
> + <function>PQtypeint2ext</function>
> +           Converts an internal type name into a SQL-ish
> +           type name.
> + <synopsis>
> + char *PQtypeint2ext(const char **intname);
> + </synopsis>
> +       Converts an internal type name into a SQL-ish
> +           type name.
> +           NULL is returned if the internal type is not recognized
> +           (which will be the case if the type is a UDT).
> + </para>
> + </listitem>
> +
> + </itemizedlist>
> +
>   </sect2>
>
>   <sect2 id="libpq-exec-select-values">

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
> http://archives.postgresql.org

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: ECPG patches: get descriptor NULL alloc, external names
Next
From: Bruce Momjian
Date:
Subject: Re: stupid patch of pg_dumplo