1 /*-------------------------------------------------------------------------
4 * support for collection of returned tuples from an internal
5 * PQ call into a backend buffer.
7 * Copyright (c) 1994, Regents of the University of California
9 * $Id: be-dumpdata.c,v 1.23 1999/05/10 00:45:08 momjian Exp $
11 *-------------------------------------------------------------------------
15 * be_portalinit - initialize backend portal administration
16 * be_portalpush - add a portal to the top of the portal stack
17 * be_portalpop - remove portal on the top of the stack & return it
18 * be_currentportal - return the top portal on the portal stack
19 * be_newportal - return a new portal.
20 * be_portalinit - initialize backend portal expected to hold results.
21 * be_printtup - add a tuple to a backend portal
24 * Since backend user-defined operators can call queries
25 * which in turn call user-defined operators can call queries...
26 * we have to keep track of portals on a stack. BeginCommand()
27 * puts portals on the stack and the PQ functions remove them.
34 #include <lib/dllist.h>
35 #include <libpq/libpq.h>
36 #include <access/heapam.h>
37 #include <access/htup.h>
38 #include <storage/buf.h>
39 #include <utils/memutils.h>
41 #include <utils/mcxt.h>
42 #include <utils/exc.h>
43 #include <utils/syscache.h>
44 #include <catalog/pg_type.h>
45 #include <catalog/catalog.h>
46 #include <access/printtup.h>
49 * backend portal stack for recursive PQexec calls
52 static Dllist *be_portalstack;
55 * be_portalinit - initialize backend portal administration
57 * This is called once from InitPostgres() to initialize
64 be_portalstack = DLNewList();
68 * be_portalpush - add a portal to the top of the portal stack
70 * used by BeginCommand()
74 be_portalpush(PortalEntry *entry)
76 DLAddTail(be_portalstack, DLNewElem(entry));
80 * be_portalpop - remove the portal on the top of the stack & return it
91 elt = DLRemTail(be_portalstack);
93 p = (elt ? (PortalEntry *) DLE_VAL(elt) : NULL);
101 * be_currentportal - return the top portal on the portal stack
103 * used by be_printtup()
107 be_currentportal(void)
111 elt = DLGetTail(be_portalstack);
112 return elt ? (PortalEntry *) DLE_VAL(elt) : NULL;
116 * be_newportal - return a new portal.
118 * If the user-defined function does not specify a portal name,
119 * we generate a unique one. Names are generated from a combination
120 * of a postgres oid and an integer counter which is incremented
121 * every time we ask for a local portal.
123 * used by BeginCommand()
127 static Oid be_portaloid;
128 static u_int be_portalcnt = 0;
134 char buf[PortalNameLength];
137 * generate a new name
140 if (be_portalcnt == 0)
141 be_portaloid = newoid();
143 snprintf(buf, PortalNameLength, "be_%u_%d", be_portaloid, be_portalcnt);
146 * initialize the new portal entry and keep track
147 * of the current memory context for be_printtup().
148 * This is important - otherwise whatever we allocate
149 * will go away and the contents of the portal after
150 * PQexec() returns will be meaningless.
153 entry = pbuf_setup(buf);
154 entry->portalcxt = (Pointer) CurrentMemoryContext;
160 * be_typeinit - initialize backend portal expected to hold
163 * used by BeginCommand()
167 be_typeinit(PortalEntry *entry,
171 PortalBuffer *portal;
174 Form_pg_attribute *attrs = tupDesc->attrs;
177 * add a new portal group to the portal
180 portal = entry->portal;
182 portal->groups = group = pbuf_addGroup(portal);
183 group->no_fields = natts;
186 * initialize portal group type info
191 group->types = pbuf_addTypes(natts);
192 for (i = 0; i < natts; ++i)
194 strncpy(group->types[i].name, attrs[i]->attname.data, NAMEDATALEN);
195 group->types[i].typid = attrs[i]->atttypid;
196 group->types[i].typlen = attrs[i]->attlen;
202 * be_printtup - add a tuple to a backend portal
204 * used indirectly by ExecRetrieve()
206 * This code is pretty much copied from printtup(), dump_type()
207 * and dump_data(). -cim 2/12/91
211 be_printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
219 PortalEntry *entry = NULL;
220 PortalBuffer *portal = NULL;
221 GroupBuffer *group = NULL;
222 TupleBlock *tuples = NULL;
226 MemoryContext savecxt;
229 * get the current portal and group
232 entry = be_currentportal();
233 portal = entry->portal;
234 group = portal->groups;
237 * switch to the portal's memory context so that
238 * the tuples we allocate are returned to the user.
241 savecxt = MemoryContextSwitchTo((MemoryContext) entry->portalcxt);
244 * If no tuple block yet, allocate one.
245 * If the current block is full, allocate another one.
248 if (group->tuples == NULL)
250 tuples = group->tuples = pbuf_addTuples();
251 tuples->tuple_index = 0;
255 tuples = group->tuples;
256 /* walk to the end of the linked list of TupleBlocks */
258 tuples = tuples->next;
261 * now, tuples is the last TupleBlock, check to see if it is full.
262 * If so, allocate a new TupleBlock and add it to the end of the
266 if (tuples->tuple_index == TupleBlockSize)
268 tuples->next = pbuf_addTuples();
269 tuples = tuples->next;
270 tuples->tuple_index = 0;
275 * Allocate space for a tuple.
278 tuples->values[tuples->tuple_index] = pbuf_addTuple(tuple->t_data->t_natts);
279 tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(tuple->t_data->t_natts);
281 * copy printable representations of the tuple's attributes
284 * This seems silly, because the user's function which is calling
285 * PQexec() or PQfn() will probably just convert this back into the
286 * internal form anyways, but the point here is to provide a uniform
287 * libpq interface and this is how the fe libpq interface currently
288 * works. Pretty soon we'll have to add code to let the fe or be
289 * select the desired data representation and then deal with that.
290 * This should not be too hard, as there already exist typrecieve()
291 * and typsend() procedures for user-defined types (see pg_type.h)
296 values = tuples->values[tuples->tuple_index];
297 lengths = tuples->lengths[tuples->tuple_index];
299 for (i = 0; i < tuple->t_data->t_natts; i++)
301 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
302 getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
303 &typoutput, &typelem);
305 lengths[i] = typeinfo->attrs[i]->attlen;
307 if (lengths[i] == -1) /* variable length attribute */
310 lengths[i] = VARSIZE(attr) - VARHDRSZ;
315 if (!isnull && OidIsValid(typoutput))
316 values[i] = fmgr(typoutput, attr, typelem,
317 typeinfo->attrs[i]->atttypmod);
324 * increment tuple group counters
329 tuples->tuple_index++;
332 * return to the original memory context
335 MemoryContextSwitchTo(savecxt);