spi.c 19.8 KB
Newer Older
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
1 2 3
/*-------------------------------------------------------------------------
 *
 * spi.c--
4
 *				Server Programming Interface
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
5 6 7 8
 *
 *-------------------------------------------------------------------------
 */
#include "executor/spi.h"
9
#include "access/printtup.h"
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
10 11
#include "fmgr.h"

12 13
typedef struct
{
14 15 16 17 18 19
	QueryTreeList *qtlist;		/* malloced */
	uint32		processed;		/* by Executor */
	SPITupleTable *tuptable;
	Portal		portal;			/* portal per procedure */
	MemoryContext savedcxt;
	CommandId	savedId;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
20
}			_SPI_connection;
21 22

static Portal _SPI_portal = (Portal) NULL;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
23 24
static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = NULL;
25 26
static int	_SPI_connected = -1;
static int	_SPI_curid = -1;
27

28 29 30
uint32		SPI_processed = 0;
SPITupleTable *SPI_tuptable;
int			SPI_result;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
31

32
void		spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
33

34 35
typedef struct
{
36 37 38 39
	QueryTreeList *qtlist;
	List	   *ptlist;
	int			nargs;
	Oid		   *argtypes;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
40
}			_SPI_plan;
41

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
42 43
static int	_SPI_execute(char *src, int tcount, _SPI_plan * plan);
static int	_SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
44

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
45
#if 0
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
46
static void _SPI_fetch(FetchStmt * stmt);
47

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
48
#endif
49
static int
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
50
_SPI_execute_plan(_SPI_plan * plan,
51
				  char **Values, char *Nulls, int tcount);
52

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
53 54 55 56 57
#define _SPI_CPLAN_CURCXT	0
#define _SPI_CPLAN_PROCXT	1
#define _SPI_CPLAN_TOPCXT	2

static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, int location);
58

59 60
static int	_SPI_begin_call(bool execmem);
static int	_SPI_end_call(bool procmem);
61 62
static MemoryContext _SPI_execmem(void);
static MemoryContext _SPI_procmem(void);
63
static bool _SPI_checktuples(bool isRetrieveIntoRelation);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
64

65
#ifdef SPI_EXECUTOR_STATS
66 67 68
extern int	ShowExecutorStats;
extern void ResetUsage(void);
extern void ShowUsage(void);
69

70
#endif
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
71 72

int
73
SPI_connect()
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
74
{
75
	char		pname[64];
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	PortalVariableMemory pvmem;

	/*
	 * It's possible on startup and after commit/abort. In future we'll
	 * catch commit/abort in some way...
	 */
	strcpy(pname, "<SPI manager>");
	_SPI_portal = GetPortalByName(pname);
	if (!PortalIsValid(_SPI_portal))
	{
		if (_SPI_stack != NULL) /* there was abort */
			free(_SPI_stack);
		_SPI_current = _SPI_stack = NULL;
		_SPI_connected = _SPI_curid = -1;
		SPI_processed = 0;
		SPI_tuptable = NULL;
		_SPI_portal = CreatePortal(pname);
		if (!PortalIsValid(_SPI_portal))
			elog(FATAL, "SPI_connect: global initialization failed");
	}

	/*
	 * When procedure called by Executor _SPI_curid expected to be equal
	 * to _SPI_connected
	 */
	if (_SPI_curid != _SPI_connected)
		return (SPI_ERROR_CONNECT);

	if (_SPI_stack == NULL)
	{
		if (_SPI_connected != -1)
			elog(FATAL, "SPI_connect: no connection(s) expected");
		_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
	}
	else
	{
		if (_SPI_connected <= -1)
			elog(FATAL, "SPI_connect: some connection(s) expected");
		_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
115
						 (_SPI_connected + 2) * sizeof(_SPI_connection));
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	}

	/*
	 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
	 */
	_SPI_connected++;

	_SPI_current = &(_SPI_stack[_SPI_connected]);
	_SPI_current->qtlist = NULL;
	_SPI_current->processed = 0;
	_SPI_current->tuptable = NULL;

	/* Create Portal for this procedure ... */
	sprintf(pname, "<SPI %d>", _SPI_connected);
	_SPI_current->portal = CreatePortal(pname);
	if (!PortalIsValid(_SPI_current->portal))
		elog(FATAL, "SPI_connect: initialization failed");

	/* ... and switch to Portal' Variable memory - procedure' context */
	pvmem = PortalGetVariableMemory(_SPI_current->portal);
	_SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem);

	_SPI_current->savedId = GetScanCommandId();
	SetScanCommandId(GetCurrentCommandId());

	return (SPI_OK_CONNECT);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
