Thread: exec_execute_message crush

exec_execute_message crush

From
Tatsuo Ishii
Date:
While inspecting a complain from a pgpool user, I found that
PostgreSQL crushes with following statck trace:

#0  0x0826436a in list_length (l=0xaabe4e28)   at ../../../src/include/nodes/pg_list.h:94
#1  0x08262168 in IsTransactionStmtList (parseTrees=0xaabe4e28)   at postgres.c:2429
#2  0x0826132e in exec_execute_message (portal_name=0x857bab0 "", max_rows=0)   at postgres.c:1824
#3  0x08263b2a in PostgresMain (argc=4, argv=0x84f6c28,   username=0x84f6b08 "t-ishii") at postgres.c:3671
#4  0x0823299e in BackendRun (port=0x8511e68) at postmaster.c:3449
#5  0x08231f78 in BackendStartup (port=0x8511e68) at postmaster.c:3063
#6  0x0822f90a in ServerLoop () at postmaster.c:1387
#7  0x0822f131 in PostmasterMain (argc=3, argv=0x84f4bf8) at postmaster.c:1040
#8  0x081c6217 in main (argc=3, argv=0x84f4bf8) at main.c:188

This happens with following extended commands sequence:

parse
bind
describe
execute
<normaly done>
parse invalid SQL thus abort a transaction
bind (error)
describe (error)
execute (crush)

exec_execute_message crushes here:
/* Does the portal contain a transaction command? */is_xact_command = IsTransactionStmtList(portal->stmts);

Looking into portal:

$5 = {name = 0x85727bc "", prepStmtName = 0x0, heap = 0x8596798, resowner = 0x0, cleanup = 0, createSubid = 1,
sourceText= 0x859ac78 " SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,  ct.relname AS TABLE_NAME,  a.attname AS
COLUMN_NAME, a.attnum AS KEY_SEQ,  ci.relname AS PK_NAME  FROM pg_catalog.pg_namespace n, pg_catalog.pg_c"...,
commandTag= 0x84682aa "SELECT", stmts = 0xaabe4e28, cplan = 0x0, portalParams = 0x0, strategy = PORTAL_ONE_SELECT,
cursorOptions= 4, status = PORTAL_READY, queryDesc = 0x0, tupDesc = 0x85db060, formats = 0x859b0c8, holdStore = 0x0,
holdContext= 0x0, atStart = 1 '\001', atEnd = 1 '\001', posOverflow = 0 '\0', portalPos = 0, creation_time =
315313855337710,visible = 1 '\001'}
 

Problem is, stmts points to invalid memory address:

(gdb) p *portal->stmts
Cannot access memory at address 0xaabe4e28

It seems the source of the problem is, exec_execute_message tries to
execute unamed portal which has unnamed statement which has already
gone.

Please note that without pgpool backend does not crush. This is
because JDBC driver does not do execute() if prior parse, bind
etc. failed, I think.

The crush happens PostgreSQL 8.3.8, 8.3.9 and 8.4.2.

Any thought?
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crush

From
Tom Lane
Date:
Tatsuo Ishii <ishii@postgresql.org> writes:
> It seems the source of the problem is, exec_execute_message tries to
> execute unamed portal which has unnamed statement which has already
> gone.

Could we see an actual test case?
        regards, tom lane


Re: exec_execute_message crush

From
Tatsuo Ishii
Date:
> Tatsuo Ishii <ishii@postgresql.org> writes:
> > It seems the source of the problem is, exec_execute_message tries to
> > execute unamed portal which has unnamed statement which has already
> > gone.
> 
> Could we see an actual test case?

If you don't mind to use pgpool, it would be possible. If not, I need
to write a small program which handles frontend/backend protocol
directly. What shall I do?
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crush

From
Tom Lane
Date:
Tatsuo Ishii <ishii@postgresql.org> writes:
>> Could we see an actual test case?

> If you don't mind to use pgpool, it would be possible. If not, I need
> to write a small program which handles frontend/backend protocol
> directly. What shall I do?

Hm, can't you get libpq to do it?
        regards, tom lane


Re: exec_execute_message crush

From
Tatsuo Ishii
Date:
> > If you don't mind to use pgpool, it would be possible. If not, I need
> > to write a small program which handles frontend/backend protocol
> > directly. What shall I do?
> 
> Hm, can't you get libpq to do it?

That depends on how libpq is "intelligent":-) Let me try...

Another idea is a "packet recorder", which could record packets from
pgpool to PostgreSQL and replay them. I don't remember at present, but
I vaguely recall something like that exists.
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crush

From
Tatsuo Ishii
Date:
> > Hm, can't you get libpq to do it?
> 
> That depends on how libpq is "intelligent":-) Let me try...
> 
> Another idea is a "packet recorder", which could record packets from
> pgpool to PostgreSQL and replay them. I don't remember at present, but
> I vaguely recall something like that exists.

It seems we can't get libpq to do it. libpq does not provide a
function which can execute bind alone. In my understanding
PQexecPrepared does bind + execute.
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crush

From
Tom Lane
Date:
Tatsuo Ishii <ishii@postgresql.org> writes:
>>> Hm, can't you get libpq to do it?

> It seems we can't get libpq to do it. libpq does not provide a
> function which can execute bind alone. In my understanding
> PQexecPrepared does bind + execute.

The event sequence you mentioned had bind followed by execute, so
I'm not seeing the problem.

(In any case, some kind of quick lobotomy in libpq would be easier
than writing a standalone test program, no?)
        regards, tom lane


Re: exec_execute_message crush

From
Tatsuo Ishii
Date:
> (In any case, some kind of quick lobotomy in libpq would be easier
> than writing a standalone test program, no?)

Sounds nice idea.
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crush

From
Kris Jurka
Date:

On Tue, 29 Dec 2009, Tatsuo Ishii wrote:

> parse
> bind
> describe
> execute
> <normaly done>
> parse invalid SQL thus abort a transaction
> bind (error)
> describe (error)
> execute (crush)
>
> Please note that without pgpool backend does not crush. This is
> because JDBC driver does not do execute() if prior parse, bind
> etc. failed, I think.

The JDBC driver will fire away parse, bind, and execute all at once before 
a sync, to avoid network roundtrips, so your assumption of what's going on 
here without pgpool doesn't seem accurate.  Attached is a test case that 
tries to duplicate what you've described and it errors out normally. 
Below is the JDBC driver's protocol level logging.

