Thread: libpq, PQExecParams and the inserting of binary data

libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
I have been trying to figure this out all morning, and I've gotten no
where so far.

I am trying to insert binary data into a bytea column with
PQExecParams.  I couldn't find any documentation on using PQExecParams
with binary parameters, do I tried to do it this way:

int _ma_logbody(struct MailData *MailData, char *bodyp, size_t bodylen)   {   const char *paramValues[2];   char
*text_body;  PGresult   *res;   size_t      newlen; 
   text_body = PQescapeBytea(bodyp, bodylen, &newlen);
   paramValues[0] = MailData->MsgId;   paramValues[1] = text_body;
   res = PQexecParams(conn, "insert into ma_body (msg_id, body)
VALUES ($1, $2);",           2,       /* params */           NULL,    /* let the backend deduce param type */
paramValues,          NULL,    /* don't need param lengths since text */           NULL,    /* default to all text
params*/           0); 
   PQfreemem(text_body);
   if (PQresultStatus(res) != PGRES_COMMAND_OK)       {       log(LOG_MAIL, "Postresql insert failed: %s",
PQerrorMessage(conn));      log(LOG_MAIL, "bodylen: %d, bodyp: %s", bodylen, bodyp);       PQclear(res);
PQfinish(conn);      conn = NULL;       return(0);       } 
   PQclear(res);
   return(TRUE);   }

As you can see, I assumed I could use PQexapeBytea to escape the
binary data and then just use the returned value as a text parameter.
However,  I randomly get insert errors with the error message:
"invalid input syntax for type Bytea".

If anybody could help me find the right way to do this I'd really
apreciate it...


Re: libpq, PQExecParams and the inserting of binary data

From
Volkan YAZICI
Date:
Hi,

On 6/3/05, David Hinkle <drachs@gmail.com> wrote:
> As you can see, I assumed I could use PQexapeBytea to escape the
> binary data and then just use the returned value as a text parameter.

You don't need to (also you shouldn't) escape any data while using
parameters. Because, you'll miss one big advantage of parameter usage.
From PQexecParams() documentation:

«The primary advantage of PQexecParams over PQexec is that parameter
values may be separated from the command string, thus avoiding the
need for tedious and error-prone quoting and escaping. Unlike PQexec,
PQexecParams allows at most one SQL command in the given string.
(There can be semicolons in it, but not more than one nonempty
command.) This is a limitation of the underlying protocol, but has
some usefulness as an extra defense against SQL-injection attacks.»

Regards.


Re: libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
Yea, I know, but that doesn't help me.  I need to know how to insert
binary data.  I can't do it as text without escaping because it's not
null terminated and may contain nulls.  I can't do it as binary
because I don't have any documentation on how to use it that way.
What I'm asking for is an expample or a document showing me how to do
it.  I already know my way doesn't work.

If you could show me the proper way to do it that would be great.

David


On 6/3/05, Volkan YAZICI <volkan.yazici@gmail.com> wrote:
> Hi,
>
> On 6/3/05, David Hinkle <drachs@gmail.com> wrote:
> > As you can see, I assumed I could use PQexapeBytea to escape the
> > binary data and then just use the returned value as a text parameter.
>
> You don't need to (also you shouldn't) escape any data while using
> parameters. Because, you'll miss one big advantage of parameter usage.
> From PQexecParams() documentation:
>
> «The primary advantage of PQexecParams over PQexec is that parameter
> values may be separated from the command string, thus avoiding the
> need for tedious and error-prone quoting and escaping. Unlike PQexec,
> PQexecParams allows at most one SQL command in the given string.
> (There can be semicolons in it, but not more than one nonempty
> command.) This is a limitation of the underlying protocol, but has
> some usefulness as an extra defense against SQL-injection attacks.»
>
> Regards.
>


Re: libpq, PQExecParams and the inserting of binary data

From
Tom Lane
Date:
David Hinkle <drachs@gmail.com> writes:
> As you can see, I assumed I could use PQexapeBytea to escape the
> binary data and then just use the returned value as a text parameter. 

No, because PQescapeBytea is designed to do the escaping that would be
needed to put the bytea value into a string literal in a SQL command.
There's an extra level of backslashing involved to do that (because
backslashes are special to both the string-literal syntax and the
text input syntax for bytea).

