"git@git.cse.iitb.ac.in:shashankshet/Seminar-HFO.git" did not exist on "8d19b3d9b34268796c00988ea471ff46c5fbc289"
portalcmds.c 12.9 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * portalcmds.c
4 5 6
 *	  Utility commands affecting portals (that is, SQL cursor commands)
 *
 * Note: see also tcop/pquery.c, which implements portal operations for
Bruce Momjian's avatar
Bruce Momjian committed
7
 * the FE/BE protocol.	This module uses pquery.c for some operations.
8 9
 * And both modules depend on utils/mmgr/portalmem.c, which controls
 * storage management for portals (but doesn't run any queries in them).
Bruce Momjian's avatar
Bruce Momjian committed
10
 *
11
 *
12
 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
13 14 15 16
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
17
 *	  $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.62 2007/03/13 00:33:39 tgl Exp $
18 19 20 21 22 23
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

24 25
#include <limits.h>

26
#include "access/xact.h"
27 28
#include "commands/portalcmds.h"
#include "executor/executor.h"
29 30
#include "optimizer/planner.h"
#include "rewrite/rewriteHandler.h"
31
#include "tcop/pquery.h"
32
#include "tcop/tcopprot.h"
33
#include "utils/memutils.h"
34

35

36
/*
37 38
 * PerformCursorOpen
 *		Execute SQL DECLARE CURSOR command.
39 40
 */
void
41 42
PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
				  const char *queryString, bool isTopLevel)
43
{
44 45
	Oid		   *param_types;
	int			num_params;
46 47
	List	   *rewritten;
	Query	   *query;
48
	PlannedStmt *plan;
49 50
	Portal		portal;
	MemoryContext oldContext;
51 52 53 54 55

	/*
	 * Disallow empty-string cursor name (conflicts with protocol-level
	 * unnamed portal).
	 */
56
	if (!stmt->portalname || stmt->portalname[0] == '\0')
57 58 59
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_CURSOR_NAME),
				 errmsg("invalid cursor name: must not be empty")));
60

61
	/*
62 63 64
	 * If this is a non-holdable cursor, we require that this statement has
	 * been executed inside a transaction block (or else, it would have no
	 * user-visible effect).
65 66
	 */
	if (!(stmt->options & CURSOR_OPT_HOLD))
67
		RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
68

69
	/*
70
	 * Don't allow both SCROLL and NO SCROLL to be specified
71
	 */
72 73 74 75 76 77 78 79
	if ((stmt->options & CURSOR_OPT_SCROLL) &&
		(stmt->options & CURSOR_OPT_NO_SCROLL))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
				 errmsg("cannot specify both SCROLL and NO SCROLL")));

	/* Convert parameter type data to the form parser wants */
	getParamListTypes(params, &param_types, &num_params);
80

81
	/*
82 83 84 85 86 87 88 89
	 * Run parse analysis and rewrite.  Note this also acquires sufficient
	 * locks on the source table(s).
	 *
	 * Because the parser and planner tend to scribble on their input, we
	 * make a preliminary copy of the source querytree.  This prevents
	 * problems in the case that the DECLARE CURSOR is in a portal or plpgsql
	 * function and is executed repeatedly.  (See also the same hack in
	 * COPY and PREPARE.)  XXX FIXME someday.
90
	 */
91 92 93 94
	rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
									   queryString, param_types, num_params);

	/* We don't expect more or less than one result query */
95
	if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
96
		elog(ERROR, "unexpected rewrite result");
97
	query = (Query *) linitial(rewritten);
98
	if (query->commandType != CMD_SELECT)
99
		elog(ERROR, "unexpected rewrite result");
100

101
	/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
102
	if (query->into)
103
		ereport(ERROR,
104
				(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
105
				 errmsg("DECLARE CURSOR cannot specify INTO")));
106

107
	if (query->rowMarks != NIL)
108 109
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
110
			  errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
111
				 errdetail("Cursors must be READ ONLY.")));
112

113
	/* plan the query */
114
	plan = planner(query, true, stmt->options, params);
115