21:41:39.407 (1)  FE=> Parse(stmt=S_1,query="BEGIN",oids={})
21:41:39.407 (1)  FE=> Bind(stmt=S_1,portal=null)
21:41:39.407 (1)  FE=> Execute(portal=null,limit=0)
21:41:39.408 (1)  FE=> Parse(stmt=null,query="SELECT $1 ",oids={23})
21:41:39.408 (1)  FE=> Bind(stmt=null,portal=null,$1=<'1'>)
21:41:39.408 (1)  FE=> Describe(portal=null)
21:41:39.408 (1)  FE=> Execute(portal=null,limit=0)
21:41:39.408 (1)  FE=> Parse(stmt=null,query=" SELECT SELECT $1 
",oids={23})
21:41:39.408 (1)  FE=> Bind(stmt=null,portal=null,$1=<'2'>)
21:41:39.409 (1)  FE=> Describe(portal=null)
21:41:39.409 (1)  FE=> Execute(portal=null,limit=0)
21:41:39.409 (1)  FE=> Sync
21:41:39.443 (1)  <=BE ParseComplete [S_1]
21:41:39.443 (1)  <=BE BindComplete [null]
21:41:39.443 (1)  <=BE CommandStatus(BEGIN)
21:41:39.443 (1)  <=BE ParseComplete [null]
21:41:39.443 (1)  <=BE BindComplete [null]
21:41:39.444 (1)  <=BE RowDescription(1)
21:41:39.444 (1)  <=BE DataRow
21:41:39.444 (1)  <=BE CommandStatus(SELECT)
21:41:39.454 (1)  <=BE ErrorMessage(ERROR: syntax error at or near 
"SELECT"  Position: 9)

So this shows everything working as expected.  Perhaps enabling this 
logging on your JDBC client would show more clearly what it is trying to 
do.

Kris Jurka

Re: exec_execute_message crush

From
Tatsuo Ishii
Date:
> > parse
> > bind
> > describe
> > execute
> > <normaly done>
> > parse invalid SQL thus abort a transaction
> > bind (error)
> > describe (error)
> > execute (crush)
> >
> > Please note that without pgpool backend does not crush. This is
> > because JDBC driver does not do execute() if prior parse, bind
> > etc. failed, I think.
> 
> The JDBC driver will fire away parse, bind, and execute all at once before 
> a sync, to avoid network roundtrips, so your assumption of what's going on 
> here without pgpool doesn't seem accurate.  Attached is a test case that 
> tries to duplicate what you've described and it errors out normally. 
> Below is the JDBC driver's protocol level logging.
> 
> 21:41:39.407 (1)  FE=> Parse(stmt=S_1,query="BEGIN",oids={})
> 21:41:39.407 (1)  FE=> Bind(stmt=S_1,portal=null)
> 21:41:39.407 (1)  FE=> Execute(portal=null,limit=0)
> 21:41:39.408 (1)  FE=> Parse(stmt=null,query="SELECT $1 ",oids={23})
> 21:41:39.408 (1)  FE=> Bind(stmt=null,portal=null,$1=<'1'>)
> 21:41:39.408 (1)  FE=> Describe(portal=null)
> 21:41:39.408 (1)  FE=> Execute(portal=null,limit=0)
> 21:41:39.408 (1)  FE=> Parse(stmt=null,query=" SELECT SELECT $1 
> ",oids={23})
> 21:41:39.408 (1)  FE=> Bind(stmt=null,portal=null,$1=<'2'>)
> 21:41:39.409 (1)  FE=> Describe(portal=null)
> 21:41:39.409 (1)  FE=> Execute(portal=null,limit=0)
> 21:41:39.409 (1)  FE=> Sync
> 21:41:39.443 (1)  <=BE ParseComplete [S_1]
> 21:41:39.443 (1)  <=BE BindComplete [null]
> 21:41:39.443 (1)  <=BE CommandStatus(BEGIN)
> 21:41:39.443 (1)  <=BE ParseComplete [null]
> 21:41:39.443 (1)  <=BE BindComplete [null]
> 21:41:39.444 (1)  <=BE RowDescription(1)
> 21:41:39.444 (1)  <=BE DataRow
> 21:41:39.444 (1)  <=BE CommandStatus(SELECT)
> 21:41:39.454 (1)  <=BE ErrorMessage(ERROR: syntax error at or near 
> "SELECT"
>    Position: 9)
> 
> So this shows everything working as expected.  Perhaps enabling this 
> logging on your JDBC client would show more clearly what it is trying to 
> do.

Thanks for clarification. I will look into more between pgpool and
PostgreSQL packet exchange.
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crash

From
Tatsuo Ishii
Date:
> While inspecting a complain from a pgpool user, I found that
> PostgreSQL crushes with following statck trace:
> 
> #0  0x0826436a in list_length (l=0xaabe4e28)
>     at ../../../src/include/nodes/pg_list.h:94
> #1  0x08262168 in IsTransactionStmtList (parseTrees=0xaabe4e28)
>     at postgres.c:2429
> #2  0x0826132e in exec_execute_message (portal_name=0x857bab0 "", max_rows=0)
>     at postgres.c:1824
> #3  0x08263b2a in PostgresMain (argc=4, argv=0x84f6c28,
>     username=0x84f6b08 "t-ishii") at postgres.c:3671
> #4  0x0823299e in BackendRun (port=0x8511e68) at postmaster.c:3449
> #5  0x08231f78 in BackendStartup (port=0x8511e68) at postmaster.c:3063
> #6  0x0822f90a in ServerLoop () at postmaster.c:1387
> #7  0x0822f131 in PostmasterMain (argc=3, argv=0x84f4bf8) at postmaster.c:1040
> #8  0x081c6217 in main (argc=3, argv=0x84f4bf8) at main.c:188

Ok, I think I understand what's going on.

parse
bind
describe
execute

This sequence of commands create cached plan in unnamed portal.

$5 = {name = 0x8574de4 "", prepStmtName = 0x0, heap = 0x8598400, resowner = 0x8598488, cleanup = 0x81632ca
<PortalCleanup>,createSubid = 1, sourceText = 0x85ab818 " SELECT <omitted>"..., commandTag = 0x84682ca "SELECT", stmts
=0xaabf43b0, cplan = 0xaabf4950, portalParams = 0x0, strategy = PORTAL_ONE_SELECT, cursorOptions = 4, status =
PORTAL_READY,queryDesc = 0x85abc20, tupDesc = 0x85ddcb0, formats = 0x85abc68, holdStore = 0x0, holdContext = 0x0,
atStart= 1 '\001', atEnd = 1 '\001', posOverflow = 0 '\0', portalPos = 0, creation_time = 315487957498169, visible = 1
'\001'}

The cached plan(portal->cplan) and statements(portal->stmts) are
created by exec_bind_message():
    /*     * Revalidate the cached plan; this may result in replanning.  Any     * cruft will be generated in
MessageContext. The plan refcount will     * be assigned to the Portal, so it will be released at portal     *
destruction.    */    cplan = RevalidateCachedPlan(psrc, false);    plan_list = cplan->stmt_list;
 

Please note that cplan and stmts belong to the same memory context.

Then following commands are coming:

parse invalid SQL thus abort a transaction
bind (error)
describe (error)
execute (crash)

parse causes transaction to abort, which causes call to
AbortCurrentTransaction->AbortTransaction->AtAbort_portals->ReleaseCachedPlan. It
calls ReleaseCachePlan(portal->cplan). ReleaseCachePlan calls
MemoryContextDelete(plan->context) which destroys both portal->cplan
and portal->stmts.

That was the reason why I had segfault by accessing portal->stmts.

To fix this I think exec_execute_message should throw an error if
portal->cleanup is NULL, since portal->cleanup is NULLed by
AtAbort_Portals at transaction abort (or portal is dropped).

Here is a suggested fix:

diff -c postgres.c~ postgres.c
*** postgres.c~    2009-06-18 19:08:08.000000000 +0900
--- postgres.c    2009-12-30 21:34:49.000000000 +0900
***************
*** 1804,1810 ****         dest = DestRemoteExecute;      portal = GetPortalByName(portal_name);
!     if (!PortalIsValid(portal))         ereport(ERROR,                 (errcode(ERRCODE_UNDEFINED_CURSOR),
     errmsg("portal \"%s\" does not exist", portal_name)));
 
--- 1804,1810 ----         dest = DestRemoteExecute;      portal = GetPortalByName(portal_name);
!     if (!PortalIsValid(portal) || (PortalIsValid(portal) && portal->cleanup == NULL))         ereport(ERROR,
      (errcode(ERRCODE_UNDEFINED_CURSOR),                  errmsg("portal \"%s\" does not exist", portal_name)));
 

--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crash

From
Andrew Dunstan
Date:

Tatsuo Ishii wrote:
> !     if (!PortalIsValid(portal) || (PortalIsValid(portal) && portal->cleanup == NULL))
>  
>   


Surely the second call to PortalIsValid() is redundant.        if (( !PortalIsValid(portal)) || portal->cleanup ==
NULL)

should do it, no?

cheers

andrew


Re: exec_execute_message crash

From
Tatsuo Ishii
Date:
> Tatsuo Ishii wrote:
> > !     if (!PortalIsValid(portal) || (PortalIsValid(portal) && portal->cleanup == NULL))
> >  
> >   
> 
> 
> Surely the second call to PortalIsValid() is redundant.
>       
>     if (( !PortalIsValid(portal)) || portal->cleanup == NULL)
> 
> should do it, no?

Oops. You are right.
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crash

From
Tom Lane
Date:
Tatsuo Ishii <ishii@postgresql.org> writes:
> parse causes transaction to abort, which causes call to
> AbortCurrentTransaction->AbortTransaction->AtAbort_portals->ReleaseCachedPlan. It
> calls ReleaseCachePlan(portal->cplan). ReleaseCachePlan calls
> MemoryContextDelete(plan->context) which destroys both portal->cplan
> and portal->stmts.

> That was the reason why I had segfault by accessing portal->stmts.

> To fix this I think exec_execute_message should throw an error if
> portal->cleanup is NULL, since portal->cleanup is NULLed by
> AtAbort_Portals at transaction abort (or portal is dropped).

This is just a kluge, and a rather bad one I think.  The real problem
here is that AtAbort_Portals destroys the portal contents and doesn't
do anything to record the fact.  It should probably be putting the
portal into PORTAL_FAILED state, and what exec_execute_message ought
to be doing is checking for that.  It might be a good idea to explicitly
zero out the now-dangling pointers in the Portal struct, too.

It'd be nice to have a test case for this, hint hint ...
        regards, tom lane


Re: exec_execute_message crash

From
Tatsuo Ishii
Date:
> This is just a kluge, and a rather bad one I think.  The real problem
> here is that AtAbort_Portals destroys the portal contents and doesn't
> do anything to record the fact.  It should probably be putting the
> portal into PORTAL_FAILED state, and what exec_execute_message ought
> to be doing is checking for that.

Yeah I thought about that too. in AtAbort_Portals:

--------------------------------------------------------------------------
/** Abort processing for portals.** At this point we reset "active" status and run the cleanup hook if* present, but we
can'trelease the portal's memory until the cleanup call.** The reason we need to reset active is so that we can replace
theunnamed* portal, else we'll fail to execute ROLLBACK when it arrives.*/
 
void
AtAbort_Portals(void)
{HASH_SEQ_STATUS status;PortalHashEnt *hentry;
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL){    Portal        portal = hentry->portal;
    if (portal->status == PORTAL_ACTIVE)        portal->status = PORTAL_FAILED;
--------------------------------------------------------------------------

Should I change the last if clause to?
    if (portal->status == PORTAL_ACTIVE || portal->status == PORTAL_READY)        portal->status = PORTAL_FAILED;

> zero out the now-dangling pointers in the Portal struct, too.

portal->cplan is already zero out by PortalReleaseCachedPlan. Problem
is, portal->stmts may belong to PortalContext or others (in this
particluar case). So if we want to zero out portal->stmts, we need to
memorize the memory context which it belongs to and we need add a new
struct member to portal. I'm afraid this is an overkill...

> It'd be nice to have a test case for this, hint hint ...

Still working on...
--
Tatsuo Ishii
SRA OSS, Inc. Japan


Re: exec_execute_message crash

From
Tatsuo Ishii
Date:
> > It'd be nice to have a test case for this, hint hint ...
> 
> Still working on...

Done. Inclded are C test program along with modified fe-exec.c.

The modification made to fe-exec.c is sending Sync after Parse, Bind
and Describe. Pgpool-II does this in order to get current transaction
status.
--
Tatsuo Ishii
SRA OSS, Inc. Japan
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "libpq-fe.h"
#include "libpq/libpq-fs.h"

int main(int argc, char **argv) {PGconn *conn;PGresult *res;int doTrace = 0;int doTransaction = 1;int i;
static char *commands[] = {    "SELECT 1 FROM pg_catalog.pg_type WHERE typname = 'smgr' AND
typinput='array_in'::regproc",   "SELECT typname FROM pg_catalog.pg_type WHERE oid = 210",    "SELECT NULL , n.nspname,
ct.relname,  a.attname,  a.attnum,  ci.relname FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct,
pg_catalog.pg_classci, pg_catalog.pg_attribute a, pg_catalog.pg_index i  WHERE ct.oid=i.indrelid AND
ci.oid=i.indexrelid AND a.attrelid=ci.oid AND i.indisprimary  AND ct.relname = 'mst_Ucompany_feature_setting'  AND
ct.relnamespace= n.oid  AND n.nspname = 'foo' ORDER BY 1, 2, 3",    "SELECT * FROM foo",};conn =
PQconnectdb("user=t-ishiidbname=test port=5433");if (PQstatus(conn) == CONNECTION_BAD) {    printf("Unable to connect
todb\n");    PQfinish(conn);    return 1;}
 
if(doTrace == 1)  PQtrace(conn, stdout);
if(doTransaction)  PQexec(conn,"BEGIN;");
for (i=0;i<sizeof(commands)/sizeof(char *);i++){    char *command = commands[i];    res = PQexecParams(conn, command,
0,NULL, NULL, NULL, NULL, 0);    switch(PQresultStatus(res)) {        case PGRES_COMMAND_OK:        case
PGRES_TUPLES_OK:           fprintf(stderr, "\"%s\" : succeeded\n", command);            break;        default:
 fprintf(stderr, "\"%s\" failed: %s\n", command, PQresultErrorMessage(res));            break;    }    PQclear(res);
 
}
if(doTransaction == 1) {    PQexec(conn,"COMMIT;");}PQfinish(conn);return 0;
}
/*-------------------------------------------------------------------------** fe-exec.c*      functions related to
sendinga query down to the backend** Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group* Portions
Copyright(c) 1994, Regents of the University of California*** IDENTIFICATION*      $PostgreSQL:
pgsql/src/interfaces/libpq/fe-exec.c,v1.203 2009/06/11 14:49:13 momjian Exp
$**-------------------------------------------------------------------------*/
#include "postgres_fe.h"

#include <ctype.h>
#include <fcntl.h>

#include "libpq-fe.h"
#include "libpq-int.h"

#include "mb/pg_wchar.h"

#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#endif

/* keep this in same order as ExecStatusType in libpq-fe.h */
char       *const pgresStatus[] =
{"PGRES_EMPTY_QUERY","PGRES_COMMAND_OK","PGRES_TUPLES_OK","PGRES_COPY_OUT","PGRES_COPY_IN","PGRES_BAD_RESPONSE","PGRES_NONFATAL_ERROR","PGRES_FATAL_ERROR"
};

/** static state needed by PQescapeString and PQescapeBytea; initialize to* values that result in backward-compatible
behavior*/
static int    static_client_encoding = PG_SQL_ASCII;
static bool static_std_strings = false;


static PGEvent *dupEvents(PGEvent *events, int count);
static bool PQsendQueryStart(PGconn *conn);
static int PQsendQueryGuts(PGconn *conn,            const char *command,            const char *stmtName,
intnParams,            const Oid *paramTypes,            const char *const * paramValues,            const int
*paramLengths,           const int *paramFormats,            int resultFormat);
 
static void parseInput(PGconn *conn);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,           const char *desc_target);
static int    check_field_number(const PGresult *res, int field_num);


/* ----------------* Space management for PGresult.** Formerly, libpq did a separate malloc() for each field of each
tuple*returned by a query.  This was remarkably expensive --- malloc/free* consumed a sizable part of the application's
runtime. And there is* no real need to keep track of the fields separately, since they will* all be freed together when
thePGresult is released.  So now, we grab* large blocks of storage from malloc and allocate space for query data*
withinthese blocks, using a trivially simple allocator.  This reduces* the number of malloc/free calls dramatically,
andit also avoids* fragmentation of the malloc storage arena.* The PGresult structure itself is still malloc'd
separately. We could* combine it with the first allocation block, but that would waste space* for the common case that
noextra storage is actually needed (that is,* the SQL command did not return tuples).** We also malloc the top-level
arrayof tuple pointers separately, because* we need to be able to enlarge it via realloc, and our trivial space*
allocatordoesn't handle that effectively.  (Too bad the FE/BE protocol* doesn't tell us up front how many tuples will
bereturned.)* All other subsidiary storage for a PGresult is kept in PGresult_data blocks* of size
PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block* is just a link to the next one, if any.    Free-space
managementinfo is* kept in the owning PGresult.* A query returning a small amount of data will thus require three
malloc*calls: one for the PGresult, one for the tuples pointer array, and one* PGresult_data block.** Only the most
recentlyallocated PGresult_data block is a candidate to* have more stuff added to it --- any extra space left over in
olderblocks* is wasted.  We could be smarter and search the whole chain, but the point* here is to be simple and fast.
Typicalapplications do not keep a PGresult* around very long anyway, so some wasted space within one is not a
problem.**Tuning constants for the space allocator are:* PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block,
inbytes* PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data* PGRESULT_SEP_ALLOC_THRESHOLD: objects
biggerthan this are given separate*     blocks, instead of being crammed into a regular allocation block.* Requirements
forcorrect function are:* PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements*        of all
machinedata types.    (Currently this is set from configure*        tests, so it should be OK automatically.)*
PGRESULT_SEP_ALLOC_THRESHOLD+ PGRESULT_BLOCK_OVERHEAD <=*            PGRESULT_DATA_BLOCKSIZE*        pqResultAlloc
assumesan object smaller than the threshold will fit*        in a new block.* The amount of space wasted at the end of
ablock could be as much as* PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large.*
----------------*/

#define PGRESULT_DATA_BLOCKSIZE        2048
#define PGRESULT_ALIGN_BOUNDARY        MAXIMUM_ALIGNOF        /* from configure */
#define PGRESULT_BLOCK_OVERHEAD        Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
#define PGRESULT_SEP_ALLOC_THRESHOLD    (PGRESULT_DATA_BLOCKSIZE / 2)


/** PQmakeEmptyPGresult*     returns a newly allocated, initialized PGresult with given status.*     If conn is not
NULLand status indicates an error, the conn's*     errorMessage is copied.  Also, any PGEvents are copied from the
conn.*/
PGresult *
PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
{PGresult   *result;
result = (PGresult *) malloc(sizeof(PGresult));if (!result)    return NULL;
result->ntups = 0;result->numAttributes = 0;result->attDescs = NULL;result->tuples = NULL;result->tupArrSize =
0;result->numParameters= 0;result->paramDescs = NULL;result->resultStatus = status;result->cmdStatus[0] =
'\0';result->binary= 0;result->events = NULL;result->nEvents = 0;result->errMsg = NULL;result->errFields =
NULL;result->null_field[0]= '\0';result->curBlock = NULL;result->curOffset = 0;result->spaceLeft = 0;
 
if (conn){    /* copy connection data we might need for operations on PGresult */    result->noticeHooks =
conn->noticeHooks;   result->client_encoding = conn->client_encoding;
 
    /* consider copying conn's errorMessage */    switch (status)    {        case PGRES_EMPTY_QUERY:        case
PGRES_COMMAND_OK:       case PGRES_TUPLES_OK:        case PGRES_COPY_OUT:        case PGRES_COPY_IN:            /*
non-errorcases */            break;        default:            pqSetResultError(result, conn->errorMessage.data);
    break;    }
 
    /* copy events last; result must be valid if we need to PQclear */    if (conn->nEvents > 0)    {
result->events= dupEvents(conn->events, conn->nEvents);        if (!result->events)        {
PQclear(result);           return NULL;        }        result->nEvents = conn->nEvents;    }}else{    /* defaults...
*/   result->noticeHooks.noticeRec = NULL;    result->noticeHooks.noticeRecArg = NULL;
result->noticeHooks.noticeProc= NULL;    result->noticeHooks.noticeProcArg = NULL;    result->client_encoding =
PG_SQL_ASCII;}
return result;
}

/** PQsetResultAttrs** Set the attributes for a given result.  This function fails if there are* already attributes
containedin the provided result.  The call is* ignored if numAttributes is is zero or attDescs is NULL.  If the*
functionfails, it returns zero.  If the function succeeds, it* returns a non-zero value.*/
 
int
PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
{int            i;
/* If attrs already exist, they cannot be overwritten. */if (!res || res->numAttributes > 0)    return FALSE;
/* ignore no-op request */if (numAttributes <= 0 || !attDescs)    return TRUE;
res->attDescs = (PGresAttDesc *)    PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
if (!res->attDescs)    return FALSE;
res->numAttributes = numAttributes;memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
/* deep-copy the attribute names, and determine format */res->binary = 1;for (i = 0; i < res->numAttributes; i++){
if(res->attDescs[i].name)        res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);    else
res->attDescs[i].name= res->null_field;
 
    if (!res->attDescs[i].name)        return FALSE;
    if (res->attDescs[i].format == 0)        res->binary = 0;}
return TRUE;
}

/** PQcopyResult** Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.* The 'flags' argument
controlswhich portions of the result will or will* NOT be copied.  The created result is always put into the*
PGRES_TUPLES_OKstatus.    The source result error message is not copied,* although cmdStatus is.** To set custom
attributes,use PQsetResultAttrs.    That function requires* that there are no attrs contained in the result, so to use
that*function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES* options with this function.** Options:*
PG_COPYRES_ATTRS- Copy the source result's attributes**     PG_COPYRES_TUPLES - Copy the source result's tuples.  This
implies*    copying the attrs, seeeing how the attrs are needed by the tuples.**     PG_COPYRES_EVENTS - Copy the
sourceresult's events.**     PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.*/
 
PGresult *
PQcopyResult(const PGresult *src, int flags)
{PGresult   *dest;int            i;
if (!src)    return NULL;
dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);if (!dest)    return NULL;
/* Always copy these over.    Is cmdStatus really useful here? */dest->client_encoding =
src->client_encoding;strcpy(dest->cmdStatus,src->cmdStatus);
 
/* Wants attrs? */if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES)){    if (!PQsetResultAttrs(dest,
src->numAttributes,src->attDescs))    {        PQclear(dest);        return NULL;    }}
 
/* Wants to copy tuples? */if (flags & PG_COPYRES_TUPLES){    int            tup,                field;
    for (tup = 0; tup < src->ntups; tup++)    {        for (field = 0; field < src->numAttributes; field++)        {
       if (!PQsetvalue(dest, tup, field,                            src->tuples[tup][field].value,
     src->tuples[tup][field].len))            {                PQclear(dest);                return NULL;            }
     }    }}
 
/* Wants to copy notice hooks? */if (flags & PG_COPYRES_NOTICEHOOKS)    dest->noticeHooks = src->noticeHooks;
/* Wants to copy PGEvents? */if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0){    dest->events =
dupEvents(src->events,src->nEvents);    if (!dest->events)    {        PQclear(dest);        return NULL;    }
dest->nEvents= src->nEvents;}
 