But this is really the hard way to do it considering that you are using 
PQexecParams.  Just tell PQexecParams that you want this parameter to be
binary format, and pass the *raw* binary string as the parameter.

> I couldn't find any documentation on using PQExecParams
> with binary parameters,

src/test/examples/testlibpq3.c ... that example is reproduced in
the libpq manual, as well.
        regards, tom lane


Re: libpq, PQExecParams and the inserting of binary data

From
"Daniel Verite"
Date:
    David Hinkle wrote:

> I am trying to insert binary data into a bytea column with
> PQExecParams.  I couldn't find any documentation on using PQExecParams
> with binary parameters, do I tried to do it this way:
> [...]

This code snippet may help you getting started:

http://archives.postgresql.org/pgsql-general/2004-07/msg00875.php

-- DanielPostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org



Re: libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
I read all the examples and documentation thouroughly.  I would not
bother you guys otherwise.  That example doesn't show you how to use
any binary parameters... All the parameters are text, it only shows
you how get binary results.  I think it's a huge oversight myself.

I could probably figure out how to use the parameters as binary, but I
have no idea what to do with the OID field.

David

On 6/3/05, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> David Hinkle <drachs@gmail.com> writes:
> > As you can see, I assumed I could use PQexapeBytea to escape the
> > binary data and then just use the returned value as a text parameter.
>
> No, because PQescapeBytea is designed to do the escaping that would be
> needed to put the bytea value into a string literal in a SQL command.
> There's an extra level of backslashing involved to do that (because
> backslashes are special to both the string-literal syntax and the
> text input syntax for bytea).
>
> But this is really the hard way to do it considering that you are using
> PQexecParams.  Just tell PQexecParams that you want this parameter to be
> binary format, and pass the *raw* binary string as the parameter.
>
> > I couldn't find any documentation on using PQExecParams
> > with binary parameters,
>
> src/test/examples/testlibpq3.c ... that example is reproduced in
> the libpq manual, as well.
>
>                         regards, tom lane
>


Re: libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
Thankyou very much Daniel,

I will try this.  Out of curiosity, where does the value of 17 for the
OID field come from?

David

On 6/3/05, Daniel Verite <daniel@manitou-mail.org> wrote:
>         David Hinkle wrote:
>
> > I am trying to insert binary data into a bytea column with
> > PQExecParams.  I couldn't find any documentation on using PQExecParams
> > with binary parameters, do I tried to do it this way:
> > [...]
>
> This code snippet may help you getting started:
>
> http://archives.postgresql.org/pgsql-general/2004-07/msg00875.php
>
> --
>  Daniel
>  PostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org
>
>


Re: libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
Oh, and another thing Daniel,

Is it neccissary to provide the OID of the other fields even though
they're provided in text format?   I guess what I really need to
understand is what the OID field is and how to use it.

David

On 6/3/05, Daniel Verite <daniel@manitou-mail.org> wrote:
>         David Hinkle wrote:
>
> > I am trying to insert binary data into a bytea column with
> > PQExecParams.  I couldn't find any documentation on using PQExecParams
> > with binary parameters, do I tried to do it this way:
> > [...]
>
> This code snippet may help you getting started:
>
> http://archives.postgresql.org/pgsql-general/2004-07/msg00875.php
>
> --
>  Daniel
>  PostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org
>
>


Re: libpq, PQExecParams and the inserting of binary data

From
Robert Perry
Date:
    While on the subject might I ask where I could find some documentation 
of the raw binary format for other types such a Date and Decimal(,)?

Thanks
Robert Perry


On Jun 3, 2005, at 1:48 PM, Tom Lane wrote:

> David Hinkle <drachs@gmail.com> writes:
>> As you can see, I assumed I could use PQexapeBytea to escape the
>> binary data and then just use the returned value as a text parameter.
>
> No, because PQescapeBytea is designed to do the escaping that would be
> needed to put the bytea value into a string literal in a SQL command.
> There's an extra level of backslashing involved to do that (because
> backslashes are special to both the string-literal syntax and the
> text input syntax for bytea).
>
> But this is really the hard way to do it considering that you are using
> PQexecParams.  Just tell PQexecParams that you want this parameter to 
> be
> binary format, and pass the *raw* binary string as the parameter.
>
>> I couldn't find any documentation on using PQExecParams
>> with binary parameters,
>
> src/test/examples/testlibpq3.c ... that example is reproduced in
> the libpq manual, as well.
>
>             regards, tom lane
>
> ---------------------------(end of 
> broadcast)---------------------------
> TIP 8: explain analyze is your friend
>



