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

Attachment: 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

Reply via email to