143 144 145
}

int
146
SPI_finish()
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
147
{
148
	int			res;
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

	res = _SPI_begin_call(false);		/* live in procedure memory */
	if (res < 0)
		return (res);

	/* Restore memory context as it was before procedure call */
	MemoryContextSwitchTo(_SPI_current->savedcxt);
	PortalDestroy(&(_SPI_current->portal));

	SetScanCommandId(_SPI_current->savedId);

	/*
	 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
	 * closing connection to SPI and returning to upper Executor and so
	 * _SPI_connected must be equal to _SPI_curid.
	 */
	_SPI_connected--;
	_SPI_curid--;
	if (_SPI_connected == -1)
	{
		free(_SPI_stack);
		_SPI_stack = NULL;
	}
	else
	{
		_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
						 (_SPI_connected + 1) * sizeof(_SPI_connection));
		_SPI_current = &(_SPI_stack[_SPI_connected]);
	}

	return (SPI_OK_FINISH);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
181 182 183
}

int
184
SPI_exec(char *src, int tcount)
185
{
186
	int			res;
187 188 189 190 191 192 193 194 195 196 197 198

	if (src == NULL || tcount < 0)
		return (SPI_ERROR_ARGUMENT);

	res = _SPI_begin_call(true);
	if (res < 0)
		return (res);

	res = _SPI_execute(src, tcount, NULL);

	_SPI_end_call(true);
	return (res);
199 200
}

201 202
int
SPI_execp(void *plan, char **Values, char *Nulls, int tcount)
203
{
204
	int			res;
205 206 207 208

	if (plan == NULL || tcount < 0)
		return (SPI_ERROR_ARGUMENT);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
209
	if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
210 211 212 213 214 215
		return (SPI_ERROR_PARAM);

	res = _SPI_begin_call(true);
	if (res < 0)
		return (res);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
216 217 218
	/* copy plan to current (executor) context */
	plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT);

219 220 221 222
	res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);

	_SPI_end_call(true);
	return (res);
223 224
}

225
void	   *
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
226
SPI_prepare(char *src, int nargs, Oid * argtypes)
227
{
228
	_SPI_plan  *plan;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

	if (nargs < 0 || (nargs > 0 && argtypes == NULL))
	{
		SPI_result = SPI_ERROR_ARGUMENT;
		return (NULL);
	}

	SPI_result = _SPI_begin_call(true);
	if (SPI_result < 0)
		return (NULL);

	plan = (_SPI_plan *) palloc(sizeof(_SPI_plan));		/* Executor context */
	plan->argtypes = argtypes;
	plan->nargs = nargs;

	SPI_result = _SPI_execute(src, 0, plan);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
246 247
	if (SPI_result >= 0)		/* copy plan to procedure context */
		plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
248 249 250 251 252 253 254
	else
		plan = NULL;

	_SPI_end_call(true);

	return ((void *) plan);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
255 256
}

257
void	   *
258
SPI_saveplan(void *plan)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
259
{
260
	_SPI_plan  *newplan;
261 262 263 264 265 266 267 268 269 270 271

	if (plan == NULL)
	{
		SPI_result = SPI_ERROR_ARGUMENT;
		return (NULL);
	}

	SPI_result = _SPI_begin_call(false);		/* don't change context */
	if (SPI_result < 0)
		return (NULL);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
272
	newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
273 274 275 276 277 278

	_SPI_curid--;
	SPI_result = 0;

	return ((void *) newplan);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
279 280 281
}

int
282
SPI_fnumber(TupleDesc tupdesc, char *fname)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
283
{
284
	int			res;
285 286 287 288 289 290

	if (_SPI_curid + 1 != _SPI_connected)
		return (SPI_ERROR_UNCONNECTED);

	for (res = 0; res < tupdesc->natts; res++)
	{
291
		if (strcasecmp(tupdesc->attrs[res]->attname.data, fname) == 0)
292 293 294 295
			return (res + 1);
	}

	return (SPI_ERROR_NOATTRIBUTE);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
296 297
}

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
char	   *
SPI_fname(TupleDesc tupdesc, int fnumber)
{

	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (NULL);
	}

	if (tupdesc->natts < fnumber || fnumber <= 0)
	{
		SPI_result = SPI_ERROR_NOATTRIBUTE;
		return (NULL);
	}

	return (nameout(&(tupdesc->attrs[fnumber - 1]->attname)));

}

