Thread: Enum binary access

Enum binary access

From
Petr Chmelar
Date:
Hi there,

we tried to create the libpqtypes enum binary send but it doesn't work:

// register types
PGregisterType user_def[] = { {"seqtype", enum_put, enum_get} };
PQregisterTypes(connector->getConn(), PQT_USERDEFINED, user_def, 1, 0);

// enum_put throws format error
int enum_put (PGtypeArgs *args ) {
    char *val = va_arg(args->ap, char *);    char *out = NULL;    int vallen = 0, len = 0, oid = 0;    float sortorder
=0.0;
 
    if (!args || !val) return 0;
    /* expand buffer enough */    vallen = strlen(val);    len = sizeof(int) + sizeof(float) + (vallen * sizeof(char));
  if (args->put.expandBuffer(args, len) == -1) return -1;
 
    /* put header (oid, sortorder) and value */    out = args->put.out;    memcpy(out, &oid, sizeof(int));    out +=
sizeof(int);   memcpy(out, &sortorder, sizeof(float));    out += sizeof(float);    memcpy(out, val, vallen);
 
    return len;
}

// enum_get (FYI, get works OK)
int enum_get (PGtypeArgs *args) {    char *val = PQgetvalue(args->get.result, args->get.tup_num, 
args->get.field_num);    int len = PQgetlength(args->get.result, args->get.tup_num, 
args->get.field_num);        char **result = va_arg(args->ap, char **);    *result = (char *) PQresultAlloc((PGresult
*)args->get.result, len 
 
* sizeof(char));    memcpy(*result, val, len * sizeof(char));        return 0;
}

Postgres doesn't accept enum sent like this and throws format error. 
This should be used as a prototype for derived types. There is no real 
"enum" named type. Libpqypes doesn't seem to provide simplified binary 
manipulation for enum types.

What should we do, please? Can you fix it? I think there is more people 
that need access enum types in binary mode.

Cheers,

Petr and Vojtech


P.S. We have created the 9.1's cube extension send/receive functionality 
as in https://gitorious.org/vtapi/vtapi/trees/master/postgres/cube



Re: Enum binary access

From
Merlin Moncure
Date:
On Mon, Sep 10, 2012 at 8:42 AM, Petr Chmelar <chmelarp@fit.vutbr.cz> wrote:
> Hi there,
>
> we tried to create the libpqtypes enum binary send but it doesn't work:
>
> // register types
> PGregisterType user_def[] = { {"seqtype", enum_put, enum_get} };
> PQregisterTypes(connector->getConn(), PQT_USERDEFINED, user_def, 1, 0);
>
> // enum_put throws format error
> int enum_put (PGtypeArgs *args ) {
>
>     char *val = va_arg(args->ap, char *);
>     char *out = NULL;
>     int vallen = 0, len = 0, oid = 0;
>     float sortorder = 0.0;
>
>     if (!args || !val) return 0;
>
>     /* expand buffer enough */
>     vallen = strlen(val);
>     len = sizeof(int) + sizeof(float) + (vallen * sizeof(char));
>     if (args->put.expandBuffer(args, len) == -1) return -1;
>
>     /* put header (oid, sortorder) and value */
>     out = args->put.out;
>     memcpy(out, &oid, sizeof(int));
>     out += sizeof(int);
>     memcpy(out, &sortorder, sizeof(float));
>     out += sizeof(float);
>     memcpy(out, val, vallen);
>
>     return len;
> }
>
> // enum_get (FYI, get works OK)
> int enum_get (PGtypeArgs *args) {
>     char *val = PQgetvalue(args->get.result, args->get.tup_num,
> args->get.field_num);
>     int len = PQgetlength(args->get.result, args->get.tup_num,
> args->get.field_num);
>         char **result = va_arg(args->ap, char **);
>     *result = (char *) PQresultAlloc((PGresult *) args->get.result, len *
> sizeof(char));
>     memcpy(*result, val, len * sizeof(char));
>         return 0;
> }
>
> Postgres doesn't accept enum sent like this and throws format error. This
> should be used as a prototype for derived types. There is no real "enum"
> named type. Libpqypes doesn't seem to provide simplified binary manipulation
> for enum types.
>
> What should we do, please? Can you fix it? I think there is more people that
> need access enum types in binary mode.

I was able to get it to work what I did:

*) your 'get' routine should probably be allocating a terminating
byte.  In binary situations, PQgetlength does not return the length of
the null terminator.

*) backend binary format for enums is just the label text string both
on get and put side.  So your putting the oid and sort order was
breaking the put side.  Removed that and everything worked.

*) see (very messy quickly written) code below:


#include "libpq-fe.h"
#include "libpqtypes.h"
#include "string.h"


// enum_put throws format error
int enum_put (PGtypeArgs *args ) {
   char *val = va_arg(args->ap, char *);   char *out = NULL;   int vallen = 0, len = 0, oid = 0;   float sortorder =
0.0;
   if (!args || !val) return 0;
   /* expand buffer enough */   vallen = strlen(val);   if (args->put.expandBuffer(args, len) == -1) return -1;
   out = args->put.out;   memcpy(out, val, vallen);
   return len;
}

// enum_get (FYI, get works OK)
int enum_get (PGtypeArgs *args) {   char *val = PQgetvalue(args->get.result, args->get.tup_num,
args->get.field_num);   int len = PQgetlength(args->get.result, args->get.tup_num,
args->get.field_num) + 1;       char **result = va_arg(args->ap, char **);   *result = (char *) PQresultAlloc((PGresult
*)args->get.result,
 
len * sizeof(char));   memcpy(*result, val, len * sizeof(char));   result[len] = 0;       return 0;
}