Re: libpq, PQExecParams and the inserting of binary data

From
David Hinkle
Date:
It would be great if someone could take  the time to write this up and
include it in the manual, I have to do about a million extraniouse
text conversions, small string allocations and deallocations because I
couldn't find any docs.

David

On 6/3/05, Robert Perry <rlperry@lodestonetechnologies.com> wrote:
>         While on the subject might I ask where I could find some documentation
> of the raw binary format for other types such a Date and Decimal(,)?
>
> Thanks
> Robert Perry
>
>
> On Jun 3, 2005, at 1:48 PM, Tom Lane wrote:
>
> > David Hinkle <drachs@gmail.com> writes:
> >> As you can see, I assumed I could use PQexapeBytea to escape the
> >> binary data and then just use the returned value as a text parameter.
> >
> > No, because PQescapeBytea is designed to do the escaping that would be
> > needed to put the bytea value into a string literal in a SQL command.
> > There's an extra level of backslashing involved to do that (because
> > backslashes are special to both the string-literal syntax and the
> > text input syntax for bytea).
> >
> > But this is really the hard way to do it considering that you are using
> > PQexecParams.  Just tell PQexecParams that you want this parameter to
> > be
> > binary format, and pass the *raw* binary string as the parameter.
> >
> >> I couldn't find any documentation on using PQExecParams
> >> with binary parameters,
> >
> > src/test/examples/testlibpq3.c ... that example is reproduced in
> > the libpq manual, as well.
> >
> >                       regards, tom lane
> >
> > ---------------------------(end of
> > broadcast)---------------------------
> > TIP 8: explain analyze is your friend
> >
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: the planner will ignore your desire to choose an index scan if your
>       joining column's datatypes do not match
>


Re: libpq, PQExecParams and the inserting of binary data

From
Tom Lane
Date:
David Hinkle <drachs@gmail.com> writes:
> ...  That example doesn't show you how to use
> any binary parameters... All the parameters are text, it only shows
> you how get binary results.  I think it's a huge oversight myself.

Duh, you are right.  Will fix that.

> I could probably figure out how to use the parameters as binary, but I
> have no idea what to do with the OID field.

If you didn't need it for the text version, you won't need it for the
binary version.  The type OID is only needed if the query is such that
the backend can't determine the type of the parameter symbol from
context.  Whether you send the parameter value as text or binary has
nada to do with that.
        regards, tom lane


Re: libpq, PQExecParams and the inserting of binary data

From
"Daniel Verite"
Date:
    David Hinkle wrote:

> I will try this.  Out of curiosity, where does the value of 17 for the
> OID field come from?

From the pg_type table:

template1=# select oid from pg_type where typname='bytea';oid 
----- 17
(1 row)

Alternatively, there's the server/catalog/pg_type.h include file
which has #defines for built-in datatypes:

$ grep BYTEA server/catalog/pg_type.h
#define BYTEAOID                17

-- DanielPostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org


Re: libpq, PQExecParams and the inserting of binary data

From
"Daniel Verite"
Date:
    David Hinkle wrote:

> Is it neccissary to provide the OID of the other fields even though
> they're provided in text format?   I guess what I really need to
> understand is what the OID field is and how to use it.

As far as I understand, no it's not necessary. 0 instead of an
OID in the paramTypes array is fine for any parameter as long
as the type of this parameter can be correctly guessed by the
server at execution time.

-- DanielPostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org



Re: libpq, PQExecParams and the inserting of binary data

From
Volkan YAZICI
Date:
Hi,

On 6/3/05, David Hinkle <drachs@gmail.com> wrote:
> [snipped]
> What I'm asking for is an expample or a document showing me how to do
> it.

While trying to prepare an example for your question, I scracthed sth.
like this:

{{{ Code snippet
const char *command = "INSERT INTO tbl1 VALUES ($1)";
int nParams = 1;
const Oid paramTypes[] = {17};      // 17, for bytea type's OID.
const char * const paramValues[] = {"\\101\\102\\103"}; // "ABC"
const int *paramLengths = NULL;     // Letting the backend to
const int *paramFormats = NULL;     // deduce these params.
int resultFormat = 0;               // Result will be in text format.

conn = PQconnectdb("dbname=test");
if ( PQstatus(conn) != CONNECTION_OK ) {   fprintf(stderr, "Connection failed!\n%s", PQerrorMessage(conn));
exit_nicely(conn);
}

printf("Command: \"%s\",\n", command);
printf("Param. : \"%s\".\n", paramValues[0]);
res = PQexecParams(conn, command, nParams, paramTypes, paramValues,                  paramLengths, paramFormats,
resultFormat);

if ( PQresultStatus(res) != PGRES_COMMAND_OK ) {   fprintf(stderr, "Insert failed!\n%s", PQresultErrorMessage(res));
PQclear(res);  exit_nicely(conn); 
} else {   printf("Insert succeeded.\n");   PQclear(res);
}
}}}

Above code is working well for me. But while trying some other
combinations (out of NULL usage) for PQexecParams parameters, I
realized that when we use "const int paramFormats[] = {1};" execution
dumps SegFault:

{{{ Command output snippet
$ gcc -g -Wall -lpq binIns.c && ./a.out
Command: "INSERT INTO tbl1 VALUES ($1)",
Param. : "\101\102\103".
Segmentation fault
$ gdb ./a.out
[snipped]
Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) run
Starting program: /home/knt/temp/a.out
Command: "INSERT INTO tbl1 VALUES ($1)",
Param. : "\101\102\103".

Program received signal SIGSEGV, Segmentation fault.
0xb7fcee05 in PQsendQueryGuts () from /usr/lib/libpq.so.4
(gdb) bt
#0  0xb7fcee05 in PQsendQueryGuts () from /usr/lib/libpq.so.4
#1  0xb7fce902 in PQsendQueryParams () from /usr/lib/libpq.so.4
#2  0xb7fcf420 in PQexecParams () from /usr/lib/libpq.so.4
#3  0x08048811 in main () at binIns.c:34
}}}

[Line 34: "res = PQexecParams(conn, ..."]

Is this an expected behaviour or did I miss sth. important?

Regards.


Re: libpq, PQExecParams and the inserting of binary data

From
"Daniel Verite"
Date:
    Volkan YAZICI wrote:

> res = PQexecParams(conn, command, nParams, paramTypes, paramValues, 
>                 paramLengths, paramFormats, resultFormat); 
[...]
> Above code is working well for me. But while trying some other
> combinations (out of NULL usage) for PQexecParams parameters, I
> realized that when we use "const int paramFormats[] = {1};" execution
> dumps SegFault:

Are you passing a NULL pointer in paramLengths? You need to
pass an array containing the actual length of your binary data.

-- DanielPostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org



Re: libpq, PQExecParams and the inserting of binary data

From
Volkan YAZICI
Date:
Hi,

On 6/4/05, Daniel Verite <daniel@manitou-mail.org> wrote:
> Are you passing a NULL pointer in paramLengths? You need to
> pass an array containing the actual length of your binary data.

Yes, paramLengthts is a NULL pointer. But, is SegFault an expected result?

Himms... When I take a look at src/interfaces/libpq/fe-exec.c:

956 if (paramValues && paramValues[i])
957 {
958     int         nbytes;
960     if (paramFormats && paramFormats[i] != 0)
961     {
962         /* binary parameter */
963         nbytes = paramLengths[i];

It appears to be SegFault was caused by paramLengths[i] on line 963.
So, this means, user _has_to_ specify the size of input while using
binary data. Maybe a "if ( paramLengths )" check can be useful.

Regards.


Re: libpq, PQExecParams and the inserting of binary data

From
Tom Lane
Date:
Volkan YAZICI <volkan.yazici@gmail.com> writes:
> So, this means, user _has_to_ specify the size of input while using
> binary data.

You expected something different?  libpq could hardly assume that
strlen() is the way to find out the length of a binary parameter.
        regards, tom lane


Re: libpq, PQExecParams and the inserting of binary data

From
Volkan YAZICI
Date:
Hi,

On 6/4/05, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Volkan YAZICI <volkan.yazici@gmail.com> writes:
> > So, this means, user _has_to_ specify the size of input while using
> > binary data.
>
> You expected something different?

No. But, throwing an exception inspite of a segmentation fault could be better.

Regards.