319
char	   *
320
SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
321
{
322 323 324
	char	   *val;
	bool		isnull;
	Oid			foutoid;
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (NULL);
	}

	if (tuple->t_natts < fnumber || fnumber <= 0)
		return (NULL);

	val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, &isnull);
	if (isnull)
		return (NULL);
	foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid);
	if (!OidIsValid(foutoid))
	{
		SPI_result = SPI_ERROR_NOOUTFUNC;
		return (NULL);
	}

	return (fmgr(foutoid, val, gettypelem(tupdesc->attrs[fnumber - 1]->atttypid)));
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
347 348
}

349
char	   *
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
350
SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
351
{
352
	char	   *val;
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

	*isnull = true;
	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (NULL);
	}

	if (tuple->t_natts < fnumber || fnumber <= 0)
		return (NULL);

	val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, isnull);

	return (val);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
368 369
}

370
char	   *
371
SPI_gettype(TupleDesc tupdesc, int fnumber)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
372
{
373
	HeapTuple	typeTuple;
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398

	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (NULL);
	}

	if (tupdesc->natts < fnumber || fnumber <= 0)
	{
		SPI_result = SPI_ERROR_NOATTRIBUTE;
		return (NULL);
	}

	typeTuple = SearchSysCacheTuple(TYPOID,
				 ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid),
									0, 0, 0);

	if (!HeapTupleIsValid(typeTuple))
	{
		SPI_result = SPI_ERROR_TYPUNKNOWN;
		return (NULL);
	}

	return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data));
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
399 400 401
}

Oid
402
SPI_gettypeid(TupleDesc tupdesc, int fnumber)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
403
{
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (InvalidOid);
	}

	if (tupdesc->natts < fnumber || fnumber <= 0)
	{
		SPI_result = SPI_ERROR_NOATTRIBUTE;
		return (InvalidOid);
	}

	return (tupdesc->attrs[fnumber - 1]->atttypid);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
419 420
}

421
char	   *
422
SPI_getrelname(Relation rel)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
423
{
424 425 426 427 428 429 430 431 432

	SPI_result = 0;
	if (_SPI_curid + 1 != _SPI_connected)
	{
		SPI_result = SPI_ERROR_UNCONNECTED;
		return (NULL);
	}

	return (pstrdup(rel->rd_rel->relname.data));
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
433 434 435 436
}

/*
 * spi_printtup --
437 438
 *		store tuple retrieved by Executor into SPITupleTable
 *		of current SPI procedure
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
439 440 441
 *
 */
void
442
spi_printtup(HeapTuple tuple, TupleDesc tupdesc)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
443
{
444 445
	SPITupleTable *tuptable;
	MemoryContext oldcxt;
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

	/*
	 * When called by Executor _SPI_curid expected to be equal to
	 * _SPI_connected
	 */
	if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
		elog(FATAL, "SPI: improper call to spi_printtup");
	if (_SPI_current != &(_SPI_stack[_SPI_curid]))
		elog(FATAL, "SPI: stack corrupted in spi_printtup");

	oldcxt = _SPI_procmem();	/* switch to procedure memory context */

	tuptable = _SPI_current->tuptable;
	if (tuptable == NULL)
	{
		_SPI_current->tuptable = tuptable = (SPITupleTable *)
			palloc(sizeof(SPITupleTable));
		tuptable->alloced = tuptable->free = 128;
		tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
		tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
	}
	else if (tuptable->free == 0)
	{
		tuptable->free = 256;
		tuptable->alloced += tuptable->free;
		tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
								  tuptable->alloced * sizeof(HeapTuple));
	}

	tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
	(tuptable->free)--;

	MemoryContextSwitchTo(oldcxt);
	return;
480 481
}

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
482 483 484 485
/*
 * Static functions
 */