116
	/*
117
	 * Create a portal and copy the plan into its memory context.
118
	 */
119
	portal = CreatePortal(stmt->portalname, false, false);
120 121

	oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
122

123
	plan = copyObject(plan);
124

125
	PortalDefineQuery(portal,
126
					  NULL,
127
					  queryString,
Bruce Momjian's avatar
Bruce Momjian committed
128
					  "SELECT", /* cursor's query is always a SELECT */
129
					  list_make1(plan),
130
					  NULL);
131

132
	/*----------
133
	 * Also copy the outer portal's parameter list into the inner portal's
134
	 * memory context.	We want to pass down the parameter values in case we
135 136 137 138 139 140
	 * had a command like
	 *		DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
	 * This will have been parsed using the outer parameter set and the
	 * parameter value needs to be preserved for use when the cursor is
	 * executed.
	 *----------
141 142 143
	 */
	params = copyParamList(params);

144 145
	MemoryContextSwitchTo(oldContext);

146
	/*
147 148
	 * Set up options for portal.
	 *
Bruce Momjian's avatar
Bruce Momjian committed
149
	 * If the user didn't specify a SCROLL type, allow or disallow scrolling
150 151
	 * based on whether it would require any additional runtime overhead to do
	 * so.
152
	 */
153 154 155
	portal->cursorOptions = stmt->options;
	if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
	{
156
		if (ExecSupportsBackwardScan(plan->planTree))
157 158 159 160
			portal->cursorOptions |= CURSOR_OPT_SCROLL;
		else
			portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
	}
161 162

	/*
163
	 * Start execution, inserting parameters if any.
164
	 */
165
	PortalStart(portal, params, ActiveSnapshot);
166

167
	Assert(portal->strategy == PORTAL_ONE_SELECT);
168 169

	/*
170 171
	 * We're done; the query won't actually be run until PerformPortalFetch is
	 * called.
172 173
	 */
}
174 175 176

/*
 * PerformPortalFetch
177
 *		Execute SQL FETCH or MOVE command.
178
 *
179
 *	stmt: parsetree node for command
180 181 182 183 184 185 186
 *	dest: where to send results
 *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
 *		in which to store a command completion status string.
 *
 * completionTag may be NULL if caller doesn't want a status string.
 */
void
187
PerformPortalFetch(FetchStmt *stmt,
188
				   DestReceiver *dest,
189 190 191
				   char *completionTag)
{
	Portal		portal;
192
	long		nprocessed;
193

194 195 196 197 198
	/*
	 * Disallow empty-string cursor name (conflicts with protocol-level
	 * unnamed portal).
	 */
	if (!stmt->portalname || stmt->portalname[0] == '\0')
199 200 201
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_CURSOR_NAME),
				 errmsg("invalid cursor name: must not be empty")));
202

203
	/* get the portal from the portal name */
204
	portal = GetPortalByName(stmt->portalname);
205 206
	if (!PortalIsValid(portal))
	{
207
		ereport(ERROR,
208
				(errcode(ERRCODE_UNDEFINED_CURSOR),
209
				 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
Bruce Momjian's avatar
Bruce Momjian committed
210
		return;					/* keep compiler happy */
211 212
	}

213
	/* Adjust dest if needed.  MOVE wants destination DestNone */
214
	if (stmt->ismove)
215
		dest = None_Receiver;
216

217
	/* Do it */
218 219 220
	nprocessed = PortalRunFetch(portal,
								stmt->direction,
								stmt->howMany,
221
								dest);
222 223 224

	/* Return command status if wanted */
	if (completionTag)
225
		snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
226
				 stmt->ismove ? "MOVE" : "FETCH",
227 228 229
				 nprocessed);
}

230 231
/*
 * PerformPortalClose
232
 *		Close a cursor.
233 234
 */
void
235
PerformPortalClose(const char *name)
236 237 238
{
	Portal		portal;

239 240 241 242 243
	/*
	 * Disallow empty-string cursor name (conflicts with protocol-level
	 * unnamed portal).
	 */
	if (!name || name[0] == '\0')
244 245 246
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_CURSOR_NAME),
				 errmsg("invalid cursor name: must not be empty")));
