Attached is version 0.3 of the proposed PGparam extension to the libpq API. I think we are wrapping up our changes in the short term and will begin using our api for our internal projects. There were a lot of changes and reorganizations, but the big features are that client side geometry types were introduced and we folded the PGparam struct into PGconn which simplifies the interface in our opinion. See the attached changes file for a complete list. Also attached is a small test which is a good overview of how the proposed API changes work.
The code has been reorganized into a proper patch so that things are injected into libpq in what we think are the right places along with an updated makefile and exports.txt. There are many things we discussed but did not implement because of time concerns, for example client side support for binary arrays and a queryf interface which would map input parameters into the various put functions. These are exciting things but fairly complex features and may require some reorganization of code on the backend to do properly. Hopefully this will help developers who would like to use the high performance binary interface or optimize access to the database from their particular language. Assuming the code is acceptable to the community, we will keep the patch up to date through the 8.4 cycle and write the documentation. Things are obviously really busy right now with HOT and getting 8.3 locked down...but comments and suggestions are most welcome. merlin
pg_param.tgz
Description: GNU Zip compressed data
#if defined(_WIN32) || defined(_WIN64) # define U64FMT "%I64u" typedef unsigned __int64 myuint64_t; #else # define U64FMT "%llu" typedef unsigned long long myuint64_t; #endif //#include "pg.h" #include "/esilo/src/pgsql/src/interfaces/libpq/libpq-fe.h" #include <stdlib.h> #include <string.h> #include <limits.h> #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define countof(array) (sizeof(array)/sizeof(array[0])) #define TEST_TBLNAME "param_test" static const char *create_table = "CREATE TABLE " TEST_TBLNAME " (" " a_char \"char\"," " a_bool boolean," " a_int2 int2," " a_int4 int4," " a_int8 int8," " a_float4 float4," " a_float8 float8," " a_text text," " a_nulltext text," " a_bytea bytea," " a_macaddr macaddr," " a_point point," " a_lseg lseg," " a_box box," " a_circle circle," " a_path path," " a_polygon polygon)"; static const char *insert_command = "INSERT INTO " TEST_TBLNAME " VALUES " "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)"; static int getresfmt(int argc, char **argv); int main(int argc, char **argv) { int i; char ch = 0; int b = 0; short i2 = 0; int i4 = 0; myuint64_t i8; float f4 = 0; double f8 = 0; char *text; int text_isnull; unsigned char *bytea2; unsigned char bytea[4] = {1, 10, 220, 255}; PGmacaddr mac; PGpoint pt; PGlseg lseg; PGbox box; PGcircle circle; PGpath path; PGpolygon polygon; PGconn *conn; PGresult *res; ExecStatusType status; PGpoint points[64]; int resultFormat = getresfmt(argc, argv); conn = PQconnectdb("hostaddr=127.0.0.1 user=postgres"); if(PQstatus(conn) != CONNECTION_OK) { printf("connection failure\n"); return 1; } res = PQexec(conn, create_table); PQclear(res); /* clear test table */ res = PQexec(conn, "DELETE FROM " TEST_TBLNAME); PQclear(res); i8 = ULLONG_MAX; PQputchar(conn, UCHAR_MAX); PQputbool(conn, TRUE); PQputint2(conn, USHRT_MAX); PQputint4(conn, UINT_MAX); PQputint8(conn, &i8); /* pqlib has no 64-bit type. */ PQputfloat4(conn, 111.234f); PQputfloat8(conn, 11111111.234567); PQputtext(conn, "This is some text"); PQputnull(conn); PQput(conn, bytea, (int)sizeof(bytea), InvalidOid, 1); mac.a = 1; mac.b = 2; mac.c = 3; mac.d = 4; mac.e = 5; mac.f = 6; PQputmacaddr(conn, &mac); pt.x = -11.23; pt.y = 23.11; PQputpoint(conn, &pt); lseg.pts[0].x = 6712; lseg.pts[0].y = 2517.89; lseg.pts[1].x = 9087.125; lseg.pts[1].y = 7821.987; PQputlseg(conn, &lseg); box.high.x = 100; box.high.y = 10; box.low.x = 10; box.low.y = 1; PQputbox(conn, &box); circle.center.x = 100; circle.center.y = 200; circle.radius = 300; PQputcircle(conn, &circle); path.closed = 0; /* open path */ path.npts = countof(points); path.pts = points; for(i=0; i < path.npts; i++) { path.pts[i].x = i; path.pts[i].y = i+1; } PQputpath(conn, &path); polygon.npts = 16; polygon.pts = points; if(!PQputpolygon(conn, &polygon)) printf("Polygon: %s\n", PQerrorMessage(conn)); /* Execute query */ PQparamExec(conn, insert_command, 1, &res); /* check result */ status = PQresultStatus(res); if(status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { printf("%s\n", res ? PQresultErrorMessage(res) : PQerrorMessage(conn)); PQclear(res); PQfinish(conn); return 1; } printf("\nGetting results in %s format\n\n", resultFormat ? "BINARY" : "TEXT"); /* select what we just inserted */ PQparamExec(conn, "SELECT * FROM " TEST_TBLNAME, resultFormat, &res); /* check result */ status = PQresultStatus(res); if(status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { printf("%s\n", res ? PQresultErrorMessage(res) : PQerrorMessage(conn)); PQclear(res); PQfinish(conn); return 1; } i8 = 0; i = 0; memset(bytea, 0, sizeof(bytea)); memset(&mac, 0, sizeof(mac)); memset(&pt, 0, sizeof(pt)); memset(&lseg, 0, sizeof(lseg)); memset(&box, 0, sizeof(box)); memset(&circle, 0, sizeof(circle)); memset(&path, 0, sizeof(path)); memset(&polygon, 0, sizeof(polygon)); /* process results */ PQgetchar(res, 0, i++, &ch); PQgetbool(res, 0, i++, &b); PQgetint2(res, 0, i++, &i2); PQgetint4(res, 0, i++, &i4); PQgetint8(res, 0, i++, &i8); PQgetfloat4(res, 0, i++, &f4); PQgetfloat8(res, 0, i++, &f8); text = PQgetvalue(res, 0, i++); text_isnull = PQgetisnull(res, 0, i++); /* bytea handling. There is no PQgetbytea function! PQgetvalue * should be used. */ if(resultFormat) { bytea2 = memcpy(bytea, PQgetvalue(res, 0, i), PQgetlength(res, 0, i)); i++; } /* have to unescape bytea. */ else { size_t x; bytea2 = PQunescapeBytea( (const unsigned char *)PQgetvalue(res, 0, i++), &x); if(!bytea2) { printf("\nFailed to unescape bytea\n"); bytea2 = bytea; } } PQgetmacaddr(res, 0, i++, &mac); PQgetpoint(res, 0, i++, &pt); PQgetlseg(res, 0, i++, &lseg); PQgetbox(res, 0, i++, &box); PQgetcircle(res, 0, i++, &circle); PQgetpath(res, 0, i++, &path); PQgetpolygon(res, 0, i++, &polygon); printf( "char = %hhu\nbool = %d\nint2 = %hu\nint4 = %u\n" "int8 = " U64FMT "\nfloat4 = %.3f\nfloat8 = %lf\n" "text = '%s'\nnulltext = %s\nbytea = {%d, %d, %d, %d}\n" "macaddr = %02x:%02x:%02x:%02x:%02x:%02x\n" "point = (%lf,%lf)\nlseg = [(%lf,%lf),(%lf,%lf)]\n" "box = (%lf,%lf),(%lf,%lf)\ncircle = <(%lf,%lf),%lf>\n", ch, b, i2, i4, i8, f4, f8, text, text_isnull ? "ISNULL" : "NOTNULL", bytea2[0], bytea2[1], bytea2[2], bytea2[3], mac.a, mac.b, mac.c, mac.d, mac.e, mac.f, pt.x, pt.y, lseg.pts[0].x, lseg.pts[0].y, lseg.pts[1].x, lseg.pts[1].y, box.high.x, box.high.y, box.low.x, box.low.y, circle.center.x, circle.center.y, circle.radius); if(resultFormat==0 && bytea2 != bytea) PQfreemem(bytea2); printf("path[%d] = %c", path.npts, path.closed ? '(' : '['); for(i=0; i < path.npts; i++) { printf("(%lf,%lf)", path.pts[i].x, path.pts[i].y); if(i+1 != path.npts) printf(","); } printf("%c\n", path.closed ? ')' : ']'); /* must free path points array! */ if(path.pts) PQfreemem(path.pts); printf("polygon[%d] = (", polygon.npts); for(i=0; i < polygon.npts; i++) { printf("(%lf,%lf)", polygon.pts[i].x, polygon.pts[i].y); if(i+1 != polygon.npts) printf(","); } printf(")\n"); /* must free polygon points array! */ if(polygon.pts) PQfreemem(polygon.pts); PQclear(res); PQfinish(conn); return 0; } static int getresfmt(int argc, char **argv) { int i; if(argc == 1) return 1; /* lazy man getopt */ for(i=1; i < argc; i++) { if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { printf("-t Indicates that resultFormat should be text. By default\n" " resultFormat is binary. Always good to test both\n" " because text and binary results are handled differently.\n"); exit(0); } else if(!strcmp(argv[i], "-t")) { return 0; } } return 1; }
---------------------------(end of broadcast)--------------------------- TIP 1: if posting/reading through Usenet, please send an appropriate subscribe-nomail command to [EMAIL PROTECTED] so that your message can get through to the mailing list cleanly