486
static int
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
487
_SPI_execute(char *src, int tcount, _SPI_plan * plan)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
488
{
489 490 491 492 493 494 495 496 497 498 499 500
	QueryTreeList *queryTree_list;
	List	   *planTree_list;
	List	   *ptlist;
	QueryDesc  *qdesc;
	Query	   *queryTree;
	Plan	   *planTree;
	EState	   *state;
	int			qlen;
	int			nargs = 0;
	Oid		   *argtypes = NULL;
	int			res;
	int			i;
501 502 503 504 505 506 507 508 509 510 511 512 513

	/* Increment CommandCounter to see changes made by now */
	CommandCounterIncrement();

	SPI_processed = 0;
	SPI_tuptable = NULL;
	_SPI_current->tuptable = NULL;
	_SPI_current->qtlist = NULL;

	if (plan)
	{
		nargs = plan->nargs;
		argtypes = plan->argtypes;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
514
	}
515 516 517 518 519 520 521
	ptlist = planTree_list = (List *)
		pg_plan(src, argtypes, nargs, &queryTree_list, None);

	_SPI_current->qtlist = queryTree_list;

	qlen = queryTree_list->len;
	for (i = 0;; i++)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
522
	{
523 524 525 526 527 528 529 530 531
		queryTree = (Query *) (queryTree_list->qtrees[i]);
		planTree = lfirst(planTree_list);

		planTree_list = lnext(planTree_list);

		if (queryTree->commandType == CMD_UTILITY)
		{
			if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
			{
532
				CopyStmt   *stmt = (CopyStmt *) (queryTree->utilityStmt);
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

				if (stmt->filename == NULL)
					return (SPI_ERROR_COPY);
			}
			else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
					 nodeTag(queryTree->utilityStmt) == T_FetchStmt)
				return (SPI_ERROR_CURSOR);
			else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
				return (SPI_ERROR_TRANSACTION);
			res = SPI_OK_UTILITY;
			if (plan == NULL)
			{
				ProcessUtility(queryTree->utilityStmt, None);
				if (i < qlen - 1)
					CommandCounterIncrement();
				else
					return (res);
			}
			else if (i >= qlen - 1)
				break;
		}
		else if (plan == NULL)
		{
			qdesc = CreateQueryDesc(queryTree, planTree,
									(i < qlen - 1) ? None : SPI);
			state = CreateExecutorState();
			res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
			if (res < 0 || i >= qlen - 1)
				return (res);
			CommandCounterIncrement();
		}
		else
		{
			qdesc = CreateQueryDesc(queryTree, planTree,
									(i < qlen - 1) ? None : SPI);
			res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount);
			if (res < 0)
				return (res);
			if (i >= qlen - 1)
				break;
		}
	}

	plan->qtlist = queryTree_list;
	plan->ptlist = ptlist;

	return (res);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
581 582 583
}

static int
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
584
_SPI_execute_plan(_SPI_plan * plan, char **Values, char *Nulls, int tcount)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
585
{
586 587 588 589 590 591 592 593 594 595 596
	QueryTreeList *queryTree_list = plan->qtlist;
	List	   *planTree_list = plan->ptlist;
	QueryDesc  *qdesc;
	Query	   *queryTree;
	Plan	   *planTree;
	EState	   *state;
	int			nargs = plan->nargs;
	int			qlen = queryTree_list->len;
	int			res;
	int			i,
				k;
597 598 599 600 601 602 603 604 605 606

	/* Increment CommandCounter to see changes made by now */
	CommandCounterIncrement();

	SPI_processed = 0;
	SPI_tuptable = NULL;
	_SPI_current->tuptable = NULL;
	_SPI_current->qtlist = NULL;

	for (i = 0;; i++)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
607
	{
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
		queryTree = (Query *) (queryTree_list->qtrees[i]);
		planTree = lfirst(planTree_list);

		planTree_list = lnext(planTree_list);

		if (queryTree->commandType == CMD_UTILITY)
		{
			ProcessUtility(queryTree->utilityStmt, None);
			if (i < qlen - 1)
				CommandCounterIncrement();
			else
				return (SPI_OK_UTILITY);
		}
		else
		{
			qdesc = CreateQueryDesc(queryTree, planTree,
									(i < qlen - 1) ? None : SPI);
			state = CreateExecutorState();
			if (nargs > 0)
			{
628
				ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) *
629 630 631 632 633 634 635
											  sizeof(ParamListInfoData));

				state->es_param_list_info = paramLI;
				for (k = 0; k < plan->nargs; paramLI++, k++)
				{
					paramLI->kind = PARAM_NUM;
					paramLI->id = k + 1;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
636
					paramLI->isnull = (Nulls != NULL && Nulls[k] != 'n');
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
					paramLI->value = (Datum) Values[k];
				}
				paramLI->kind = PARAM_INVALID;
			}
			else
				state->es_param_list_info = NULL;
			res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
			if (res < 0 || i >= qlen - 1)
				return (res);
			CommandCounterIncrement();
		}
	}

	return (res);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