247

248 249 250 251 252 253
	/*
	 * get the portal from the portal name
	 */
	portal = GetPortalByName(name);
	if (!PortalIsValid(portal))
	{
254
		ereport(ERROR,
255
				(errcode(ERRCODE_UNDEFINED_CURSOR),
256
				 errmsg("cursor \"%s\" does not exist", name)));
Bruce Momjian's avatar
Bruce Momjian committed
257
		return;					/* keep compiler happy */
258 259 260 261 262
	}

	/*
	 * Note: PortalCleanup is called as a side-effect
	 */
263
	PortalDrop(portal, false);
264
}
265 266 267 268

/*
 * PortalCleanup
 *
269 270
 * Clean up a portal when it's dropped.  This is the standard cleanup hook
 * for portals.
271 272
 */
void
273
PortalCleanup(Portal portal)
274
{
275 276
	QueryDesc  *queryDesc;

277 278 279 280 281 282
	/*
	 * sanity checks
	 */
	AssertArg(PortalIsValid(portal));
	AssertArg(portal->cleanup == PortalCleanup);

283
	/*
284 285 286
	 * Shut down executor, if still running.  We skip this during error abort,
	 * since other mechanisms will take care of releasing executor resources,
	 * and we can't be sure that ExecutorEnd itself wouldn't fail.
287
	 */
288 289
	queryDesc = PortalGetQueryDesc(portal);
	if (queryDesc)
290
	{
291
		portal->queryDesc = NULL;
292 293 294 295 296 297
		if (portal->status != PORTAL_FAILED)
		{
			ResourceOwner saveResourceOwner;

			/* We must make the portal's resource owner current */
			saveResourceOwner = CurrentResourceOwner;
298 299 300
			PG_TRY();
			{
				CurrentResourceOwner = portal->resowner;
301
				/* we do not need AfterTriggerEndQuery() here */
302
				ExecutorEnd(queryDesc);
303 304 305 306 307 308 309 310
			}
			PG_CATCH();
			{
				/* Ensure CurrentResourceOwner is restored on error */
				CurrentResourceOwner = saveResourceOwner;
				PG_RE_THROW();
			}
			PG_END_TRY();
311 312
			CurrentResourceOwner = saveResourceOwner;
		}
313
	}
314 315 316 317 318 319 320 321 322 323 324 325 326
}

/*
 * PersistHoldablePortal
 *
 * Prepare the specified Portal for access outside of the current
 * transaction. When this function returns, all future accesses to the
 * portal must be done via the Tuplestore (not by invoking the
 * executor).
 */
void
PersistHoldablePortal(Portal portal)
{
Bruce Momjian's avatar
Bruce Momjian committed
327
	QueryDesc  *queryDesc = PortalGetQueryDesc(portal);
328
	Portal		saveActivePortal;
329
	Snapshot	saveActiveSnapshot;
330
	ResourceOwner saveResourceOwner;
331
	MemoryContext savePortalContext;
332
	MemoryContext oldcxt;
333 334

	/*
Bruce Momjian's avatar
Bruce Momjian committed
335 336
	 * If we're preserving a holdable portal, we had better be inside the
	 * transaction that originally created it.
337
	 */
338
	Assert(portal->createSubid != InvalidSubTransactionId);
339
	Assert(queryDesc != NULL);
340 341

	/*
342
	 * Caller must have created the tuplestore already.
343
	 */
344
	Assert(portal->holdContext != NULL);
345
	Assert(portal->holdStore != NULL);
346

347
	/*
348 349
	 * Before closing down the executor, we must copy the tupdesc into
	 * long-term memory, since it was created in executor memory.
350
	 */
351 352
	oldcxt = MemoryContextSwitchTo(portal->holdContext);

353 354 355 356 357 358 359
	portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);

	MemoryContextSwitchTo(oldcxt);

	/*
	 * Check for improper portal use, and mark portal active.
	 */
360
	if (portal->status != PORTAL_READY)