/* Okay, trigger PGEVT_RESULTCOPY event */for (i = 0; i < dest->nEvents; i++){    if (src->events[i].resultInitialized)
  {        PGEventResultCopy evt;
 
        evt.src = src;        evt.dest = dest;        if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
                dest->events[i].passThrough))        {            PQclear(dest);            return NULL;        }
dest->events[i].resultInitialized = TRUE;    }}
 
return dest;
}

/** Copy an array of PGEvents (with no extra space for more).* Does not duplicate the event instance data, sets this to
NULL.*Also, the resultInitialized flags are all cleared.*/
 
static PGEvent *
dupEvents(PGEvent *events, int count)
{PGEvent    *newEvents;int            i;
if (!events || count <= 0)    return NULL;
newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));if (!newEvents)    return NULL;
for (i = 0; i < count; i++){    newEvents[i].proc = events[i].proc;    newEvents[i].passThrough =
events[i].passThrough;   newEvents[i].data = NULL;    newEvents[i].resultInitialized = FALSE;    newEvents[i].name =
strdup(events[i].name);   if (!newEvents[i].name)    {        while (--i >= 0)            free(newEvents[i].name);
 free(newEvents);        return NULL;    }}
 
return newEvents;
}


/** Sets the value for a tuple field.  The tup_num must be less than or* equal to PQntuples(res).  If it is equal, a
newtuple is created and* added to the result.* Returns a non-zero value for success and zero for failure.*/
 
int
PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
{PGresAttValue *attval;
if (!check_field_number(res, field_num))    return FALSE;
/* Invalid tup_num, must be <= ntups */if (tup_num < 0 || tup_num > res->ntups)    return FALSE;
/* need to grow the tuple table? */if (res->ntups >= res->tupArrSize){    int            n = res->tupArrSize ?
res->tupArrSize* 2 : 128;    PGresAttValue **tups;
 
    if (res->tuples)        tups = (PGresAttValue **) realloc(res->tuples, n * sizeof(PGresAttValue *));    else
tups= (PGresAttValue **) malloc(n * sizeof(PGresAttValue *));
 
    if (!tups)        return FALSE;
    memset(tups + res->tupArrSize, 0,           (n - res->tupArrSize) * sizeof(PGresAttValue *));    res->tuples =
tups;   res->tupArrSize = n;}
 
/* need to allocate a new tuple? */if (tup_num == res->ntups && !res->tuples[tup_num]){    PGresAttValue *tup;    int
        i;
 
    tup = (PGresAttValue *)        pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
TRUE);
    if (!tup)        return FALSE;
    /* initialize each column to NULL */    for (i = 0; i < res->numAttributes; i++)    {        tup[i].len = NULL_LEN;
      tup[i].value = res->null_field;    }
 
    res->tuples[tup_num] = tup;    res->ntups++;}
attval = &res->tuples[tup_num][field_num];
/* treat either NULL_LEN or NULL value pointer as a NULL field */if (len == NULL_LEN || value == NULL){    attval->len
=NULL_LEN;    attval->value = res->null_field;}else if (len <= 0){    attval->len = 0;    attval->value =
res->null_field;}else{   attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);    if (!attval->value)
returnFALSE;    attval->len = len;    memcpy(attval->value, value, len);    attval->value[len] = '\0';}
 
return TRUE;
}

/** pqResultAlloc - exported routine to allocate local storage in a PGresult.** We force all such allocations to be
maxaligned,since we don't know* whether the value might be binary.*/
 
void *
PQresultAlloc(PGresult *res, size_t nBytes)
{return pqResultAlloc(res, nBytes, TRUE);
}

/** pqResultAlloc -*        Allocate subsidiary storage for a PGresult.** nBytes is the amount of space needed for the
object.*If isBinary is true, we assume that we need to align the object on* a machine allocation boundary.* If isBinary
isfalse, we assume the object is a char string and can* be allocated on any byte boundary.*/
 
void *
pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
{char       *space;PGresult_data *block;
if (!res)    return NULL;
if (nBytes <= 0)    return res->null_field;
/* * If alignment is needed, round up the current position to an alignment * boundary. */if (isBinary){    int
 offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;
 
    if (offset)    {        res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;        res->spaceLeft -=
PGRESULT_ALIGN_BOUNDARY- offset;    }}
 
/* If there's enough space in the current block, no problem. */if (nBytes <= (size_t) res->spaceLeft){    space =
res->curBlock->space+ res->curOffset;    res->curOffset += nBytes;    res->spaceLeft -= nBytes;    return space;}
 
/* * If the requested object is very large, give it its own block; this * avoids wasting what might be most of the
currentblock to start a new * block.  (We'd have to special-case requests bigger than the block size * anyway.)  The
objectis always given binary alignment in this case. */if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD){    block =
(PGresult_data*) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD);    if (!block)        return NULL;    space = block->space +
PGRESULT_BLOCK_OVERHEAD;   if (res->curBlock)    {        /*         * Tuck special block below the active block, so
thatwe don't         * have to waste the free space in the active block.         */        block->next =
res->curBlock->next;       res->curBlock->next = block;    }    else    {        /* Must set up the new block as the
firstactive block. */        block->next = NULL;        res->curBlock = block;        res->spaceLeft = 0; /* be sure
it'smarked full */    }    return space;}
 
/* Otherwise, start a new block. */block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);if (!block)    return
NULL;block->next= res->curBlock;res->curBlock = block;if (isBinary){    /* object needs full alignment */
res->curOffset= PGRESULT_BLOCK_OVERHEAD;    res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;}else{
/* we can cram it right after the overhead pointer */    res->curOffset = sizeof(PGresult_data);    res->spaceLeft =
PGRESULT_DATA_BLOCKSIZE- sizeof(PGresult_data);}
 
space = block->space + res->curOffset;res->curOffset += nBytes;res->spaceLeft -= nBytes;return space;
}

/** pqResultStrdup -*        Like strdup, but the space is subsidiary PGresult space.*/
char *
pqResultStrdup(PGresult *res, const char *str)
{char       *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE);
if (space)    strcpy(space, str);return space;
}

/** pqSetResultError -*        assign a new error message to a PGresult*/
void
pqSetResultError(PGresult *res, const char *msg)
{if (!res)    return;if (msg && *msg)    res->errMsg = pqResultStrdup(res, msg);else    res->errMsg = NULL;
}

/** pqCatenateResultError -*        concatenate a new error message to the one already in a PGresult*/
void
pqCatenateResultError(PGresult *res, const char *msg)
{PQExpBufferData errorBuf;
if (!res || !msg)    return;initPQExpBuffer(&errorBuf);if (res->errMsg)    appendPQExpBufferStr(&errorBuf,
res->errMsg);appendPQExpBufferStr(&errorBuf,msg);pqSetResultError(res, errorBuf.data);termPQExpBuffer(&errorBuf);
 
}