652 653 654
}

static int
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
655
_SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
656
{
657 658 659 660 661 662 663 664
	Query	   *parseTree;
	Plan	   *plan;
	int			operation;
	TupleDesc	tupdesc;
	bool		isRetrieveIntoPortal = false;
	bool		isRetrieveIntoRelation = false;
	char	   *intoName = NULL;
	int			res;
665 666 667 668 669 670 671

	parseTree = queryDesc->parsetree;
	plan = queryDesc->plantree;
	operation = queryDesc->operation;

	switch (operation)
	{
672 673 674 675 676 677 678
		case CMD_SELECT:
			res = SPI_OK_SELECT;
			if (parseTree->isPortal)
			{
				isRetrieveIntoPortal = true;
				intoName = parseTree->into;
				parseTree->isBinary = false;	/* */
679

680
				return (SPI_ERROR_CURSOR);
681

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
			}
			else if (parseTree->into != NULL)	/* select into table */
			{
				res = SPI_OK_SELINTO;
				isRetrieveIntoRelation = true;
			}
			break;
		case CMD_INSERT:
			res = SPI_OK_INSERT;
			break;
		case CMD_DELETE:
			res = SPI_OK_DELETE;
			break;
		case CMD_UPDATE:
			res = SPI_OK_UPDATE;
			break;
		default:
			return (SPI_ERROR_OPUNKNOWN);
700 701 702 703
	}

	if (state == NULL)			/* plan preparation */
		return (res);
704
#ifdef SPI_EXECUTOR_STATS
705 706
	if (ShowExecutorStats)
		ResetUsage();
707
#endif
708 709 710 711 712 713 714 715 716 717 718 719 720 721
	tupdesc = ExecutorStart(queryDesc, state);

	/* Don't work currently */
	if (isRetrieveIntoPortal)
	{
		ProcessPortal(intoName,
					  parseTree,
					  plan,
					  state,
					  tupdesc,
					  None);
		return (SPI_OK_CURSOR);
	}

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
722
	ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
723 724 725 726 727 728 729 730 731 732

	_SPI_current->processed = state->es_processed;
	if (operation == CMD_SELECT && queryDesc->dest == SPI)
	{
		if (_SPI_checktuples(isRetrieveIntoRelation))
			elog(FATAL, "SPI_select: # of processed tuples check failed");
	}

	ExecutorEnd(queryDesc, state);

733
#ifdef SPI_EXECUTOR_STATS
734 735 736 737 738
	if (ShowExecutorStats)
	{
		fprintf(stderr, "! Executor Stats:\n");
		ShowUsage();
	}
739
#endif
740 741 742 743 744 745 746 747

	if (queryDesc->dest == SPI)
	{
		SPI_processed = _SPI_current->processed;
		SPI_tuptable = _SPI_current->tuptable;
	}

	return (res);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
748 749 750 751 752

}

#if 0
static void
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
753
_SPI_fetch(FetchStmt * stmt)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
754
{
755 756 757 758 759 760 761
	char	   *name = stmt->portalname;
	int			feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK;
	int			count = stmt->howMany;
	Portal		portal;
	QueryDesc  *queryDesc;
	EState	   *state;
	MemoryContext context;
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786

	if (name == NULL)
		elog(FATAL, "SPI_fetch from blank portal unsupported");

	portal = GetPortalByName(name);
	if (!PortalIsValid(portal))
		elog(FATAL, "SPI_fetch: portal \"%s\" not found", name);

	context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));

	queryDesc = PortalGetQueryDesc(portal);
	state = PortalGetState(portal);

	ExecutorRun(queryDesc, state, feature, count);

	MemoryContextSwitchTo(context);		/* switch to the normal Executor
										 * context */

	_SPI_current->processed = state->es_processed;
	if (_SPI_checktuples(false))
		elog(FATAL, "SPI_fetch: # of processed tuples check failed");

	SPI_processed = _SPI_current->processed;
	SPI_tuptable = _SPI_current->tuptable;

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
787
}
788

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
789 790
#endif

