pqt-specs - A manual for libpqtypes data type specifier strings.
Contents
Array
Arrays are put using the PGarray structure. Elements are put using PQputf(3) into a PGparam structure
contained withn a PGarray. The PGarray contains array dimension members for specifing the number of
dimension, the dimensions of each dimension and the lower bounds of each dimension.
Arrays are only handled using binary format. This means that any type used as an array element must be
put and gotten in binary format. If a user-defined type does not implement a send and recv function in
the backend, it can not be used as an array element.
For a discussion of composite arrays, `man pqt-composites(3)´.
typedefstruct{/*Thenumberofarraydimensions.Specifingzeroforthis*valueonputshasspecialmeaning.Whenzero,thisvalue*issettoone,dims[0]issettothenumberofitemsin*the´param´memberandlbound[0]issettoone.*/intndims;/*Anarrayoflowerboundsforeachdimension.*/intlbound[MAXDIM];/*Anarrayofdimensionsforeachdimension.*/intdims[MAXDIM];/*Whenputtingarrayelements,thisPGparamisused.Each*itemputisonearrayelement.BecausethePQputf(3)*interfaceallowsputtingmorethanoneitematatime,you*canputmultiplearrayelements.*/PGparam*param;/*Whengettinganarray,thisisthePGresultobjectthat*containsthearrayelements.Eachelementisonetuple,*regardlessofthearraydimensions.Ifthearrayhas100*elementsacross3dimensions,PQntuples(arr.res)willreturn*100.Theonlyvalidfield,fornon-compositearrays,is*fieldzero.*/PGresult*res;}PGarray;Whenallelementshavebeenput,thePGarraystructuremustbeputusingthe"[]"arrayspeciferflagintoaPGparamstructure.PQputf(3)isusedtobuildthearrayelementsandtoputtheresultingPGarray.Puttinganarrayvalue:PGint4i;PGarrayarr;PGparam*param;/*Onedimensionalarraysdonotrequiresettingdimensioninfo.For*convience,youcanzerothestructureorsetndimstozero.*/arr.ndims=0;/*createtheparamobjectthatwillcontaintheelements*/arr.param=PQparamCreate(conn);/*UsePQputf(3)toputthearrayelements*/for(i=0;i<1000;i++)PQputf(arr.param,"%int4",i);/*ThePGarraymustbeputintoaPGparamstruture.Sofar,only*thearrayelementshavebeenput.´param´cancontinuetobe*usedtopackmoreparameters.Thearrayisnowasingleparameter*within´param´.*/param=PQparamCreate(conn);PQputf(param,"%int[]",&arr);/*nolongerneeded*/PQparamClear(arr.param);Togetanarray,PQgetf(3)isusedinconjunctionwiththePGarraystructure.TheresultobjectcontainedwiththePGarrayispopulatedwiththearrayelements.Thedimensioninfoisassignedaswell.Eacharrayelementisitsowntuplethatonlycontainsasinglefieldfornoncompositearrays.Gettinganarrayvalue:inti;PGint4val;intntups;PGarrayarr;PGresult*result=...;PQgetf(result,0,"%int[]",0,&arr);/*notneededanymore*/PQclear(result);/*eachtupleisanarrayelement*/ntups=PQntuples(arr.res);for(i=0;i<ntups;i++){/*Alwaysfield0*/PQgetf(arr.res,i,"%int4",0,&val);printf("[%03d]%d\n",i,val);}PQclear(arr.res);Theresultobjectisnotorganizedbasedonthedimensioninfo.Indexesarealwayszero-based.Ifthedimensioninfoismeaningfultoyourapplication,indextranslationmustbedoneusingthendims,dimsandlboundmembersofthePGarraystructure.Youcannotreferenceanarrayelementbyfieldname.Theonlylogicalnameforanarrayelementwouldbethestringversionofitsindex..."0","1",etc..Theindexvalueofanon-compositearrayisitstuplenumber,thefieldnumberisalwayszero.Thismeans"#int"isnotlegalfornon-compositearrays.Youmustuse"%int"andreferencefield0.Bool
The bool type is put/get as a PGbool. To put true or false, use 1 or 0.
Putting a bool value:
PGboolb=1;//puttruePQputf(param,"%bool",b);
Getting a bool value:
PGboolb;PQgetf(result,tup_num,"%bool",field_num,&b);Box
The PGbox structure is used to put and get a box.
typedefstruct{PGpointhigh;PGpointlow;}PGbox;
Putting a box value:
PGboxbox={{12.345,6.789},{22.234,1.9998}};PQputf(param,"%box",&box);
Getting a box value:
PGboxbox;PQgetf(result,tup_num,"%box",field_num,&box);Bytea
There are two ways to put a bytea: copy or direct pointer (just like variable-length character types).
In either case, you supply a pointer to a PGbytea.
typedefstruct{intlen;/*numberofbytes*/char*data;/*pointertothebyteadata*/}PGbytea;/*Putabytealettinglibpqtypesmakeacopy;*/PGbyteabytea={4,{0,1,2,3}};PQputf(param,"%bytea",&bytea);/*Putabyteanotlettinglibpqtypesmakeacopy,storesa*directpointertoPGbytea.data.*/PQputf(param,"%bytea*",&bytea);
To get a bytea, you provide a pointer to a PGbytea. Unlike putting bytea values, there is only one way
to get them.
/*Getabyteavalue(exposedasbinary,no*escaping/unescapingneeded)*/PGbyteabytea;PQgetf(result,tup_num,"%bytea",field_num,&bytea);
NOTE: For text results, PQgetlength will not match the length returned by PQgetf(3). The values
PQgetf(3) assigns to the user provided PGbytea* represent the unescaped bytea value.
Char
The "char" data type uses the PGchar. The value is limited to 8-bits.
Putting a "char" value:
PGcharc=´a´;PQputf(param,"%char%char",213,c);
Getting a "char" value:
PGcharc;PQgetf(result,tup_num,"%char",field_num,&c);Circle
The PGcircle structure is used to put and get a circle.
typedefstruct{PGpointcenter;doubleradius;}PGcircle;
Putting a circle value:
PGcirclecircle={{12.345,6.789},2.34567};PQputf(param,"%circle",&circle);
Getting a circle value:
PGcirclecircle;PQgetf(result,tup_num,"%circle",field_num,&circle);Copyright
Copyright (c) 2011 eSilo, LLC. All rights reserved.
This is free software; see the source for copying conditions. There is NO warranty; not even for
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Date
PGdate is used by DATE, TIMESTAMP and TIMESTAMPTZ data types. To put a date, you must set the isbc, year,
mon and mday members. All other members are ignored.
typedefstruct{/*Whennon-zero,thedateisintheBCERA.*/intisbc;/**TheBCorADyear,whichisNOTadjustedby1900like*thePOSIXstructtm.Yearsarealwayspositivevalues,*evenBCyears.TodistinguishbetweenBCandADyears,*usetheisbcflag:(year0notused)*Ex.-1210isrepresentedas:isbc=1,year=1209*/intyear;/*ThenumberofmonthssinceJanuary,intherange0to11.*/intmon;/*Thedayofthemonth,intherange1to31.*/intmday;/*TheJuliandayintheGregoriancalendar.*/intjday;/*ThenumberofdayssinceJanuary1,intherange0to365.*/intyday;/*ThenumberofdayssinceSunday,intherange0to6.*/intwday;}PGdate;
Putting a date value:
//´1401-01-19BC´PGdatedate;date.isbc=1;date.year=1401;date.mon=0;date.mday=19;PQputf(param,"%date",&date);
Getting a date value:
PQgetf(result,tup_num,"%date",field_num,&date);Description
The type system allows addressing backend data types by their fully qualified schema name. Similar to
the backend type system, libpqtypes has an input and output function for each type: put and get
respectively. All builtin types are supported by libpqtypes. Additional types, such as composites and
user-defined types, can be registered by an API-user on a per connection basis.
Putting and getting types are addressed by their backend names as printf-style format specifiers. For
instance: %int4, %timestamptz or %text. They can also be addressed using their fully qualified schema
names: %pg_catalog.int4, %pg_catalog.timestamptz, %pg_catalog.text or %myschema.mytype to avoid
ambiguity.
SpecifierStringsSpecifierMark
A specifier mark indicates where a specifier begins. A mark is not part of the type name.
% Marks the beginning of a type name. When getting results, this
also indicates that a field will be referenced by number.
# Marks the beginning of a type name. When getting results, this
also indicates that a field will be referenced by name.
@ Marks the beginning of a prepared specifier name, see
PQspecPrepare(). When used, it must be the first and only
specifier in the format string: "@prepared_spec", "@myfunc",
etc... NOTE: the ´@´ must be the first character, no spaces.
TypeSpecifier
Type specifiers are comprised of an optional schema name and type name. Type specifiers have a set of
rules:
-) Format: [schema].type - optional schema name, a "." separator
between schema and type and the type name.
-) First character must be a-z or an underscore
-) Double quotes are required for characters not in [a-zA-Z0-9_]
NOTE: In libpqtypes, this includes "double precision"
-) Schema "." separator, specifier marks or flags are not included
in double quotes
-) Non-quoted type names are casefolded, quoted names are not.
-) Examples:
"%int4"
"%pg_catalog.int4"
"%\"my oDd~ !tYpe naMe#\""
"%myschema.\"my oDd~ !tYpe naMe#\""
"%abc.int4 %pg_catalog.int4" <= fully qualified type names
-) Last in First out: To find a type referenced in a specifier
string, the search begins with the last type registered.
User registered type handlers are searched first, followed
by builtin types.
-) pqt schema: There is a builtin schema named pqt. By default,
it contains two types: ´str´ and ´null´. Anything can be
put into this schema, which has nothing to do with the server.
This is good for aliases or type sub-classes that are
client-specific.
SpecifierFlag
Flags are used to alter the behavior of a type specifier. They are always placed at the end of the
specifier name. If the name is double quoted, the flag is just after the closing quote.
* This is called the pointer flag. It is only supported on a
handful of builtin types during a PQputf(3), but user registered
types can provide support for them. Supported types are:
VARCHAR, BPCHAR, TEXT, BYTEA, NAME and the pqt.str.
Putting data: this flag tells libpqtypes to store a direct
pointer to the data being put, rather than making a copy of
it.
Getting data: no built-in types make use of the pointer flag.
User-defined type handlers can make the pointer flag behave
anyway they see fit. The ´get´ type handler is supplied a
PGtypeArgs which contains an ´is_ptr´ member.
[] This is called the array flag. It indicates that an array is
being referenced rather than a simple type. This flag is always
used with a PGarray.
Examples
None.
Inet & Cidr
When putting an inet or cidr, all members must be set excluding the sa_len.
typedefstruct{/*Theaddressmask,32forasingleIP.*/intmask;/*Whennon-zero,thePGinetstructurerepresentsacidr*otherwiseaninet.*/intis_cidr;/*thelengthinbytesofthesa_bufmember.*/intsa_len;/*thesocketaddressbuffer,containsthedata.Thiscan*becastedtoasockaddr,sockaddr_in,sockaddr_in6ora*sockaddr_storagestructure.Thisbufferis128bytesso*thatitislargeenoughforasockaddr_storagestructure.*/charsa_buf[128];}PGinet;
Putting an inet or cidr:
socklen_tlen;PGinetinet;cli_fd=accept(srv_fd,(structsockaddr*)inet.sa_buf,&len);if(cli_fd!=-1){inet.is_cidr=0;inet.mask=32;PQputf(param,"%inet",&inet);}
Getting an inet or cidr:
PGinetinet;unsignedshortport;/*getsaninetfromfield2andanint2fromfield6*/if(PQgetf(result,tup_num,"%inet%int2",2,&inet,6,&port)){charip[80];structsockaddr*sa=(structsockaddr*)inet.sa_buf;//convertingaPGinettoanIPv4orIPv6addressstringgetnameinfo(sa,inet.sa_len,ip,sizeof(ip),NULL,0,NI_NUMERICHOST);//Theinetdatatypedoesnotstoreaport.if(sa->sa_family==AF_INET)((structsockaddr_in*)sa)->sin_port=htons(port);else((structsockaddr_in6*)sa)->sin6_port=htons(port);printf("Connectingto%s:%d\n",ip,port);connect(sock_fd,sa,inet.sa_len);}Interval
To put an interval, all relevant members of a PGinterval should be assigned and those not used should be
set to zero.
typedefstruct{/*thenumberofyears*/intyears;/*thenumberofmonths*/intmons;/*thenumberofdays*/intdays;/*thenumberofhours*/inthours;/*thenumberofmins*/intmins;/*thenumberofseconds*/intsecs;/*thenumberofmicroseconds*/intusecs;}PGinterval;
Putting an interval value:
//"20years8months9hours10mins15secs123456usecs"PGintervalinterval;interval.years=20;interval.mons=8;interval.days=0;//notused,setto0interval.hours=9;interval.mins=10;interval.secs=15;interval.usecs=123456;PQputf(param,"%interval",&interval);
Getting an interval value:
PQgetf(result,tup_num,"%interval",field_num,&interval);
NOTE: When using text results with a non-ISO DateStyle, microseconds are truncated to a 2 digit value.
For example: "4 mins 2.11 secs" but microseconds is really 111456.
Lseg
The PGlseg structure is used to put and get a line segnment.
typedefstruct{PGpointpts[2];}PGlseg;
Putting a lseg value:
PGlseglseg={{{12.345,6.789},{99.8,88.9}}};PQputf(param,"%lseg",&lseg);
Getting a lseg value:
PGlseglseg;PQgetf(result,tup_num,"%lseg",field_num,&lseg);Macaddr
The PGmacaddr structure is used to put and get a macaddr.
typedefstruct{inta;intb;intc;intd;inte;intf;}PGmacaddr;
Putting a macaddr value:
PGmacaddrmacaddr={0,1,2,3,4,5};PQputf(param,"%macaddr",&macaddr);
Getting a macaddr value:
PGmacaddrmacaddr;PQgetf(result,tup_num,"%macaddr",field_num,&macaddr);Money
The money type is put/get as a PGmoney (64-bit integer). It can be converted to dollar and cents format
by dividing by 100: double money = (double)money64 / 100.0;. Pre 8.3 servers are limited to 32-bit money
values.
Putting a money value:
PGmoneymoney=600000000054LL;//6billiondollarsand54centsPQputf(param,"%money",money);
Getting a money value:
PQgetf(result,tup_num,"%money",field_num,&money);Name
pqt-specs - A manual for libpqtypes data type specifier strings.
Numerics
With the exception of the "numeric" type, all numeric types behave identically: int2, int4, int8, float4
and float8.
PG type C type
PGint2 short
PGint4 int
PGint8 long long (platform dependent)
PGfloat4 float
PGfloat8 double
Putting numeric values: If the value supplied is too large for the PG type, it will be silently
truncated.
PQputf(param,"%int2%int4%int8%float4%float8",SHRT_MAX,INT_MAX,LLONG_MAX,1234.56,123456.789);
Getting numeric values: Like scanf, the correctly sized data type must be used. For instance: you cannot
use a 4-byte int for %int2 - you must use a short.
//Readanint2fromfield0,int4fromfield1,int8from//field2,float4fromfield3andafloat8fromfield4PGint2i2;PGint4i4;PGint8i8;PGfloat4f4;PGfloat8f8;PQgetf(result,tup_num,"%int2%int4%int8%float4%float8",0,&i2,1,&i4,2,&i8,3,&f4,4,&f8);
The numeric data type is always exposed in text format. There is no C data structure. It is always sent
in binary format.
PQputf(param,"%numeric","1728718718271827121233.121212121212");
Even if binary results are used when getting a numeric value, libpqtypes will internally convert the
numeric to text. This has the advantage of allowing you to use binary results and still have access to
numeric fields. If you want to work with a numeric in binary form, use PQgetvalue() on a binary result
set.
PGnumericnumstr;PQgetf(result,tup_num,"%numeric",field_num,&numstr);
The first argument is the field number of the numeric. The second argument is a pointer to a PGnumeric to
receive the numeric string value (which will always be NUL terminated).
Oid
Putting an oid value:
Oidoid=2318;PQputf(param,"%oid",oid);
Getting an oid value:
Oidoid;PQgetf(result,tup_num,"%oid",field_num,&oid);Path
The PGpath structure is used to put and get a path. If the closed member is non-zero, the path is
closed, otherwise it is open.
typedefstruct{intnpts;intclosed;PGpoint*pts;}PGpath;
Putting a path value:
//Putaclosedpaththatcontains2pointsPGpointpts[]={{12.345,6.789},{19.773,7.882}};PGpathpath={2,1,pts};PQputf(param,"%path",&path);
Getting a path value:
PGpathpath;if(PQgetf(result,tup_num,"%path",field_num,&path)){//path.ptsmustbecopiedoutifneededafterclearingresultscopy_points(path.npts,path.pts,...);PQclear(result);//path.ptsisnowinvalid!}Point
The PGpoint structure is used to put and get a point.
typedefstruct{doublex;//pointxvaluedoubley;//pointyvalue}PGpoint;
Putting a point value:
PGpointpt={12.345,6.789};PQputf(param,"%point",&pt);
Getting a point value:
PGpointpt;PQgetf(result,tup_num,"%point",field_num,&pt);Polygon
The PGpolygon structure is used to put and get a polygon.
typedefstruct{intnpts;PGpoint*pts;}PGpolygon;
Putting a polygon value:
//Putapolygonthatcontains2pointsPGpointpts[]={{12.345,6.789},{19.773,7.882}};PGpolygonpolygon={2,1,pts};PQputf(param,"%polygon",&polygon);
Getting a polygon value:
PGpolygonpolygon;if(PQgetf(result,tup_num,"%polygon",field_num,&polygon)){//polygon.ptsmustbecopiedoutifneededafterclearingresultscopy_points(polygon.npts,polygon.pts,...);PQclear(result);//polygon.ptsisnowinvalid}Reporting Bugs
Report bugs to <libpqtypes@esilo.com>.
See Also
PQgetf(3), PQputf(3). libpqtypes 2011 pqt-specs(3)
Time
PGtime is used by TIME, TIMETZ, TIMESTAMP and TIMESTAMPTZ data types. To put a time, you must set the
hour, min, sec and usec members. All other members are ignored.
typedefstruct{/*Thenumberofhourspastmidnight,intherange0to23.*/inthour;/*Thenumberofminutesafterthehour,inthe*range0to59.*/intmin;/*Thenumberofsecondsaftertheminute,inthe*range0to59.*/intsec;/*Thenumberofmicrosecondsafterthesecond,inthe*rangeof0to999999.*/intusec;/**Whennon-zero,thisisaTIMEWITHTIMEZONE.Otherwise,*itisaTIMEWITHOUTTIMEZONE.*/intwithtz;/*Avalueof1indicatesdaylightsavingstime.Avalueof0*indicatesstandardtime.Avalueof-1meansunknownor*couldnotdetermine.*/intisdst;/*OffsetfromUTCinseconds.Thisvalueisnotalways*available.Itissetto0ifitcannotbedetermined.*/intgmtoff;/*Timezoneabbreviation:suchasEST,GMT,PDT,etc.*Thisvalueisnotalwaysavailable.Itissettoanempty*stringifitcannotbedetermined.*/chartzabbr[16];}PGtime;
Putting a time value:
//´10:41:06.002897´PGdatetime;time.hour=10;time.min=41;time.sec=6;time.usec=2897;PQputf(param,"%time",&time);
Getting a time value:
PQgetf(result,tup_num,"%time",field_num,&time);Timestamp
To put a timestamp, the isbc, year, mon, mday, hour, min, sec and usec members must be set. No other
members are used.
typedefstruct{/*ThenumbersecondsbeforeoraftermidnightUTCof*January1,1970,notcountingleapseconds.*/PGint8epoch;/*Thedatepartofthetimestamp.*/PGdatedate;/*Thetimepartofthetimestamp.*/PGtimetime;}PGtimestamp;
Putting a timestamp value:
//´2000-01-1910:41:06´PGtimestampts;ts.date.isbc=0;ts.date.year=2000;ts.date.mon=0;ts.date.mday=19;ts.time.hour=10;ts.time.min=41;ts.time.sec=6;ts.time.usec=0;PQputf(param,"%timestamp",&ts);
Getting a timestamp value:
PQgetf(result,tup_num,"%timestamp",field_num,&ts);
The timestamp type has no concept of timezone, so the value returned by PQgetf(3) is exactly what the
server sent; no timezone adjustments are attempted. The gmtoff is always set to zero, tzabbr will be an
empty string and withtz will be zero.
Timestamptz
To put a timestamptz, the isbc, year, mon, mday, hour, min, sec, usec and gmtoff members must be set. No
other members are used.
Putting a timestamptz value:
//´2000-01-1910:41:06-05´PGtimestampts;ts.date.isbc=0;ts.date.year=2000;ts.date.mon=0;ts.date.mday=19;ts.time.hour=10;ts.time.min=41;ts.time.sec=6;ts.time.usec=0;ts.time.gmtoff=-18000;PQputf(param,"%timestamptz",&ts);
Getting a timestamptz value:
PQgetf(result,tup_num,"%timestamptz",field_num,&ts);
When using PQgetf(3) in binary mode, the timestamptz value is converted into the local machine´s
timezone. If the local machine's timezone can not be determined, the value will be in GMT (gmtoff is set
to zero and tzabbr is set to GMT).
When using PQgetf(3) in text mode, the timestamptz value is returned as a datetime string in the server´s
timezone. No adjustments are made to this value. If the server is using a DateStyle that encodes the
gmtoff "00:00:00-05", then gmtoff will be set to this value and tzabbr will be "GMT+/-hhmmss"
(00:00:00-05 => GMT-0500). In this case, isdst is set to -1 ... meaning unknown. If the server´s
DateStyle encodes a timezone abbreviation, like PST, then tzabbr is set to this value. The gmtoff and
isdst members are properly set:
DateStyle includes a timezone abbrev - "SQL, MDY"
01/25/2007 00:00:00 EST => tzabbr=EST, gmtoff=-18000, isdst=0
01/25/2007 01:00:00 EDT => tzabbr=EDT, gmtoff=-14400, isdst=1
Timetz
The TIMETZ data type uses the PGtime structure, for a description of this structure see the TIME section.
To put a timetz, you must set the hour, min, sec, usec and gmtoff members. All other members are
ignored.
Putting a timetz value:
//´10:41:06.002897-05´PGdatetimetz;timetz.hour=10;timetz.min=41;timetz.sec=6;timetz.usec=2897;timetz.gmtoff=-18000;PQputf(param,"%timetz",&timetz);
Getting a timetz value:
PQgetf(result,tup_num,"%timetz",field_num,&timetz);Uuid
The uuid type is put/get as a sequence of 16 bytes. To put a uuid as text, use "%str". NOTE: this type
is not available on pre 8.3 servers.
Putting a uuid value:
PGuuiduuid={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};PQputf(param,"%uuid",uuid);
Getting a uuid value:
PGuuiduuid;PQgetf(result,tup_num,"%uuid",field_num,&uuid);
WARNING: The data provided on a put call is expected to be at least 16 bytes.
Variable-Length Character Types
BPCHAR, VARCHAR, NAME and TEXT are handled identically. libpqtypes does no range checking on NAME,
BPCHAR or VARCHAR, it lets the server perform these checks. There are two ways to put strings: allow
libpqtypes to make an internal copy of the string (default behavior) or as a direct pointer: (both
require that the C string is NUL-terminated)
/*Putastringsolibpqtypesmakesacopy.Inthiscase,*4copieswouldbemadeofthesamestring.*/PGtextstr="foobar";PQputf(param,"%bpchar%varchar%name%text",str,str,str,str);/*Putastringsolibpqtypesdoesn´tmakeacopy,*keepsadirectpointer.Moreefficientthanabove,*especiallyifthesearelargestrings.*/PQputf(param,"%bpchar*%varchar*%name*%text*",str,str,str,str);
WARNING: Be careful about variable scope when using the "*" specifier flag:
/*when´func´returns,thestrpointerbecomesinvalid!*Thebelowshouldbeusing"%text"...w/othe*flag.*/intfunc(PGparam*param){PGcharstr[16];strcpy(str,"foobar");returnPQputf(param,"%text*",str);//BADIDEA!}
To PQgetf(3) a string, you supply a pointer to a PGtext. Unlike putting string values, getting them
doesn´t make use of the "*" specifier flag (silently ignored).
/*Getastringvalue*/PGvarcharstr;PQgetf(result,tup_num,"%varchar",field_num,&str);/*identicalto*/str=PQgetvalue(result,tup_num,field_num);
The reason the ´*´ specifier flag is silently ignored, rather than raising a syntax error, is it is
common to define macros for specifer strings; that can be used for puts and gets:
/*user_id,username,password*/#defineTYPESPEC_USERINFO"%int4%text*%text*"PGint4uid=0;PGtextuser="foo",pass="bar";PQputf(param,TYPESPEC_USERINFO,uid,user,pass);PQgetf(param,tup_num,TYPESPEC_USERINFO,0,&uid,1,&user,2,&pass);
The above allowance is more useful than a syntax error.