/** PQclear -*      free's the memory associated with a PGresult*/
void
PQclear(PGresult *res)
{PGresult_data *block;int            i;
if (!res)    return;
for (i = 0; i < res->nEvents; i++){    /* only send DESTROY to successfully-initialized event procs */    if
(res->events[i].resultInitialized)   {        PGEventResultDestroy evt;
 
        evt.result = res;        (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
res->events[i].passThrough);    }    free(res->events[i].name);}
 
if (res->events)    free(res->events);
/* Free all the subsidiary blocks */while ((block = res->curBlock) != NULL){    res->curBlock = block->next;
free(block);}
/* Free the top-level tuple pointer array */if (res->tuples)    free(res->tuples);
/* zero out the pointer fields to catch programming errors */res->attDescs = NULL;res->tuples = NULL;res->paramDescs =
NULL;res->errFields= NULL;res->events = NULL;res->nEvents = 0;/* res->curBlock was zeroed out earlier */
 
/* Free the PGresult structure itself */free(res);
}

/** Handy subroutine to deallocate any partially constructed async result.*/

void
pqClearAsyncResult(PGconn *conn)
{if (conn->result)    PQclear(conn->result);conn->result = NULL;conn->curTuple = NULL;
}

/** This subroutine deletes any existing async result, sets conn->result* to a PGresult with status PGRES_FATAL_ERROR,
andstores the current* contents of conn->errorMessage into that result.  It differs from a* plain call on
PQmakeEmptyPGresult()in that if there is already an* async result with status PGRES_FATAL_ERROR, the current error
message*is APPENDED to the old error message instead of replacing it.  This* behavior lets us report multiple error
conditionsproperly, if necessary.* (An example where this is needed is when the backend sends an 'E' message* and
immediatelycloses the connection --- we want to report both the* backend error and the connection closure error.)*/
 
void
pqSaveErrorResult(PGconn *conn)
{/* * If no old async result, just let PQmakeEmptyPGresult make one. Likewise * if old result is not an error message.
*/if(conn->result == NULL ||    conn->result->resultStatus != PGRES_FATAL_ERROR ||    conn->result->errMsg == NULL){
pqClearAsyncResult(conn);   conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);}else{    /* Else, concatenate
errormessage to existing async result. */    pqCatenateResultError(conn->result, conn->errorMessage.data);}
 
}

/** This subroutine prepares an async result object for return to the caller.* If there is not already an async result
object,build an error object* using whatever is in conn->errorMessage.  In any case, clear the async* result storage
andmake sure PQerrorMessage will agree with the result's* error string.*/
 
PGresult *
pqPrepareAsyncResult(PGconn *conn)
{PGresult   *res;
/* * conn->result is the PGresult to return.    If it is NULL (which probably * shouldn't happen) we assume there is an
appropriateerror message in * conn->errorMessage. */res = conn->result;conn->result = NULL;        /* handing over
ownershipto caller */conn->curTuple = NULL;        /* just in case */if (!res)    res = PQmakeEmptyPGresult(conn,
PGRES_FATAL_ERROR);else{   /*     * Make sure PQerrorMessage agrees with result; it could be different     * if we have
concatenatedmessages.     */    resetPQExpBuffer(&conn->errorMessage);    appendPQExpBufferStr(&conn->errorMessage,
                   PQresultErrorMessage(res));}return res;
 
}

/** pqInternalNotice - produce an internally-generated notice message** A format string and optional arguments can be
passed. Note that we do* libpq_gettext() here, so callers need not.** The supplied text is taken as primary message
(ie.,it should not include* a trailing newline, and should not be more than one line).*/
 
void
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
{char        msgBuf[1024];va_list        args;PGresult   *res;
if (hooks->noticeRec == NULL)    return;                    /* nobody home to receive notice? */
/* Format the message */va_start(args, fmt);vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt),
args);va_end(args);msgBuf[sizeof(msgBuf)- 1] = '\0';    /* make real sure it's terminated */
 
/* Make a PGresult to pass to the notice receiver */res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);if (!res)
return;res->noticeHooks= *hooks;
 
/* * Set up fields of notice. */pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);pqSaveMessageField(res,
PG_DIAG_SEVERITY,libpq_gettext("NOTICE"));/* XXX should provide a SQLSTATE too? */
 
/* * Result text is always just the primary message + newline. If we can't * allocate it, don't bother invoking the
receiver.*/res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);if (res->errMsg){
sprintf(res->errMsg,"%s\n", msgBuf);
 
    /*     * Pass to receiver, then free it.     */    (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg,
res);}PQclear(res);
}

/** pqAddTuple*      add a row pointer to the PGresult structure, growing it if necessary*      Returns TRUE if OK,
FALSEif not enough memory to add the row*/
 
int
pqAddTuple(PGresult *res, PGresAttValue *tup)
{if (res->ntups >= res->tupArrSize){    /*     * Try to grow the array.     *     * We can use realloc because shallow
copyingof the structure is     * okay. Note that the first time through, res->tuples is NULL. While     * ANSI says
thatrealloc() should act like malloc() in that case,     * some old C libraries (like SunOS 4.1.x) coredump instead. On
   * failure realloc is supposed to return NULL without damaging the     * existing allocation. Note that the positions
beyondres->ntups are     * garbage, not necessarily NULL.     */    int            newSize = (res->tupArrSize > 0) ?
res->tupArrSize* 2 : 128;    PGresAttValue **newTuples;
 
    if (res->tuples == NULL)        newTuples = (PGresAttValue **)            malloc(newSize * sizeof(PGresAttValue
*));   else        newTuples = (PGresAttValue **)            realloc(res->tuples, newSize * sizeof(PGresAttValue *));
if (!newTuples)        return FALSE;        /* malloc or realloc failed */    res->tupArrSize = newSize;    res->tuples
=newTuples;}res->tuples[res->ntups] = tup;res->ntups++;return TRUE;
 
}

/** pqSaveMessageField - save one field of an error or notice message*/
void
pqSaveMessageField(PGresult *res, char code, const char *value)
{PGMessageField *pfield;
pfield = (PGMessageField *)    pqResultAlloc(res,                  sizeof(PGMessageField) + strlen(value),
   TRUE);if (!pfield)    return;                    /* out of memory? */pfield->code = code;strcpy(pfield->contents,
value);pfield->next= res->errFields;res->errFields = pfield;
 
}

/** pqSaveParameterStatus - remember parameter status sent by backend*/
void
pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
{pgParameterStatus *pstatus;pgParameterStatus *prev;
if (conn->Pfdebug)    fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",            name, value);
/* * Forget any old information about the parameter */for (pstatus = conn->pstatus, prev = NULL;     pstatus != NULL;
 prev = pstatus, pstatus = pstatus->next){    if (strcmp(pstatus->name, name) == 0)    {        if (prev)
prev->next= pstatus->next;        else            conn->pstatus = pstatus->next;        free(pstatus);        /* frees
nameand value strings too */        break;    }}
 
/* * Store new info as a single malloc block */pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
                               strlen(name) +strlen(value) + 2);if (pstatus){    char       *ptr;
 
    ptr = ((char *) pstatus) + sizeof(pgParameterStatus);    pstatus->name = ptr;    strcpy(ptr, name);    ptr +=
strlen(name)+ 1;    pstatus->value = ptr;    strcpy(ptr, value);    pstatus->next = conn->pstatus;    conn->pstatus =
pstatus;}
/* * Special hacks: remember client_encoding and * standard_conforming_strings, and convert server version to a numeric
*form.  We keep the first two of these in static variables as well, so * that PQescapeString and PQescapeBytea can
behavesomewhat sanely (at * least in single-connection-using programs). */if (strcmp(name, "client_encoding") == 0){
conn->client_encoding= pg_char_to_encoding(value);    /* if we don't recognize the encoding name, fall back to
SQL_ASCII*/    if (conn->client_encoding < 0)        conn->client_encoding = PG_SQL_ASCII;    static_client_encoding =
conn->client_encoding;}elseif (strcmp(name, "standard_conforming_strings") == 0){    conn->std_strings = (strcmp(value,
"on")== 0);    static_std_strings = conn->std_strings;}else if (strcmp(name, "server_version") == 0){    int
cnt;   int            vmaj,                vmin,                vrev;
 
    cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
    if (cnt < 2)        conn->sversion = 0; /* unknown */    else    {        if (cnt == 2)            vrev = 0;
conn->sversion= (100 * vmaj + vmin) * 100 + vrev;    }}
 
}


/** PQsendQuery*     Submit a query, but don't wait for it to finish** Returns: 1 if successfully submitted*
0if error (conn->errorMessage is set)*/
 
int
PQsendQuery(PGconn *conn, const char *query)
{if (!PQsendQueryStart(conn))    return 0;
if (!query){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
/* construct the outgoing Query message */if (pqPutMsgStart('Q', false, conn) < 0 ||    pqPuts(query, conn) < 0 ||
pqPutMsgEnd(conn)< 0){    pqHandleSendFailure(conn);    return 0;}
 
/* remember we are using simple query protocol */conn->queryclass = PGQUERY_SIMPLE;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);conn->last_query = strdup(query);
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0){    pqHandleSendFailure(conn);    return 0;}
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;
}

/** PQsendQueryParams*        Like PQsendQuery, but use protocol 3.0 so we can pass parameters*/
int
PQsendQueryParams(PGconn *conn,              const char *command,              int nParams,              const Oid
*paramTypes,             const char *const * paramValues,              const int *paramLengths,              const int
*paramFormats,             int resultFormat)
 