int main()
{ PGtext t = "b";
 PGconn *conn = PQconnectdb("port=5492 host=localhost"); PQinitTypes(conn);
 PGregisterType user_def[] = { {"e", enum_put, enum_get} };
 if(!PQregisterTypes(conn, PQT_USERDEFINED, user_def, 1, 0))   fprintf(stderr, "*ERROR: %s\n", PQgeterror());

 PGresult *res = PQexecf(conn, "select %e", t);
 if(!res)   fprintf(stderr, "*ERROR: %s\n", PQgeterror());
 if (!PQgetf(res, 0, "%e", 0, &t)) {   fprintf(stderr, "*ERROR: %s\n", PQgeterror()); }
 printf("%s\n", t);
 PQclear(res);
}



Re: Enum binary access

From
Petr Chmelar
Date:
Hi Merlin,

thanks, your code works perfectly.

Cheers,

Petr

On 10.9.2012 16:33, Merlin Moncure wrote:
> On Mon, Sep 10, 2012 at 8:42 AM, Petr Chmelar <chmelarp@fit.vutbr.cz> wrote:
>> Hi there,
>>
>> we tried to create the libpqtypes enum binary send but it doesn't work:
>>
>> // register types
>> PGregisterType user_def[] = { {"seqtype", enum_put, enum_get} };
>> PQregisterTypes(connector->getConn(), PQT_USERDEFINED, user_def, 1, 0);
>>
>> // enum_put throws format error
>> int enum_put (PGtypeArgs *args ) {
>>
>>      char *val = va_arg(args->ap, char *);
>>      char *out = NULL;
>>      int vallen = 0, len = 0, oid = 0;
>>      float sortorder = 0.0;
>>
>>      if (!args || !val) return 0;
>>
>>      /* expand buffer enough */
>>      vallen = strlen(val);
>>      len = sizeof(int) + sizeof(float) + (vallen * sizeof(char));
>>      if (args->put.expandBuffer(args, len) == -1) return -1;
>>
>>      /* put header (oid, sortorder) and value */
>>      out = args->put.out;
>>      memcpy(out, &oid, sizeof(int));
>>      out += sizeof(int);
>>      memcpy(out, &sortorder, sizeof(float));
>>      out += sizeof(float);
>>      memcpy(out, val, vallen);
>>
>>      return len;
>> }
>>
>> // enum_get (FYI, get works OK)
>> int enum_get (PGtypeArgs *args) {
>>      char *val = PQgetvalue(args->get.result, args->get.tup_num,
>> args->get.field_num);
>>      int len = PQgetlength(args->get.result, args->get.tup_num,
>> args->get.field_num);
>>          char **result = va_arg(args->ap, char **);
>>      *result = (char *) PQresultAlloc((PGresult *) args->get.result, len *
>> sizeof(char));
>>      memcpy(*result, val, len * sizeof(char));
>>          return 0;
>> }
>>
>> Postgres doesn't accept enum sent like this and throws format error. This
>> should be used as a prototype for derived types. There is no real "enum"
>> named type. Libpqypes doesn't seem to provide simplified binary manipulation
>> for enum types.
>>
>> What should we do, please? Can you fix it? I think there is more people that
>> need access enum types in binary mode.
> I was able to get it to work what I did:
>
> *) your 'get' routine should probably be allocating a terminating
> byte.  In binary situations, PQgetlength does not return the length of
> the null terminator.
>
> *) backend binary format for enums is just the label text string both
> on get and put side.  So your putting the oid and sort order was
> breaking the put side.  Removed that and everything worked.
>
> *) see (very messy quickly written) code below:
>
>
> #include "libpq-fe.h"
> #include "libpqtypes.h"
> #include "string.h"
>
>
> // enum_put throws format error
> int enum_put (PGtypeArgs *args ) {
>
>      char *val = va_arg(args->ap, char *);
>      char *out = NULL;
>      int vallen = 0, len = 0, oid = 0;
>      float sortorder = 0.0;
>
>      if (!args || !val) return 0;
>
>      /* expand buffer enough */
>      vallen = strlen(val);
>      if (args->put.expandBuffer(args, len) == -1) return -1;
>
>      out = args->put.out;
>      memcpy(out, val, vallen);
>
>      return len;
> }
>
> // enum_get (FYI, get works OK)
> int enum_get (PGtypeArgs *args) {
>      char *val = PQgetvalue(args->get.result, args->get.tup_num,
> args->get.field_num);
>      int len = PQgetlength(args->get.result, args->get.tup_num,
> args->get.field_num) + 1;
>          char **result = va_arg(args->ap, char **);
>      *result = (char *) PQresultAlloc((PGresult *) args->get.result,
> len * sizeof(char));
>      memcpy(*result, val, len * sizeof(char));
>      result[len] = 0;
>          return 0;
> }
>
>
> int main()
> {
>    PGtext t = "b";
>
>    PGconn *conn = PQconnectdb("port=5492 host=localhost");
>    PQinitTypes(conn);
>
>    PGregisterType user_def[] = { {"e", enum_put, enum_get} };
>
>    if(!PQregisterTypes(conn, PQT_USERDEFINED, user_def, 1, 0))
>      fprintf(stderr, "*ERROR: %s\n", PQgeterror());
>
>
>    PGresult *res = PQexecf(conn, "select %e", t);
>
>    if(!res)
>      fprintf(stderr, "*ERROR: %s\n", PQgeterror());
>
>    if (!PQgetf(res, 0, "%e", 0, &t))
>    {
>      fprintf(stderr, "*ERROR: %s\n", PQgeterror());
>    }
>
>    printf("%s\n", t);
>
>    PQclear(res);
> }
>
>