|
|
|
@ -1,19 +1,20 @@ |
|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
|
* |
|
|
|
|
* dest.c-- |
|
|
|
|
* support for various communication destinations - see lib/H/tcop/dest.h |
|
|
|
|
* support for various communication destinations - see include/tcop/dest.h |
|
|
|
|
* |
|
|
|
|
* Copyright (c) 1994, Regents of the University of California |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.23 1998/09/01 04:32:10 momjian Exp $ |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.24 1999/01/27 00:36:14 tgl Exp $ |
|
|
|
|
* |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
/*
|
|
|
|
|
* INTERFACE ROUTINES |
|
|
|
|
* BeginCommand - prepare destination for tuples of the given type |
|
|
|
|
* DestToFunction - identify per-tuple processing routines |
|
|
|
|
* EndCommand - tell destination that no more tuples will arrive |
|
|
|
|
* NullCommand - tell dest that an empty query string was recognized |
|
|
|
|
* ReadyForQuery - tell dest that we are ready for a new query |
|
|
|
@ -23,6 +24,13 @@ |
|
|
|
|
* tuples are returned by a query to keep the backend and the |
|
|
|
|
* "destination" portals synchronized. |
|
|
|
|
* |
|
|
|
|
* There is a second level of initialization/cleanup performed by the |
|
|
|
|
* setup/cleanup routines identified by DestToFunction. This could |
|
|
|
|
* probably be merged with the work done by BeginCommand/EndCommand, |
|
|
|
|
* but as of right now BeginCommand/EndCommand are used in a rather |
|
|
|
|
* unstructured way --- some places call Begin without End, some vice |
|
|
|
|
* versa --- so I think I'll just leave 'em alone for now. tgl 1/99. |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
#include <stdio.h> /* for sprintf() */ |
|
|
|
|
#include <string.h> |
|
|
|
@ -47,44 +55,189 @@ |
|
|
|
|
static char CommandInfo[32] = {0}; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* output functions |
|
|
|
|
* dummy DestReceiver functions |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
static void |
|
|
|
|
donothing(HeapTuple tuple, TupleDesc attrdesc) |
|
|
|
|
donothingReceive (HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc); |
|
|
|
|
static void |
|
|
|
|
donothingSetup (DestReceiver* self, TupleDesc typeinfo) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void (* |
|
|
|
|
DestToFunction(CommandDest dest)) (HeapTuple, TupleDesc) |
|
|
|
|
static void |
|
|
|
|
donothingCleanup (DestReceiver* self) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* static DestReceiver structs for dest types needing no local state |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
static DestReceiver donothingDR = { |
|
|
|
|
donothingReceive, donothingSetup, donothingCleanup |
|
|
|
|
}; |
|
|
|
|
static DestReceiver printtup_internalDR = { |
|
|
|
|
printtup_internal, donothingSetup, donothingCleanup |
|
|
|
|
}; |
|
|
|
|
static DestReceiver be_printtupDR = { |
|
|
|
|
be_printtup, donothingSetup, donothingCleanup |
|
|
|
|
}; |
|
|
|
|
static DestReceiver debugtupDR = { |
|
|
|
|
debugtup, donothingSetup, donothingCleanup |
|
|
|
|
}; |
|
|
|
|
static DestReceiver spi_printtupDR = { |
|
|
|
|
spi_printtup, donothingSetup, donothingCleanup |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* BeginCommand - prepare destination for tuples of the given type |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
void |
|
|
|
|
BeginCommand(char *pname, |
|
|
|
|
int operation, |
|
|
|
|
TupleDesc tupdesc, |
|
|
|
|
bool isIntoRel, |
|
|
|
|
bool isIntoPortal, |
|
|
|
|
char *tag, |
|
|
|
|
CommandDest dest) |
|
|
|
|
{ |
|
|
|
|
PortalEntry *entry; |
|
|
|
|
Form_pg_attribute *attrs = tupdesc->attrs; |
|
|
|
|
int natts = tupdesc->natts; |
|
|
|
|
int i; |
|
|
|
|
char *p; |
|
|
|
|
|
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case RemoteInternal: |
|
|
|
|
return printtup_internal; |
|
|
|
|
case Remote: |
|
|
|
|
case RemoteInternal: |
|
|
|
|
/* ----------------
|
|
|
|
|
* if this is a "retrieve portal" query, done |
|
|
|
|
* because nothing needs to be sent to the fe. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
CommandInfo[0] = '\0'; |
|
|
|
|
if (isIntoPortal) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* if portal name not specified for remote query, |
|
|
|
|
* use the "blank" portal. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (pname == NULL) |
|
|
|
|
pname = "blank"; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* send fe info on tuples we're about to send |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
pq_putnchar("P", 1);/* new portal.. */ |
|
|
|
|
pq_putstr(pname); /* portal name */ |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* if this is a retrieve, then we send back the tuple |
|
|
|
|
* descriptor of the tuples. "retrieve into" is an |
|
|
|
|
* exception because no tuples are returned in that case. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (operation == CMD_SELECT && !isIntoRel) |
|
|
|
|
{ |
|
|
|
|
pq_putnchar("T", 1); /* type info to follow.. */ |
|
|
|
|
pq_putint(natts, 2); /* number of attributes in tuples */ |
|
|
|
|
|
|
|
|
|
for (i = 0; i < natts; ++i) |
|
|
|
|
{ |
|
|
|
|
pq_putstr(attrs[i]->attname.data); |
|
|
|
|
pq_putint((int) attrs[i]->atttypid, sizeof(attrs[i]->atttypid)); |
|
|
|
|
pq_putint(attrs[i]->attlen, sizeof(attrs[i]->attlen)); |
|
|
|
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) |
|
|
|
|
pq_putint(attrs[i]->atttypmod, sizeof(attrs[i]->atttypmod)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
|
/* ----------------
|
|
|
|
|
* prepare local portal buffer for query results |
|
|
|
|
* and setup result for PQexec() |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
entry = be_currentportal(); |
|
|
|
|
if (pname != NULL) |
|
|
|
|
pbuf_setportalinfo(entry, pname); |
|
|
|
|
|
|
|
|
|
if (operation == CMD_SELECT && !isIntoRel) |
|
|
|
|
{ |
|
|
|
|
be_typeinit(entry, tupdesc, natts); |
|
|
|
|
p = (char *) palloc(strlen(entry->name) + 2); |
|
|
|
|
p[0] = 'P'; |
|
|
|
|
strcpy(p + 1, entry->name); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
p = (char *) palloc(strlen(tag) + 2); |
|
|
|
|
p[0] = 'C'; |
|
|
|
|
strcpy(p + 1, tag); |
|
|
|
|
} |
|
|
|
|
entry->result = p; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Debug: |
|
|
|
|
/* ----------------
|
|
|
|
|
* show the return type of the tuples |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (pname == NULL) |
|
|
|
|
pname = "blank"; |
|
|
|
|
|
|
|
|
|
showatts(pname, tupdesc); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case None: |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* DestToFunction - return appropriate receiver function set for dest |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
DestReceiver* |
|
|
|
|
DestToFunction(CommandDest dest) |
|
|
|
|
{ |
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case Remote: |
|
|
|
|
return printtup; |
|
|
|
|
/* printtup wants a dynamically allocated DestReceiver */ |
|
|
|
|
return printtup_create_DR(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case RemoteInternal: |
|
|
|
|
return & printtup_internalDR; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
|
return be_printtup; |
|
|
|
|
return & be_printtupDR; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Debug: |
|
|
|
|
return debugtup; |
|
|
|
|
return & debugtupDR; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SPI: |
|
|
|
|
return spi_printtup; |
|
|
|
|
return & spi_printtupDR; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case None: |
|
|
|
|
default: |
|
|
|
|
return donothing; |
|
|
|
|
return & donothingDR; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -92,7 +245,7 @@ void (* |
|
|
|
|
* never gets here, but DECstation lint appears to be stupid... |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
return donothing; |
|
|
|
|
return & donothingDR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
@ -106,16 +259,15 @@ EndCommand(char *commandTag, CommandDest dest) |
|
|
|
|
|
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case RemoteInternal: |
|
|
|
|
case Remote: |
|
|
|
|
case RemoteInternal: |
|
|
|
|
/* ----------------
|
|
|
|
|
* tell the fe that the query is over |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
pq_putnchar("C", 1); |
|
|
|
|
sprintf(buf, "%s%s", commandTag, CommandInfo); |
|
|
|
|
CommandInfo[0] = 0; |
|
|
|
|
sprintf(buf, "C%s%s", commandTag, CommandInfo); |
|
|
|
|
pq_putstr(buf); |
|
|
|
|
CommandInfo[0] = '\0'; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
@ -172,15 +324,13 @@ NullCommand(CommandDest dest) |
|
|
|
|
{ |
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case RemoteInternal: |
|
|
|
|
case Remote: |
|
|
|
|
{ |
|
|
|
|
/* ----------------
|
|
|
|
|
* tell the fe that we saw an empty query string |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
pq_putstr("I"); |
|
|
|
|
} |
|
|
|
|
case RemoteInternal: |
|
|
|
|
case Remote: |
|
|
|
|
/* ----------------
|
|
|
|
|
* tell the fe that we saw an empty query string |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
pq_putstr("I"); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
@ -204,132 +354,18 @@ NullCommand(CommandDest dest) |
|
|
|
|
void |
|
|
|
|
ReadyForQuery(CommandDest dest) |
|
|
|
|
{ |
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case RemoteInternal: |
|
|
|
|
case Remote: |
|
|
|
|
{ |
|
|
|
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) |
|
|
|
|
pq_putnchar("Z", 1); |
|
|
|
|
/* Flush output at end of cycle in any case. */ |
|
|
|
|
pq_flush(); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
|
case Debug: |
|
|
|
|
case None: |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* BeginCommand - prepare destination for tuples of the given type |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
void |
|
|
|
|
BeginCommand(char *pname, |
|
|
|
|
int operation, |
|
|
|
|
TupleDesc tupdesc, |
|
|
|
|
bool isIntoRel, |
|
|
|
|
bool isIntoPortal, |
|
|
|
|
char *tag, |
|
|
|
|
CommandDest dest) |
|
|
|
|
{ |
|
|
|
|
PortalEntry *entry; |
|
|
|
|
Form_pg_attribute *attrs = tupdesc->attrs; |
|
|
|
|
int natts = tupdesc->natts; |
|
|
|
|
int i; |
|
|
|
|
char *p; |
|
|
|
|
|
|
|
|
|
switch (dest) |
|
|
|
|
{ |
|
|
|
|
case RemoteInternal: |
|
|
|
|
case Remote: |
|
|
|
|
/* ----------------
|
|
|
|
|
* if this is a "retrieve portal" query, just return |
|
|
|
|
* because nothing needs to be sent to the fe. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
CommandInfo[0] = 0; |
|
|
|
|
if (isIntoPortal) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* if portal name not specified for remote query, |
|
|
|
|
* use the "blank" portal. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (pname == NULL) |
|
|
|
|
pname = "blank"; |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* send fe info on tuples we're about to send |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
pq_putnchar("P", 1);/* new portal.. */ |
|
|
|
|
pq_putstr(pname); /* portal name */ |
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* if this is a retrieve, then we send back the tuple |
|
|
|
|
* descriptor of the tuples. "retrieve into" is an |
|
|
|
|
* exception because no tuples are returned in that case. |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (operation == CMD_SELECT && !isIntoRel) |
|
|
|
|
{ |
|
|
|
|
pq_putnchar("T", 1); /* type info to follow.. */ |
|
|
|
|
pq_putint(natts, 2); /* number of attributes in tuples */ |
|
|
|
|
|
|
|
|
|
for (i = 0; i < natts; ++i) |
|
|
|
|
{ |
|
|
|
|
pq_putstr(attrs[i]->attname.data); |
|
|
|
|
pq_putint((int) attrs[i]->atttypid, sizeof(attrs[i]->atttypid)); |
|
|
|
|
pq_putint(attrs[i]->attlen, sizeof(attrs[i]->attlen)); |
|
|
|
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) |
|
|
|
|
pq_putint(attrs[i]->atttypmod, sizeof(attrs[i]->atttypmod)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) |
|
|
|
|
pq_putnchar("Z", 1); |
|
|
|
|
/* Flush output at end of cycle in any case. */ |
|
|
|
|
pq_flush(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Local: |
|
|
|
|
/* ----------------
|
|
|
|
|
* prepare local portal buffer for query results |
|
|
|
|
* and setup result for PQexec() |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
entry = be_currentportal(); |
|
|
|
|
if (pname != NULL) |
|
|
|
|
pbuf_setportalinfo(entry, pname); |
|
|
|
|
|
|
|
|
|
if (operation == CMD_SELECT && !isIntoRel) |
|
|
|
|
{ |
|
|
|
|
be_typeinit(entry, tupdesc, natts); |
|
|
|
|
p = (char *) palloc(strlen(entry->name) + 2); |
|
|
|
|
p[0] = 'P'; |
|
|
|
|
strcpy(p + 1, entry->name); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
p = (char *) palloc(strlen(tag) + 2); |
|
|
|
|
p[0] = 'C'; |
|
|
|
|
strcpy(p + 1, tag); |
|
|
|
|
} |
|
|
|
|
entry->result = p; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case Debug: |
|
|
|
|
/* ----------------
|
|
|
|
|
* show the return type of the tuples |
|
|
|
|
* ---------------- |
|
|
|
|
*/ |
|
|
|
|
if (pname == NULL) |
|
|
|
|
pname = "blank"; |
|
|
|
|
|
|
|
|
|
showatts(pname, tupdesc); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case None: |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
@ -341,7 +377,7 @@ UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples) |
|
|
|
|
{ |
|
|
|
|
switch (operation) |
|
|
|
|
{ |
|
|
|
|
case CMD_INSERT: |
|
|
|
|
case CMD_INSERT: |
|
|
|
|
if (tuples > 1) |
|
|
|
|
lastoid = InvalidOid; |
|
|
|
|
sprintf(CommandInfo, " %u %u", lastoid, tuples); |
|
|
|
@ -351,7 +387,7 @@ UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples) |
|
|
|
|
sprintf(CommandInfo, " %u", tuples); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
CommandInfo[0] = 0; |
|
|
|
|
CommandInfo[0] = '\0'; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|