{if (!PQsendQueryStart(conn))    return 0;
if (!command){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
return PQsendQueryGuts(conn,                       command,                       "",    /* use unnamed statement */
                  nParams,                       paramTypes,                       paramValues,
paramLengths,                      paramFormats,                       resultFormat);
 
}

/** PQsendPrepare*     Submit a Parse message, but don't wait for it to finish** Returns: 1 if successfully submitted*
         0 if error (conn->errorMessage is set)*/
 
int
PQsendPrepare(PGconn *conn,          const char *stmtName, const char *query,          int nParams, const Oid
*paramTypes)
{if (!PQsendQueryStart(conn))    return 0;
if (!stmtName){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("statement name is a null
pointer\n"));   return 0;}
 
if (!query){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("command string is a null
pointer\n"));   return 0;}
 
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* construct the Parse message */if (pqPutMsgStart('P', false, conn) < 0 ||    pqPuts(stmtName, conn) < 0 ||
pqPuts(query,conn) < 0)    goto sendFailed;
 
if (nParams > 0 && paramTypes){    int            i;
    if (pqPutInt(nParams, 2, conn) < 0)        goto sendFailed;    for (i = 0; i < nParams; i++)    {        if
(pqPutInt(paramTypes[i],4, conn) < 0)            goto sendFailed;    }}else{    if (pqPutInt(0, 2, conn) < 0)
gotosendFailed;}if (pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are doing just a Parse */conn->queryclass = PGQUERY_PREPARE;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);conn->last_query = strdup(query);
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** PQsendQueryPrepared*        Like PQsendQuery, but execute a previously prepared statement,*        using protocol
3.0so we can pass parameters*/
 
int
PQsendQueryPrepared(PGconn *conn,                const char *stmtName,                int nParams,                const
char*const * paramValues,                const int *paramLengths,                const int *paramFormats,
int resultFormat)
 
{if (!PQsendQueryStart(conn))    return 0;
if (!stmtName){    printfPQExpBuffer(&conn->errorMessage,                    libpq_gettext("statement name is a null
pointer\n"));   return 0;}
 
return PQsendQueryGuts(conn,                       NULL,    /* no command to parse */                       stmtName,
                   nParams,                       NULL,    /* no param types */                       paramValues,
                paramLengths,                       paramFormats,                       resultFormat);
 
}

/** Common startup code for PQsendQuery and sibling routines*/
static bool
PQsendQueryStart(PGconn *conn)
{if (!conn)    return false;
/* clear the error string */resetPQExpBuffer(&conn->errorMessage);
/* Don't try to send if we know there's no live connection. */if (conn->status != CONNECTION_OK){
printfPQExpBuffer(&conn->errorMessage,                     libpq_gettext("no connection to the server\n"));    return
false;}/*Can't send while already busy, either. */if (conn->asyncStatus != PGASYNC_IDLE){
printfPQExpBuffer(&conn->errorMessage,             libpq_gettext("another command is already in progress\n"));
returnfalse;}
 
/* initialize async result-accumulation state */conn->result = NULL;conn->curTuple = NULL;
/* ready to send command message */return true;
}

/** PQsendQueryGuts*        Common code for protocol-3.0 query sending*        PQsendQueryStart should be done
already**command may be NULL to indicate we use an already-prepared statement*/
 
static int
PQsendQueryGuts(PGconn *conn,            const char *command,            const char *stmtName,            int nParams,
         const Oid *paramTypes,            const char *const * paramValues,            const int *paramLengths,
  const int *paramFormats,            int resultFormat)
 
{int            i;
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync, * using specified statement name and the
unnamedportal. */
 

#define ADD_SYNC
if (command){    /* construct the Parse message */    if (pqPutMsgStart('P', false, conn) < 0 ||
pqPuts(stmtName,conn) < 0 ||        pqPuts(command, conn) < 0)        goto sendFailed;    if (nParams > 0 &&
paramTypes)   {        if (pqPutInt(nParams, 2, conn) < 0)            goto sendFailed;        for (i = 0; i < nParams;
i++)       {            if (pqPutInt(paramTypes[i], 4, conn) < 0)                goto sendFailed;        }    }    else
  {        if (pqPutInt(0, 2, conn) < 0)            goto sendFailed;    }    if (pqPutMsgEnd(conn) < 0)        goto
sendFailed;}

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* Construct the Bind message */if (pqPutMsgStart('B', false, conn) < 0 ||    pqPuts("", conn) < 0 ||
pqPuts(stmtName,conn) < 0)    goto sendFailed;
 
/* Send parameter formats */if (nParams > 0 && paramFormats){    if (pqPutInt(nParams, 2, conn) < 0)        goto
sendFailed;   for (i = 0; i < nParams; i++)    {        if (pqPutInt(paramFormats[i], 2, conn) < 0)            goto
sendFailed;   }}else{    if (pqPutInt(0, 2, conn) < 0)        goto sendFailed;}
 
if (pqPutInt(nParams, 2, conn) < 0)    goto sendFailed;
/* Send parameters */for (i = 0; i < nParams; i++){    if (paramValues && paramValues[i])    {        int
nbytes;
        if (paramFormats && paramFormats[i] != 0)        {            /* binary parameter */            if
(paramLengths)               nbytes = paramLengths[i];            else            {
printfPQExpBuffer(&conn->errorMessage,                                 libpq_gettext("length must be given for binary
parameter\n"));               goto sendFailed;            }        }        else        {            /* text parameter,
donot use paramLengths */            nbytes = strlen(paramValues[i]);        }        if (pqPutInt(nbytes, 4, conn) < 0
||           pqPutnchar(paramValues[i], nbytes, conn) < 0)            goto sendFailed;    }    else    {        /* take
theparam as NULL */        if (pqPutInt(-1, 4, conn) < 0)            goto sendFailed;    }}if (pqPutInt(1, 2, conn) < 0
||   pqPutInt(resultFormat, 2, conn))    goto sendFailed;if (pqPutMsgEnd(conn) < 0)    goto sendFailed;
 

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* construct the Describe Portal message */if (pqPutMsgStart('D', false, conn) < 0 ||    pqPutc('P', conn) < 0 ||
pqPuts("",conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 

#ifdef ADD_SYNC/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)
gotosendFailed;
 
#endif
/* construct the Execute message */if (pqPutMsgStart('E', false, conn) < 0 ||    pqPuts("", conn) < 0 ||    pqPutInt(0,
4,conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are using extended query protocol */conn->queryclass = PGQUERY_EXTENDED;
/* and remember the query text too, if possible *//* if insufficient memory, last_query just winds up NULL */if
(conn->last_query)   free(conn->last_query);if (command)    conn->last_query = strdup(command);else    conn->last_query
=NULL;
 
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** pqHandleSendFailure: try to clean up after failure to send command.** Primarily, what we want to accomplish here is
toprocess an async* NOTICE message that the backend might have sent just before it died.** NOTE: this routine should
onlybe called in PGASYNC_IDLE state.*/
 
void
pqHandleSendFailure(PGconn *conn)
{/* * Accept any available input data, ignoring errors.  Note that if * pqReadData decides the backend has closed the
channel,it will close * our side of the socket --- that's just what we want here. */while (pqReadData(conn) > 0)     /*
loopuntil no more data readable */ ;
 
/* * Parse any available input messages.    Since we are in PGASYNC_IDLE * state, only NOTICE and NOTIFY messages will
beeaten. */parseInput(conn);
 
}

/** Consume any available input from the backend* 0 return: some kind of trouble* 1 return: no problem*/
int
PQconsumeInput(PGconn *conn)
{if (!conn)    return 0;
/* * for non-blocking connections try to flush the send-queue, otherwise we * may never get a response for something
thatmay not have already been * sent because it's in our write buffer! */if (pqIsnonblocking(conn)){    if
(pqFlush(conn)< 0)        return 0;}
 
/* * Load more data, if available. We do this no matter what state we are * in, since we are probably getting called
becausethe application wants * to get rid of a read-select condition. Note that we will NOT block * waiting for more
input.*/if (pqReadData(conn) < 0)    return 0;
 
/* Parsing of the data waits till later. */return 1;
}


/** parseInput: if appropriate, parse input data from backend* until input is exhausted or a stopping state is
reached.*Note that this function will NOT attempt to read more data from the backend.*/
 
static void
parseInput(PGconn *conn)
{if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    pqParseInput3(conn);else    pqParseInput2(conn);
}

/** PQisBusy*     Return TRUE if PQgetResult would block waiting for input.*/

int
PQisBusy(PGconn *conn)
{if (!conn)    return FALSE;
/* Parse any available data, if our state permits. */parseInput(conn);
/* PQgetResult will return immediately in all states except BUSY. */return conn->asyncStatus == PGASYNC_BUSY;
}


/** PQgetResult*      Get the next PGresult produced by a query.  Returns NULL if no*      query work remains or an
errorhas occurred (e.g. out of*      memory).*/
 

PGresult *
PQgetResult(PGconn *conn)
{PGresult   *res;
if (!conn)    return NULL;
/* Parse any available data, if our state permits. */parseInput(conn);
/* If not ready to return something, block until we are. */while (conn->asyncStatus == PGASYNC_BUSY){    int
flushResult;
    /*     * If data remains unsent, send it.  Else we might be waiting for the     * result of a command the backend
hasn'teven got yet.     */    while ((flushResult = pqFlush(conn)) > 0)    {        if (pqWait(FALSE, TRUE, conn))
 {            flushResult = -1;            break;        }    }
 
    /* Wait for some more data, and load it. */    if (flushResult ||        pqWait(TRUE, FALSE, conn) ||
pqReadData(conn)< 0)    {        /*         * conn->errorMessage has been set by pqWait or pqReadData. We         *
wantto append it to any already-received error message.         */        pqSaveErrorResult(conn);
conn->asyncStatus= PGASYNC_IDLE;        return pqPrepareAsyncResult(conn);    }
 
    /* Parse it. */    parseInput(conn);}
/* Return the appropriate thing. */switch (conn->asyncStatus){    case PGASYNC_IDLE:        res = NULL;            /*
queryis complete */        break;    case PGASYNC_READY:        res = pqPrepareAsyncResult(conn);        /* Set the
stateback to BUSY, allowing parsing to proceed. */        conn->asyncStatus = PGASYNC_BUSY;        break;    case
PGASYNC_COPY_IN:       if (conn->result && conn->result->resultStatus == PGRES_COPY_IN)            res =
pqPrepareAsyncResult(conn);       else            res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN);        break;    case
PGASYNC_COPY_OUT:       if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT)            res =
pqPrepareAsyncResult(conn);       else            res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);        break;
default:       printfPQExpBuffer(&conn->errorMessage,                          libpq_gettext("unexpected asyncStatus:
%d\n"),                         (int) conn->asyncStatus);        res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
   break;}
 
if (res){    int            i;
    for (i = 0; i < res->nEvents; i++)    {        PGEventResultCreate evt;
        evt.conn = conn;        evt.result = res;        if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
                   res->events[i].passThrough))        {            printfPQExpBuffer(&conn->errorMessage,
               libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
 res->events[i].name);            pqSetResultError(res, conn->errorMessage.data);            res->resultStatus =
PGRES_FATAL_ERROR;           break;        }        res->events[i].resultInitialized = TRUE;    }}
 
return res;
}


/** PQexec*      send a query to the backend and package up the result in a PGresult** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).* The user is responsible for freeing the PGresult via PQclear()* when done
withit.*/
 
PGresult *
PQexec(PGconn *conn, const char *query)
{if (!PQexecStart(conn))    return NULL;if (!PQsendQuery(conn, query))    return NULL;return PQexecFinish(conn);
}

/** PQexecParams*        Like PQexec, but use protocol 3.0 so we can pass parameters*/
PGresult *
PQexecParams(PGconn *conn,         const char *command,         int nParams,         const Oid *paramTypes,
constchar *const * paramValues,         const int *paramLengths,         const int *paramFormats,         int
resultFormat)
{if (!PQexecStart(conn))    return NULL;if (!PQsendQueryParams(conn, command,                       nParams,
paramTypes,paramValues, paramLengths,                       paramFormats, resultFormat))    return NULL;return
PQexecFinish(conn);
}

/** PQprepare*      Creates a prepared statement by issuing a v3.0 parse message.** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).* The user is responsible for freeing the PGresult via PQclear()* when done
withit.*/
 
PGresult *
PQprepare(PGconn *conn,      const char *stmtName, const char *query,      int nParams, const Oid *paramTypes)
{if (!PQexecStart(conn))    return NULL;if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))    return
NULL;returnPQexecFinish(conn);
 
}

/** PQexecPrepared*        Like PQexec, but execute a previously prepared statement,*        using protocol 3.0 so we
canpass parameters*/
 
PGresult *
PQexecPrepared(PGconn *conn,           const char *stmtName,           int nParams,           const char *const *
paramValues,          const int *paramLengths,           const int *paramFormats,           int resultFormat)
 
{if (!PQexecStart(conn))    return NULL;if (!PQsendQueryPrepared(conn, stmtName,                         nParams,
paramValues,paramLengths,                         paramFormats, resultFormat))    return NULL;return
PQexecFinish(conn);
}

/** Common code for PQexec and sibling routines: prepare to send command*/
static bool
PQexecStart(PGconn *conn)
{PGresult   *result;
if (!conn)    return false;
/* * Silently discard any prior query result that application didn't eat. * This is probably poor design, but it's here
forbackward compatibility. */while ((result = PQgetResult(conn)) != NULL){    ExecStatusType resultStatus =
result->resultStatus;
    PQclear(result);        /* only need its status */    if (resultStatus == PGRES_COPY_IN)    {        if
(PG_PROTOCOL_MAJOR(conn->pversion)>= 3)        {            /* In protocol 3, we can get out of a COPY IN state */
     if (PQputCopyEnd(conn,                     libpq_gettext("COPY terminated by new PQexec")) < 0)
returnfalse;            /* keep waiting to swallow the copy's failure message */        }        else        {
 /* In older protocols we have to punt */            printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPYIN state must be terminated first\n"));            return false;        }    }    else if
(resultStatus== PGRES_COPY_OUT)    {        if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)        {            /*
  * In protocol 3, we can get out of a COPY OUT state: we just             * switch back to BUSY and allow the
remainingCOPY data to be             * dropped on the floor.             */            conn->asyncStatus =
PGASYNC_BUSY;           /* keep waiting to swallow the copy's completion message */        }        else        {
    /* In older protocols we have to punt */            printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("COPYOUT state must be terminated first\n"));            return false;        }    }    /* check for loss
ofconnection, too */    if (conn->status == CONNECTION_BAD)        return false;}
 
/* OK to send a command */return true;
}

/** Common code for PQexec and sibling routines: wait for command result*/
static PGresult *
PQexecFinish(PGconn *conn)
{PGresult   *result;PGresult   *lastResult;
/* * For backwards compatibility, return the last result if there are more * than one --- but merge error messages if
weget more than one error * result. * * We have to stop if we see copy in/out, however. We will resume parsing * after
applicationperforms the data transfer. * * Also stop if the connection is lost (else we'll loop infinitely).
*/lastResult= NULL;while ((result = PQgetResult(conn)) != NULL){    if (lastResult)    {        if
(lastResult->resultStatus== PGRES_FATAL_ERROR &&            result->resultStatus == PGRES_FATAL_ERROR)        {
  pqCatenateResultError(lastResult, result->errMsg);            PQclear(result);            result = lastResult;
 
            /*             * Make sure PQerrorMessage agrees with concatenated result             */
resetPQExpBuffer(&conn->errorMessage);           appendPQExpBufferStr(&conn->errorMessage, result->errMsg);        }
   else            PQclear(lastResult);    }    lastResult = result;    if (result->resultStatus == PGRES_COPY_IN ||
   result->resultStatus == PGRES_COPY_OUT ||        conn->status == CONNECTION_BAD)        break;}
 
return lastResult;
}

/** PQdescribePrepared*      Obtain information about a previously prepared statement** If the query was not even sent,
returnNULL; conn->errorMessage is set to* a relevant message.* If the query was sent, a new PGresult is returned (which
couldindicate* either success or failure).    On success, the PGresult contains status* PGRES_COMMAND_OK, and its
parameterand column-heading fields describe* the statement's inputs and outputs respectively.* The user is responsible
forfreeing the PGresult via PQclear()* when done with it.*/
 
PGresult *
PQdescribePrepared(PGconn *conn, const char *stmt)
{if (!PQexecStart(conn))    return NULL;if (!PQsendDescribe(conn, 'S', stmt))    return NULL;return
PQexecFinish(conn);
}

/** PQdescribePortal*      Obtain information about a previously created portal** This is much like PQdescribePrepared,
exceptthat no parameter info is* returned.  Note that at the moment, libpq doesn't really expose portals* to the
client;but this can be used with a portal created by a SQL* DECLARE CURSOR command.*/
 
PGresult *
PQdescribePortal(PGconn *conn, const char *portal)
{if (!PQexecStart(conn))    return NULL;if (!PQsendDescribe(conn, 'P', portal))    return NULL;return
PQexecFinish(conn);
}

/** PQsendDescribePrepared*     Submit a Describe Statement command, but don't wait for it to finish** Returns: 1 if
successfullysubmitted*            0 if error (conn->errorMessage is set)*/
 
int
PQsendDescribePrepared(PGconn *conn, const char *stmt)
{return PQsendDescribe(conn, 'S', stmt);
}

/** PQsendDescribePortal*     Submit a Describe Portal command, but don't wait for it to finish** Returns: 1 if
successfullysubmitted*            0 if error (conn->errorMessage is set)*/
 
int
PQsendDescribePortal(PGconn *conn, const char *portal)
{return PQsendDescribe(conn, 'P', portal);
}

/** PQsendDescribe*     Common code to send a Describe command** Available options for desc_type are*     'S' to
describea prepared statement; or*     'P' to describe a portal.* Returns 1 on success and 0 on failure.*/
 
static int
PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
{/* Treat null desc_target as empty string */if (!desc_target)    desc_target = "";
if (!PQsendQueryStart(conn))    return 0;
/* This isn't gonna work on a 2.0 server */if (PG_PROTOCOL_MAJOR(conn->pversion) < 3){
printfPQExpBuffer(&conn->errorMessage,    libpq_gettext("function requires at least protocol version 3.0\n"));
return0;}
 
/* construct the Describe message */if (pqPutMsgStart('D', false, conn) < 0 ||    pqPutc(desc_type, conn) < 0 ||
pqPuts(desc_target,conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto sendFailed;
 
/* construct the Sync message */if (pqPutMsgStart('S', false, conn) < 0 ||    pqPutMsgEnd(conn) < 0)    goto
sendFailed;
/* remember we are doing a Describe */conn->queryclass = PGQUERY_DESCRIBE;
/* reset last-query string (not relevant now) */if (conn->last_query){    free(conn->last_query);    conn->last_query =
NULL;}
/* * Give the data a push.  In nonblock mode, don't complain if we're unable * to send it all; PQgetResult() will do
anyadditional flushing needed. */if (pqFlush(conn) < 0)    goto sendFailed;
 
/* OK, it's launched! */conn->asyncStatus = PGASYNC_BUSY;return 1;

sendFailed:pqHandleSendFailure(conn);return 0;
}

/** PQnotifies*      returns a PGnotify* structure of the latest async notification* that has not yet been handled**
returnsNULL, if there is currently* no unhandled async notification from the backend** the CALLER is responsible for
FREE'ingthe structure returned*/
 
PGnotify *
PQnotifies(PGconn *conn)
{PGnotify   *event;
if (!conn)    return NULL;
/* Parse any available data to see if we can extract NOTIFY messages. */parseInput(conn);
event = conn->notifyHead;if (event){    conn->notifyHead = event->next;    if (!conn->notifyHead)
conn->notifyTail= NULL;    event->next = NULL;        /* don't let app see the internal state */}return event;
 
}

/** PQputCopyData - send some data to the backend during COPY IN** Returns 1 if successful, 0 if data could not be sent
(onlypossible* in nonblock mode), or -1 if an error occurs.*/
 
int
PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
{if (!conn)    return -1;if (conn->asyncStatus != PGASYNC_COPY_IN){    printfPQExpBuffer(&conn->errorMessage,
          libpq_gettext("no COPY in progress\n"));    return -1;}
 
/* * Process any NOTICE or NOTIFY messages that might be pending in the * input buffer.  Since the server might
generatemany notices during the * COPY, we want to clean those out reasonably promptly to prevent * indefinite
expansionof the input buffer.  (Note: the actual read of * input data into the input buffer happens down inside
pqSendSome,but * it's not authorized to get rid of the data again.) */parseInput(conn);
 
if (nbytes > 0){    /*     * Try to flush any previously sent data in preference to growing the     * output buffer.
Ifwe can't enlarge the buffer enough to hold the     * data, return 0 in the nonblock case, else hard error. (For     *
simplicity,always assume 5 bytes of overhead even in protocol 2.0     * case.)     */    if ((conn->outBufSize -
conn->outCount- 5) < nbytes)    {        if (pqFlush(conn) < 0)            return -1;        if
(pqCheckOutBufferSpace(conn->outCount+ 5 + (size_t) nbytes,                                  conn))            return
pqIsnonblocking(conn)? 0 : -1;    }    /* Send the data (too simple to delegate to fe-protocol files) */    if
(PG_PROTOCOL_MAJOR(conn->pversion)>= 3)    {        if (pqPutMsgStart('d', false, conn) < 0 ||
pqPutnchar(buffer,nbytes, conn) < 0 ||            pqPutMsgEnd(conn) < 0)            return -1;    }    else    {
if(pqPutMsgStart(0, false, conn) < 0 ||            pqPutnchar(buffer, nbytes, conn) < 0 ||            pqPutMsgEnd(conn)
<0)            return -1;    }}return 1;
 
}

/** PQputCopyEnd - send EOF indication to the backend during COPY IN** After calling this, use PQgetResult() to check
commandcompletion status.** Returns 1 if successful, 0 if data could not be sent (only possible* in nonblock mode), or
-1if an error occurs.*/
 
int
PQputCopyEnd(PGconn *conn, const char *errormsg)
{if (!conn)    return -1;if (conn->asyncStatus != PGASYNC_COPY_IN){    printfPQExpBuffer(&conn->errorMessage,
          libpq_gettext("no COPY in progress\n"));    return -1;}
 
/* * Send the COPY END indicator.  This is simple enough that we don't * bother delegating it to the fe-protocol files.
*/if(PG_PROTOCOL_MAJOR(conn->pversion) >= 3){    if (errormsg)    {        /* Send COPY FAIL */        if
(pqPutMsgStart('f',false, conn) < 0 ||            pqPuts(errormsg, conn) < 0 ||            pqPutMsgEnd(conn) < 0)
    return -1;    }    else    {        /* Send COPY DONE */        if (pqPutMsgStart('c', false, conn) < 0 ||
 pqPutMsgEnd(conn) < 0)            return -1;    }
 
    /*     * If we sent the COPY command in extended-query mode, we must issue a     * Sync as well.     */    if
(conn->queryclass!= PGQUERY_SIMPLE)    {        if (pqPutMsgStart('S', false, conn) < 0 ||            pqPutMsgEnd(conn)
<0)            return -1;    }}else{    if (errormsg)    {        /* Ooops, no way to do this in 2.0 */
printfPQExpBuffer(&conn->errorMessage,                         libpq_gettext("function requires at least protocol
version3.0\n"));        return -1;    }    else    {        /* Send old-style end-of-data marker */        if
(pqPutMsgStart(0,false, conn) < 0 ||            pqPutnchar("\\.\n", 3, conn) < 0 ||            pqPutMsgEnd(conn) < 0)
        return -1;    }}
 
/* Return to active duty */conn->asyncStatus = PGASYNC_BUSY;resetPQExpBuffer(&conn->errorMessage);
/* Try to flush data */if (pqFlush(conn) < 0)    return -1;
return 1;
}

/** PQgetCopyData - read a row of data from the backend during COPY OUT** If successful, sets *buffer to point to a
malloc'drow of data, and* returns row length (always > 0) as result.* Returns 0 if no row available yet (only possible
ifasync is true),* -1 if end of copy (consult PQgetResult), or -2 if error (consult* PQerrorMessage).*/
 
int
PQgetCopyData(PGconn *conn, char **buffer, int async)
{*buffer = NULL;                /* for all failure cases */if (!conn)    return -2;if (conn->asyncStatus !=
PGASYNC_COPY_OUT){   printfPQExpBuffer(&conn->errorMessage,                      libpq_gettext("no COPY in
progress\n"));   return -2;}if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqGetCopyData3(conn, buffer,
async);else   return pqGetCopyData2(conn, buffer, async);
 
}

/** PQgetline - gets a newline-terminated string from the backend.** Chiefly here so that applications can use "COPY
<rel>to stdout"* and read the output string.    Returns a null-terminated string in s.** XXX this routine is now
deprecated,because it can't handle binary data.* If called during a COPY BINARY we return EOF.** PQgetline reads up to
maxlen-1characters (like fgets(3)) but strips* the terminating \n (like gets(3)).** CAUTION: the caller is responsible
fordetecting the end-of-copy signal* (a line containing just "\.") when using this routine.** RETURNS:*        EOF if
error(eg, invalid arguments are given)*        0 if EOL is reached (i.e., \n has been read)*                (this is
requiredfor backward-compatibility -- this*                 routine used to always return EOF or 0, assuming that*
          the line ended within maxlen bytes.)*        1 in other cases (i.e., the buffer was filled before \n is
reached)*/
int
PQgetline(PGconn *conn, char *s, int maxlen)
{if (!s || maxlen <= 0)    return EOF;*s = '\0';/* maxlen must be at least 3 to hold the \. terminator! */if (maxlen <
3)   return EOF;
 
if (!conn)    return EOF;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqGetline3(conn, s, maxlen);else    return pqGetline2(conn, s,
maxlen);
}

/** PQgetlineAsync - gets a COPY data row without blocking.** This routine is for applications that want to do "COPY
<rel>to stdout"* asynchronously, that is without blocking.  Having issued the COPY command* and gotten a PGRES_COPY_OUT
response,the app should call PQconsumeInput* and this routine until the end-of-data signal is detected.  Unlike*
PQgetline,this routine takes responsibility for detecting end-of-data.** On each call, PQgetlineAsync will return data
ifa complete data row* is available in libpq's input buffer.  Otherwise, no data is returned* until the rest of the row
arrives.**If -1 is returned, the end-of-data signal has been recognized (and removed* from libpq's input buffer).  The
caller*must* next call PQendcopy and* then return to normal processing.** RETURNS:*     -1    if the end-of-copy-data
markerhas been recognized*     0       if no data is available*     >0    the number of bytes returned.** The data
returnedwill not extend beyond a data-row boundary.  If possible* a whole row will be returned at one time.  But if the
bufferoffered by* the caller is too small to hold a row sent by the backend, then a partial* data row will be returned.
In text mode this can be detected by testing* whether the last returned byte is '\n' or not.** The returned data is
*not*null-terminated.*/
 

int
PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
{if (!conn)    return -1;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqGetlineAsync3(conn, buffer, bufsize);else    return
pqGetlineAsync2(conn,buffer, bufsize);
 
}

/** PQputline -- sends a string to the backend during COPY IN.* Returns 0 if OK, EOF if not.** This is deprecated
primarilybecause the return convention doesn't allow* caller to tell the difference between a hard error and a
nonblock-mode*send failure.*/
 
int
PQputline(PGconn *conn, const char *s)
{return PQputnbytes(conn, s, strlen(s));
}

/** PQputnbytes -- like PQputline, but buffer need not be null-terminated.* Returns 0 if OK, EOF if not.*/
int
PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
{if (PQputCopyData(conn, buffer, nbytes) > 0)    return 0;else    return EOF;
}

/** PQendcopy*        After completing the data transfer portion of a copy in/out,*        the application must call
thisroutine to finish the command protocol.** When using protocol 3.0 this is deprecated; it's cleaner to use
PQgetResult*to get the transfer status.    Note however that when using 2.0 protocol,* recovering from a copy failure
oftenrequires a PQreset.  PQendcopy will* take care of that, PQgetResult won't.** RETURNS:*        0 on success*
1on failure*/
 
int
PQendcopy(PGconn *conn)
{if (!conn)    return 0;
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqEndcopy3(conn);else    return pqEndcopy2(conn);
}


/* ----------------*        PQfn -    Send a function call to the POSTGRES backend.**        conn            : backend
connection*       fnid            : function id*        result_buf        : pointer to result buffer (&int if integer)*
      result_len        : length of return value.*        actual_result_len: actual length returned. (differs from
result_len*                         for varlena structures.)*        result_type        : If the result is an integer,
thismust be 1,*                          otherwise this should be 0*        args            : pointer to an array of
functionarguments.*                          (each has length, if integer, and value/pointer)*        nargs
:# of arguments in args array.** RETURNS*        PGresult with status = PGRES_COMMAND_OK if successful.*
*actual_result_lenis > 0 if there is a return value, 0 if not.*        PGresult with status = PGRES_FATAL_ERROR if
backendreturns an error.*        NULL on communications failure.  conn->errorMessage will be set.* ----------------*/
 

PGresult *
PQfn(PGconn *conn, int fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int
nargs)
{*actual_result_len = 0;
if (!conn)    return NULL;
/* clear the error string */resetPQExpBuffer(&conn->errorMessage);
if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE ||    conn->result != NULL){
printfPQExpBuffer(&conn->errorMessage,                     libpq_gettext("connection in wrong state\n"));    return
NULL;}
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)    return pqFunctionCall3(conn, fnid,                           result_buf,
actual_result_len,                          result_is_int,                           args, nargs);else    return
pqFunctionCall2(conn,fnid,                           result_buf, actual_result_len,
result_is_int,                          args, nargs);
 
}


/* ====== accessor funcs for PGresult ======== */

ExecStatusType
PQresultStatus(const PGresult *res)
{if (!res)    return PGRES_FATAL_ERROR;return res->resultStatus;
}

char *
PQresStatus(ExecStatusType status)
{if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0])    return libpq_gettext("invalid
ExecStatusTypecode");return pgresStatus[status];
 
}

char *
PQresultErrorMessage(const PGresult *res)
{if (!res || !res->errMsg)    return "";return res->errMsg;
}

char *
PQresultErrorField(const PGresult *res, int fieldcode)
{PGMessageField *pfield;
if (!res)    return NULL;for (pfield = res->errFields; pfield != NULL; pfield = pfield->next){    if (pfield->code ==
fieldcode)       return pfield->contents;}return NULL;
 
}

int
PQntuples(const PGresult *res)
{if (!res)    return 0;return res->ntups;
}

int
PQnfields(const PGresult *res)
{if (!res)    return 0;return res->numAttributes;
}

int
PQbinaryTuples(const PGresult *res)
{if (!res)    return 0;return res->binary;
}

/** Helper routines to range-check field numbers and tuple numbers.* Return TRUE if OK, FALSE if not*/

static int
check_field_number(const PGresult *res, int field_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (field_num < 0 || field_num >=
res->numAttributes){   pqInternalNotice(&res->noticeHooks,                     "column number %d is out of range
0..%d",                    field_num, res->numAttributes - 1);    return FALSE;}return TRUE;
 
}

static int
check_tuple_field_number(const PGresult *res,                     int tup_num, int field_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (tup_num < 0 || tup_num >=
res->ntups){   pqInternalNotice(&res->noticeHooks,                     "row number %d is out of range 0..%d",
         tup_num, res->ntups - 1);    return FALSE;}if (field_num < 0 || field_num >= res->numAttributes){
pqInternalNotice(&res->noticeHooks,                    "column number %d is out of range 0..%d",
field_num,res->numAttributes - 1);    return FALSE;}return TRUE;
 
}

static int
check_param_number(const PGresult *res, int param_num)
{if (!res)    return FALSE;            /* no way to display error message... */if (param_num < 0 || param_num >=
res->numParameters){   pqInternalNotice(&res->noticeHooks,                     "parameter number %d is out of range
0..%d",                    param_num, res->numParameters - 1);    return FALSE;}
 
return TRUE;
}

/** returns NULL if the field_num is invalid*/
char *
PQfname(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return NULL;if (res->attDescs)    return
res->attDescs[field_num].name;else   return NULL;
 
}

/** PQfnumber: find column number given column name** The column name is parsed as if it were in a SQL statement,
including*case-folding and double-quote processing.  But note a possible gotcha:* downcasing in the frontend might
followdifferent locale rules than* downcasing in the backend...** Returns -1 if no match.    In the present backend it
isalso possible* to have multiple matches, in which case the first one is found.*/
 
int
PQfnumber(const PGresult *res, const char *field_name)
{char       *field_case;bool        in_quotes;char       *iptr;char       *optr;int            i;
if (!res)    return -1;
/* * Note: it is correct to reject a zero-length input string; the proper * input to match a zero-length field name
wouldbe "". */if (field_name == NULL ||    field_name[0] == '\0' ||    res->attDescs == NULL)    return -1;
 
/* * Note: this code will not reject partially quoted strings, eg * foo"BAR"foo will become fooBARfoo when it probably
oughtto be an error * condition. */field_case = strdup(field_name);if (field_case == NULL)    return -1;
/*grotty */
 
in_quotes = false;optr = field_case;for (iptr = field_case; *iptr; iptr++){    char        c = *iptr;
    if (in_quotes)    {        if (c == '"')        {            if (iptr[1] == '"')            {                /*
doubledquotes become a single quote */                *optr++ = '"';                iptr++;            }
else               in_quotes = false;        }        else            *optr++ = c;    }    else if (c == '"')
in_quotes= true;    else    {        c = pg_tolower((unsigned char) c);        *optr++ = c;    }}*optr = '\0';
 
for (i = 0; i < res->numAttributes; i++){    if (strcmp(field_case, res->attDescs[i].name) == 0)    {
free(field_case);       return i;    }}free(field_case);return -1;
 
}

Oid
PQftable(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return InvalidOid;if (res->attDescs)    return
res->attDescs[field_num].tableid;else   return InvalidOid;
 
}

int
PQftablecol(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return
res->attDescs[field_num].columnid;else   return 0;
 
}

int
PQfformat(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return res->attDescs[field_num].format;else
  return 0;
 
}

Oid
PQftype(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return InvalidOid;if (res->attDescs)    return
res->attDescs[field_num].typid;else   return InvalidOid;
 
}

int
PQfsize(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return res->attDescs[field_num].typlen;else
  return 0;
 
}

int
PQfmod(const PGresult *res, int field_num)
{if (!check_field_number(res, field_num))    return 0;if (res->attDescs)    return
res->attDescs[field_num].atttypmod;else   return 0;
 
}

char *
PQcmdStatus(PGresult *res)
{if (!res)    return NULL;return res->cmdStatus;
}

/** PQoidStatus -*    if the last command was an INSERT, return the oid string*    if not, return ""*/
char *
PQoidStatus(const PGresult *res)
{/* * This must be enough to hold the result. Don't laugh, this is better * than what this function used to do.
*/staticchar buf[24];
 
size_t        len;
if (!res || !res->cmdStatus || strncmp(res->cmdStatus, "INSERT ", 7) != 0)    return "";
len = strspn(res->cmdStatus + 7, "0123456789");if (len > 23)    len = 23;strncpy(buf, res->cmdStatus + 7, len);buf[len]
='\0';
 
return buf;
}

/** PQoidValue -*    a perhaps preferable form of the above which just returns*    an Oid type*/
Oid
PQoidValue(const PGresult *res)
{char       *endptr = NULL;unsigned long result;
if (!res ||    !res->cmdStatus ||    strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||    res->cmdStatus[7] < '0' ||
res->cmdStatus[7]> '9')    return InvalidOid;
 
result = strtoul(res->cmdStatus + 7, &endptr, 10);
if (!endptr || (*endptr != ' ' && *endptr != '\0'))    return InvalidOid;else    return (Oid) result;
}


/** PQcmdTuples -*    If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return*    a string containing the
numberof inserted/affected tuples. If not,*    return "".**    XXX: this should probably return an int*/
 
char *
PQcmdTuples(PGresult *res)
{char       *p,           *c;
if (!res)    return "";
if (strncmp(res->cmdStatus, "INSERT ", 7) == 0){    p = res->cmdStatus + 7;    /* INSERT: skip oid and space */
while(*p && *p != ' ')        p++;    if (*p == 0)        goto interpret_error;        /* no space? */    p++;}else if
(strncmp(res->cmdStatus,"DELETE ", 7) == 0 ||         strncmp(res->cmdStatus, "UPDATE ", 7) == 0)    p = res->cmdStatus
+7;else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)    p = res->cmdStatus + 6;else if (strncmp(res->cmdStatus, "MOVE
",5) == 0 ||         strncmp(res->cmdStatus, "COPY ", 5) == 0)    p = res->cmdStatus + 5;else    return "";
 
/* check that we have an integer (at least one digit, nothing else) */for (c = p; *c; c++){    if (!isdigit((unsigned
char)*c))        goto interpret_error;}if (c == p)    goto interpret_error;
 
return p;

interpret_error:pqInternalNotice(&res->noticeHooks,                 "could not interpret result from server: %s",
         res->cmdStatus);return "";
 
}

/** PQgetvalue:*    return the value of field 'field_num' of row 'tup_num'*/
char *
PQgetvalue(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return NULL;return res->tuples[tup_num][field_num].value;
}

/* PQgetlength:*    returns the actual length of a field value in bytes.*/
int
PQgetlength(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return 0;if (res->tuples[tup_num][field_num].len !=
NULL_LEN)   return res->tuples[tup_num][field_num].len;else    return 0;
 
}

/* PQgetisnull:*    returns the null status of a field value.*/
int
PQgetisnull(const PGresult *res, int tup_num, int field_num)
{if (!check_tuple_field_number(res, tup_num, field_num))    return 1;                /* pretend it is null */if
(res->tuples[tup_num][field_num].len== NULL_LEN)    return 1;else    return 0;
 
}

/* PQnparams:*    returns the number of input parameters of a prepared statement.*/
int
PQnparams(const PGresult *res)
{if (!res)    return 0;return res->numParameters;
}

/* PQparamtype:*    returns type Oid of the specified statement parameter.*/
Oid
PQparamtype(const PGresult *res, int param_num)
{if (!check_param_number(res, param_num))    return InvalidOid;if (res->paramDescs)    return
res->paramDescs[param_num].typid;else   return InvalidOid;
 
}


/* PQsetnonblocking:*    sets the PGconn's database connection non-blocking if the arg is TRUE*    or makes it
non-blockingif the arg is FALSE, this will not protect*    you from PQexec(), you'll only be safe when using the
non-blockingAPI.*    Needs to be called only on a connected database connection.*/
 
int
PQsetnonblocking(PGconn *conn, int arg)
{bool        barg;
if (!conn || conn->status == CONNECTION_BAD)    return -1;
barg = (arg ? TRUE : FALSE);
/* early out if the socket is already in the state requested */if (barg == conn->nonblocking)    return 0;
/* * to guarantee constancy for flushing/query/result-polling behavior we * need to flush the send queue at this point
inorder to guarantee proper * behavior. this is ok because either they are making a transition _from_ * or _to_
blockingmode, either way we can block them. *//* if we are going from blocking to non-blocking flush here */if
(pqFlush(conn))   return -1;
 
conn->nonblocking = barg;
return 0;
}

/** return the blocking status of the database connection*        TRUE == nonblocking, FALSE == blocking*/
int
PQisnonblocking(const PGconn *conn)
{return pqIsnonblocking(conn);
}

/* libpq is thread-safe? */
int
PQisthreadsafe(void)
{
#ifdef ENABLE_THREAD_SAFETYreturn true;
#elsereturn false;
#endif
}


/* try to force data out, really only useful for non-blocking users */
int
PQflush(PGconn *conn)
{return pqFlush(conn);
}


/**        PQfreemem - safely frees memory allocated** Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)*
Usedfor freeing memory from PQescapeByte()a/PQunescapeBytea()*/
 
void
PQfreemem(void *ptr)
{free(ptr);
}

/** PQfreeNotify - free's the memory associated with a PGnotify** This function is here only for binary backward
compatibility.*New code should use PQfreemem().  A macro will automatically map* calls to PQfreemem.    It should be
removedin the future.  bjm 2003-03-24*/
 

#undef PQfreeNotify
void        PQfreeNotify(PGnotify *notify);

void
PQfreeNotify(PGnotify *notify)
{PQfreemem(notify);
}


/** Escaping arbitrary strings to get valid SQL literal strings.** Replaces "'" with "''", and if not std_strings,
replaces"\" with "\\".** length is the length of the source string.  (Note: if a terminating NUL* is encountered
sooner,PQescapeString stops short of "length"; the behavior* is thus rather like strncpy.)** For safety the buffer at
"to"must be at least 2*length + 1 bytes long.* A terminating NUL character is added to the output string, whether the*
inputis NUL-terminated or not.** Returns the actual length of the output (not counting the terminating NUL).*/
 
static size_t
PQescapeStringInternal(PGconn *conn,                   char *to, const char *from, size_t length,                   int
*error,                  int encoding, bool std_strings)
 
{const char *source = from;char       *target = to;size_t        remaining = length;
if (error)    *error = 0;
while (remaining > 0 && *source != '\0'){    char        c = *source;    int            len;    int            i;
    /* Fast path for plain ASCII */    if (!IS_HIGHBIT_SET(c))    {        /* Apply quoting if needed */        if
(SQL_STR_DOUBLE(c,!std_strings))            *target++ = c;        /* Copy the character */        *target++ = c;
source++;       remaining--;        continue;    }
 
    /* Slow path for possible multibyte characters */    len = pg_encoding_mblen(encoding, source);
    /* Copy the character */    for (i = 0; i < len; i++)    {        if (remaining == 0 || *source == '\0')
break;       *target++ = *source++;        remaining--;    }
 
    /*     * If we hit premature end of string (ie, incomplete multibyte     * character), try to pad out to the
correctlength with spaces. We     * may not be able to pad completely, but we will always be able to     * insert at
leastone pad space (since we'd not have quoted a     * multibyte character).  This should be enough to make a string
that    * the server will error out on.     */    if (i < len)    {        if (error)            *error = 1;        if
(conn)           printfPQExpBuffer(&conn->errorMessage,                      libpq_gettext("incomplete multibyte
character\n"));       for (; i < len; i++)        {            if (((size_t) (target - to)) / 2 >= length)
 break;            *target++ = ' ';        }        break;    }}
 
/* Write the terminating NUL character. */*target = '\0';
return target - to;
}

size_t
PQescapeStringConn(PGconn *conn,               char *to, const char *from, size_t length,               int *error)
{if (!conn){    /* force empty-string result */    *to = '\0';    if (error)        *error = 1;    return 0;}return
PQescapeStringInternal(conn,to, from, length, error,                              conn->client_encoding,
             conn->std_strings);
 
}

size_t
PQescapeString(char *to, const char *from, size_t length)
{return PQescapeStringInternal(NULL, to, from, length, NULL,                              static_client_encoding,
                      static_std_strings);
 
}

/**        PQescapeBytea    - converts from binary string to the*        minimal encoding necessary to include the
stringin an SQL*        INSERT statement with a bytea type column as the target.**        The following transformations
areapplied*        '\0' == ASCII  0 == \000*        '\'' == ASCII 39 == ''*        '\\' == ASCII 92 == \\*
anything< 0x20, or > 0x7e ---> \ooo*                                        (where ooo is an octal expression)*
Ifnot std_strings, all backslashes sent to the output are doubled.*/
 
static unsigned char *
PQescapeByteaInternal(PGconn *conn,                  const unsigned char *from, size_t from_length,
size_t*to_length, bool std_strings)
 
{const unsigned char *vp;unsigned char *rp;unsigned char *result;size_t        i;size_t        len;size_t
bslash_len= (std_strings ? 1 : 2);
 
/* * empty string has 1 char ('\0') */len = 1;
vp = from;for (i = from_length; i > 0; i--, vp++){    if (*vp < 0x20 || *vp > 0x7e)        len += bslash_len + 3;
elseif (*vp == '\'')        len += 2;    else if (*vp == '\\')        len += bslash_len + bslash_len;    else
len++;}
*to_length = len;rp = result = (unsigned char *) malloc(len);if (rp == NULL){    if (conn)
printfPQExpBuffer(&conn->errorMessage,                         libpq_gettext("out of memory\n"));    return NULL;}
 
vp = from;for (i = from_length; i > 0; i--, vp++){    if (*vp < 0x20 || *vp > 0x7e)    {        int            val =
*vp;
        if (!std_strings)            *rp++ = '\\';        *rp++ = '\\';        *rp++ = (val >> 6) + '0';        *rp++ =
((val>> 3) & 07) + '0';        *rp++ = (val & 07) + '0';    }    else if (*vp == '\'')    {        *rp++ = '\'';
*rp++= '\'';    }    else if (*vp == '\\')    {        if (!std_strings)        {            *rp++ = '\\';
*rp++= '\\';        }        *rp++ = '\\';        *rp++ = '\\';    }    else        *rp++ = *vp;}*rp = '\0';
 
return result;
}

unsigned char *
PQescapeByteaConn(PGconn *conn,              const unsigned char *from, size_t from_length,              size_t
*to_length)
{if (!conn)    return NULL;return PQescapeByteaInternal(conn, from, from_length, to_length,
conn->std_strings);
}

unsigned char *
PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
{return PQescapeByteaInternal(NULL, from, from_length, to_length,                             static_std_strings);
}


#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
#define OCTVAL(CH) ((CH) - '0')

/**        PQunescapeBytea - converts the null terminated string representation*        of a bytea, strtext, into
binary,filling a buffer. It returns a*        pointer to the buffer (or NULL on error), and the size of the*
bufferin retbuflen. The pointer may subsequently be used as an*        argument to the function PQfreemem.**        The
followingtransformations are made:*        \\     == ASCII 92 == \*        \ooo == a byte whose value = ooo (ooo is an
octalnumber)*        \x     == x (x is any character not matched by the above transformations)*/
 
unsigned char *
PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
{size_t        strtextlen,            buflen;unsigned char *buffer,           *tmpbuf;size_t        i,            j;
if (strtext == NULL)    return NULL;
strtextlen = strlen((const char *) strtext);
/* * Length of input is max length of output, but add one to avoid * unportable malloc(0) if input is zero-length.
*/buffer= (unsigned char *) malloc(strtextlen + 1);if (buffer == NULL)    return NULL;
 
for (i = j = 0; i < strtextlen;){    switch (strtext[i])    {        case '\\':            i++;            if
(strtext[i]== '\\')                buffer[j++] = strtext[i++];            else            {                if
((ISFIRSTOCTDIGIT(strtext[i]))&&                    (ISOCTDIGIT(strtext[i + 1])) &&
(ISOCTDIGIT(strtext[i+ 2])))                {                    int byte;
 
                    byte = OCTVAL(strtext[i++]);                    byte = (byte <<3) +OCTVAL(strtext[i++]);
       byte = (byte <<3) +OCTVAL(strtext[i++]);                    buffer[j++] = byte;                }            }
 
            /*             * Note: if we see '\' followed by something that isn't a             * recognized escape
sequence,we loop around having done             * nothing except advance i.  Therefore the something will be
* emitted as ordinary data on the next cycle. Corner case:             * '\' at end of string will just be discarded.
         */            break;
 
        default:            buffer[j++] = strtext[i++];            break;    }}buflen = j;                    /* buflen
isthe length of the dequoted data */
 
/* Shrink the buffer to be no larger than necessary *//* +1 avoids unportable behavior when buflen==0 */tmpbuf =
realloc(buffer,buflen + 1);
 
/* It would only be a very brain-dead realloc that could fail, but... */if (!tmpbuf){    free(buffer);    return
NULL;}
*retbuflen = buflen;return tmpbuf;
}

Re: exec_execute_message crash

From
Tom Lane
Date:
Tatsuo Ishii <ishii@postgresql.org> writes:
> Done. Inclded are C test program along with modified fe-exec.c.

> The modification made to fe-exec.c is sending Sync after Parse, Bind
> and Describe. Pgpool-II does this in order to get current transaction
> status.

I tried this but didn't have any luck crashing the backend.  libpq gets
tremendously confused by the extra ReadyForQuery responses, which is
unsurprising.  The postmaster log shows

LOG:  could not send data to client: Broken pipe
ERROR:  relation "foo" does not exist at character 15
STATEMENT:  SELECT * FROM foo
ERROR:  unnamed prepared statement does not exist
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ERROR:  current transaction is aborted, commands ignored until end of transaction block
STATEMENT:  SELECT NULL , n.nspname,  ct.relname,  a.attname,  a.attnum,  ci.relname FROM pg_catalog.pg_namespace n,
pg_catalog.pg_classct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i  WHERE
ct.oid=i.indrelidAND ci.oid=i.indexrelid  AND a.attrelid=ci.oid AND i.indisprimary  AND ct.relname =
'mst_Ucompany_feature_setting' AND ct.relnamespace = n.oid  AND n.nspname = 'foo' ORDER BY 1, 2, 3
 

So the "unnamed prepared statement does not exist" bit seems to be
related to what you are talking about, but it doesn't actually fail.
        regards, tom lane


Re: exec_execute_message crash

From
Tatsuo Ishii
Date:
> I tried this but didn't have any luck crashing the backend.  libpq gets
> tremendously confused by the extra ReadyForQuery responses, which is
> unsurprising.  The postmaster log shows
> 
> LOG:  could not send data to client: Broken pipe
> ERROR:  relation "foo" does not exist at character 15
> STATEMENT:  SELECT * FROM foo
> ERROR:  unnamed prepared statement does not exist
> ERROR:  current transaction is aborted, commands ignored until end of transaction block
> ERROR:  current transaction is aborted, commands ignored until end of transaction block
> STATEMENT:  SELECT NULL , n.nspname,  ct.relname,  a.attname,  a.attnum,  ci.relname FROM pg_catalog.pg_namespace n,
pg_catalog.pg_classct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i  WHERE
ct.oid=i.indrelidAND ci.oid=i.indexrelid  AND a.attrelid=ci.oid AND i.indisprimary  AND ct.relname =
'mst_Ucompany_feature_setting' AND ct.relnamespace = n.oid  AND n.nspname = 'foo' ORDER BY 1, 2, 3
 
> 
> So the "unnamed prepared statement does not exist" bit seems to be
> related to what you are talking about, but it doesn't actually fail.

I have put some debugging codes to make sure that portal->cplan and
portal->stmts belong to the same memory context by calling
GetMemoryChunkContext and surely they did. It appears that the memory
was surely deleted by MemeoryContextDelete in ReleaseCachedPlan. Also
I defined CLOBBER_FREED_MEMORY in aset.c to fill with 0x7f the freed
memory. Strange thing was portal->smts was not clobbered by 0x7f.
It seems I have missed something here...
--
Tatsuo Ishii
SRA OSS, Inc. Japan