791
static MemoryContext
792
_SPI_execmem()
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
793
{
794
	MemoryContext oldcxt;
795 796 797 798 799 800 801
	PortalHeapMemory phmem;

	phmem = PortalGetHeapMemory(_SPI_current->portal);
	oldcxt = MemoryContextSwitchTo((MemoryContext) phmem);

	return (oldcxt);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
802 803
}

804
static MemoryContext
805
_SPI_procmem()
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
806
{
807
	MemoryContext oldcxt;
808 809 810 811 812 813 814
	PortalVariableMemory pvmem;

	pvmem = PortalGetVariableMemory(_SPI_current->portal);
	oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem);

	return (oldcxt);

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
815 816 817 818 819 820 821
}

/*
 * _SPI_begin_call --
 *
 */
static int
822
_SPI_begin_call(bool execmem)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
823
{
824 825 826 827 828 829 830 831 832 833 834 835 836
	if (_SPI_curid + 1 != _SPI_connected)
		return (SPI_ERROR_UNCONNECTED);
	_SPI_curid++;
	if (_SPI_current != &(_SPI_stack[_SPI_curid]))
		elog(FATAL, "SPI: stack corrupted");

	if (execmem)				/* switch to the Executor memory context */
	{
		_SPI_execmem();
		StartPortalAllocMode(DefaultAllocMode, 0);
	}

	return (0);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
837 838 839
}

static int
840
_SPI_end_call(bool procmem)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
841
{
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

	/*
	 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
	 */
	_SPI_curid--;

	if (_SPI_current->qtlist)	/* free _SPI_plan allocations */
	{
		free(_SPI_current->qtlist->qtrees);
		free(_SPI_current->qtlist);
		_SPI_current->qtlist = NULL;
	}

	if (procmem)				/* switch to the procedure memory context */
	{							/* but free Executor memory before */
		EndPortalAllocMode();
		_SPI_procmem();
	}

	return (0);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
862 863
}

864
static bool
865
_SPI_checktuples(bool isRetrieveIntoRelation)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
866
{
867 868 869
	uint32		processed = _SPI_current->processed;
	SPITupleTable *tuptable = _SPI_current->tuptable;
	bool		failed = false;
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890

	if (processed == 0)
	{
		if (tuptable != NULL)
			failed = true;
	}
	else
/* some tuples were processed */
	{
		if (tuptable == NULL)	/* spi_printtup was not called */
		{
			if (!isRetrieveIntoRelation)
				failed = true;
		}
		else if (isRetrieveIntoRelation)
			failed = true;
		else if (processed != (tuptable->alloced - tuptable->free))
			failed = true;
	}

	return (failed);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
891
}
892

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
893
static _SPI_plan *
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
894
_SPI_copy_plan(_SPI_plan * plan, int location)
895
{
896
	_SPI_plan  *newplan;
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
897
	MemoryContext oldcxt = NULL;
898
	int			i;
899

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
900
	if (location == _SPI_CPLAN_PROCXT)
901 902
		oldcxt = MemoryContextSwitchTo((MemoryContext)
						  PortalGetVariableMemory(_SPI_current->portal));
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
903
	else if (location == _SPI_CPLAN_TOPCXT)
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
		oldcxt = MemoryContextSwitchTo(TopMemoryContext);

	newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
	newplan->qtlist = (QueryTreeList *) palloc(sizeof(QueryTreeList));
	newplan->qtlist->len = plan->qtlist->len;
	newplan->qtlist->qtrees = (Query **) palloc(plan->qtlist->len *
												sizeof(Query *));
	for (i = 0; i < plan->qtlist->len; i++)
		newplan->qtlist->qtrees[i] = (Query *)
			copyObject(plan->qtlist->qtrees[i]);

	newplan->ptlist = (List *) copyObject(plan->ptlist);
	newplan->nargs = plan->nargs;
	if (plan->nargs > 0)
	{
		newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
		memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
	}
	else
		newplan->argtypes = NULL;

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
925 926
	if (location != _SPI_CPLAN_CURCXT)
		MemoryContextSwitchTo(oldcxt);
927 928

	return (newplan);
929
}