361
		ereport(ERROR,
362 363 364
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("portal \"%s\" cannot be run", portal->name)));
	portal->status = PORTAL_ACTIVE;
365 366

	/*
367
	 * Set up global portal context pointers.
368
	 */
369
	saveActivePortal = ActivePortal;
370
	saveActiveSnapshot = ActiveSnapshot;
371
	saveResourceOwner = CurrentResourceOwner;
372
	savePortalContext = PortalContext;
373 374 375
	PG_TRY();
	{
		ActivePortal = portal;
376
		ActiveSnapshot = queryDesc->snapshot;
377 378 379 380 381 382
		CurrentResourceOwner = portal->resowner;
		PortalContext = PortalGetHeapMemory(portal);

		MemoryContextSwitchTo(PortalContext);

		/*
383 384
		 * Rewind the executor: we need to store the entire result set in the
		 * tuplestore, so that subsequent backward FETCHs can be processed.
385 386 387 388
		 */
		ExecutorRewind(queryDesc);

		/* Change the destination to output to the tuplestore */
389
		queryDesc->dest = CreateDestReceiver(DestTuplestore, portal);
390 391 392 393 394 395 396 397 398 399

		/* Fetch the result set into the tuplestore */
		ExecutorRun(queryDesc, ForwardScanDirection, 0L);

		(*queryDesc->dest->rDestroy) (queryDesc->dest);
		queryDesc->dest = NULL;

		/*
		 * Now shut down the inner executor.
		 */
Bruce Momjian's avatar
Bruce Momjian committed
400
		portal->queryDesc = NULL;		/* prevent double shutdown */
401
		/* we do not need AfterTriggerEndQuery() here */
402
		ExecutorEnd(queryDesc);
403 404

		/*
405
		 * Set the position in the result set: ideally, this could be
406 407 408
		 * implemented by just skipping straight to the tuple # that we need
		 * to be at, but the tuplestore API doesn't support that. So we start
		 * at the beginning of the tuplestore and iterate through it until we
409 410 411
		 * reach where we need to be.  FIXME someday?  (Fortunately, the
		 * typical case is that we're supposed to be at or near the start
		 * of the result set, so this isn't as bad as it sounds.)
412 413 414
		 */
		MemoryContextSwitchTo(portal->holdContext);

415 416 417 418 419 420 421
		if (portal->atEnd)
		{
			/* we can handle this case even if posOverflow */
			while (tuplestore_advance(portal->holdStore, true))
				/* continue */ ;
		}
		else
422 423
		{
			long		store_pos;
424

425 426
			if (portal->posOverflow)	/* oops, cannot trust portalPos */
				ereport(ERROR,
427 428
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("could not reposition held cursor")));
429

430
			tuplestore_rescan(portal->holdStore);
431

432 433
			for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
			{
434
				if (!tuplestore_advance(portal->holdStore, true))
435 436 437 438 439
					elog(ERROR, "unexpected end of tuple stream");
			}
		}
	}
	PG_CATCH();
440
	{
441 442
		/* Uncaught error while executing portal: mark it dead */
		portal->status = PORTAL_FAILED;
443

444 445
		/* Restore global vars and propagate error */
		ActivePortal = saveActivePortal;
446
		ActiveSnapshot = saveActiveSnapshot;
447 448
		CurrentResourceOwner = saveResourceOwner;
		PortalContext = savePortalContext;
449

450
		PG_RE_THROW();
451
	}
452
	PG_END_TRY();
453

454
	MemoryContextSwitchTo(oldcxt);
455

456 457 458 459
	/* Mark portal not active */
	portal->status = PORTAL_READY;

	ActivePortal = saveActivePortal;
460
	ActiveSnapshot = saveActiveSnapshot;
461 462
	CurrentResourceOwner = saveResourceOwner;
	PortalContext = savePortalContext;
463 464

	/*
465 466 467 468
	 * We can now release any subsidiary memory of the portal's heap context;
	 * we'll never use it again.  The executor already dropped its context,
	 * but this will clean up anything that glommed onto the portal's heap via
	 * PortalContext.
469 470
	 */
	MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
471
}