📄 spi.c
字号:
for (res = 0; res < tupdesc->natts; res++) { if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0) return res + 1; } sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); if (sysatt != NULL) return sysatt->attnum; /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */ return SPI_ERROR_NOATTRIBUTE;}char *SPI_fname(TupleDesc tupdesc, int fnumber){ Form_pg_attribute att; SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } if (fnumber > 0) att = tupdesc->attrs[fnumber - 1]; else att = SystemAttributeDefinition(fnumber, true); return pstrdup(NameStr(att->attname));}char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber){ Datum origval, val, result; bool isnull; Oid typoid, foutoid; bool typisvarlena; SPI_result = 0; if (fnumber > tuple->t_data->t_natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); if (isnull) return NULL; if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; getTypeOutputInfo(typoid, &foutoid, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; result = OidFunctionCall1(foutoid, val); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); return DatumGetCString(result);}DatumSPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull){ SPI_result = 0; if (fnumber > tuple->t_data->t_natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; *isnull = true; return (Datum) NULL; } return heap_getattr(tuple, fnumber, tupdesc, isnull);}char *SPI_gettype(TupleDesc tupdesc, int fnumber){ Oid typoid; HeapTuple typeTuple; char *result; SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typoid), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) { SPI_result = SPI_ERROR_TYPUNKNOWN; return NULL; } result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname)); ReleaseSysCache(typeTuple); return result;}OidSPI_gettypeid(TupleDesc tupdesc, int fnumber){ SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return InvalidOid; } if (fnumber > 0) return tupdesc->attrs[fnumber - 1]->atttypid; else return (SystemAttributeDefinition(fnumber, true))->atttypid;}char *SPI_getrelname(Relation rel){ return pstrdup(RelationGetRelationName(rel));}char *SPI_getnspname(Relation rel){ return get_namespace_name(RelationGetNamespace(rel));}void *SPI_palloc(Size size){ MemoryContext oldcxt = NULL; void *pointer; if (_SPI_curid + 1 == _SPI_connected) /* connected */ { if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) elog(ERROR, "SPI stack corrupted"); oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); } pointer = palloc(size); if (oldcxt) MemoryContextSwitchTo(oldcxt); return pointer;}void *SPI_repalloc(void *pointer, Size size){ /* No longer need to worry which context chunk was in... */ return repalloc(pointer, size);}voidSPI_pfree(void *pointer){ /* No longer need to worry which context chunk was in... */ pfree(pointer);}voidSPI_freetuple(HeapTuple tuple){ /* No longer need to worry which context tuple was in... */ heap_freetuple(tuple);}voidSPI_freetuptable(SPITupleTable *tuptable){ if (tuptable != NULL) MemoryContextDelete(tuptable->tuptabcxt);}/* * SPI_cursor_open() * * Open a prepared SPI plan as a portal */PortalSPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls, bool read_only){ _SPI_plan *spiplan = (_SPI_plan *) plan; List *qtlist = spiplan->qtlist; List *ptlist = spiplan->ptlist; Query *queryTree; Plan *planTree; ParamListInfo paramLI; Snapshot snapshot; MemoryContext oldcontext; Portal portal; int k; /* Ensure that the plan contains only one query */ if (list_length(ptlist) != 1 || list_length(qtlist) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open multi-query plan as cursor"))); queryTree = (Query *) linitial((List *) linitial(qtlist)); planTree = (Plan *) linitial(ptlist); /* Must be a query that returns tuples */ switch (queryTree->commandType) { case CMD_SELECT: if (queryTree->into != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open SELECT INTO query as cursor"))); break; case CMD_UTILITY: if (!UtilityReturnsTuples(queryTree->utilityStmt)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; } /* Reset SPI result (note we deliberately don't touch lastoid) */ SPI_processed = 0; SPI_tuptable = NULL; _SPI_current->processed = 0; _SPI_current->tuptable = NULL; /* Create the portal */ if (name == NULL || name[0] == '\0') { /* Use a random nonconflicting name */ portal = CreateNewPortal(); } else { /* In this path, error if portal of same name already exists */ portal = CreatePortal(name, false, false); } /* Switch to portals memory and copy the parsetree and plan to there */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); queryTree = copyObject(queryTree); planTree = copyObject(planTree); /* If the plan has parameters, set them up */ if (spiplan->nargs > 0) { paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) * sizeof(ParamListInfoData)); for (k = 0; k < spiplan->nargs; k++) { paramLI[k].kind = PARAM_NUM; paramLI[k].id = k + 1; paramLI[k].ptype = spiplan->argtypes[k]; paramLI[k].isnull = (Nulls && Nulls[k] == 'n'); if (paramLI[k].isnull) { /* nulls just copy */ paramLI[k].value = Values[k]; } else { /* pass-by-ref values must be copied into portal context */ int16 paramTypLen; bool paramTypByVal; get_typlenbyval(spiplan->argtypes[k], ¶mTypLen, ¶mTypByVal); paramLI[k].value = datumCopy(Values[k], paramTypByVal, paramTypLen); } } paramLI[k].kind = PARAM_INVALID; } else paramLI = NULL; /* * Set up the portal. */ PortalDefineQuery(portal, NULL, /* unfortunately don't have sourceText */ "SELECT", /* nor the raw parse tree... */ list_make1(queryTree), list_make1(planTree), PortalGetHeapMemory(portal)); MemoryContextSwitchTo(oldcontext); /* * Set up options for portal. */ portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL); if (planTree == NULL || ExecSupportsBackwardScan(planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; /* * Set up the snapshot to use. (PortalStart will do CopySnapshot, so we * skip that here.) */ if (read_only) snapshot = ActiveSnapshot; else { CommandCounterIncrement(); snapshot = GetTransactionSnapshot(); } /* * Start portal execution. */ PortalStart(portal, paramLI, snapshot); Assert(portal->strategy == PORTAL_ONE_SELECT || portal->strategy == PORTAL_UTIL_SELECT); /* Return the created portal */ return portal;}/* * SPI_cursor_find() * * Find the portal of an existing open cursor */PortalSPI_cursor_find(const char *name){ return GetPortalByName(name);}/* * SPI_cursor_fetch() * * Fetch rows in a cursor */voidSPI_cursor_fetch(Portal portal, bool forward, long count){ _SPI_cursor_operation(portal, forward, count, CreateDestReceiver(DestSPI, NULL)); /* we know that the DestSPI receiver doesn't need a destroy call */}/* * SPI_cursor_move() * * Move in a cursor */voidSPI_cursor_move(Portal portal, bool forward, long count){ _SPI_cursor_operation(portal, forward, count, None_Receiver);}/* * SPI_cursor_close() * * Close a cursor */voidSPI_cursor_close(Portal portal){ if (!PortalIsValid(portal)) elog(ERROR, "invalid portal in SPI cursor operation"); PortalDrop(portal, false);}/* * Returns the Oid representing the type id for argument at argIndex. First * parameter is at index zero. */OidSPI_getargtypeid(void *plan, int argIndex){ if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan *) plan)->nargs) { SPI_result = SPI_ERROR_ARGUMENT; return InvalidOid; } return ((_SPI_plan *) plan)->argtypes[argIndex];}/* * Returns the number of arguments for the prepared plan. */intSPI_getargcount(void *plan){ if (plan == NULL) { SPI_result = SPI_ERROR_ARGUMENT; return -1; } return ((_SPI_plan *) plan)->nargs;}/* * Returns true if the plan contains exactly one command * and that command originates from normal SELECT (i.e. * *not* a SELECT ... INTO). In essence, the result indicates * if the command can be used with SPI_cursor_open * * Parameters * plan A plan previously prepared using SPI_prepare */boolSPI_is_cursor_plan(void *plan){ _SPI_plan *spiplan = (_SPI_plan *) plan; List *qtlist; if (spiplan == NULL) { SPI_result = SPI_ERROR_ARGUMENT; return false; } qtlist = spiplan->qtlist; if (list_length(spiplan->ptlist) == 1 && list_length(qtlist) == 1) { Query *queryTree = (Query *) linitial((List *) linitial(qtlist)); if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL) return true; } return false;}/* * SPI_result_code_string --- convert any SPI return code to a string * * This is often useful in error messages. Most callers will probably * only pass negative (error-case) codes, but for generality we recognize * the success codes too. */const char *SPI_result_code_string(int code){ static char buf[64]; switch (code) { case SPI_ERROR_CONNECT: return "SPI_ERROR_CONNECT"; case SPI_ERROR_COPY: return "SPI_ERROR_COPY"; case SPI_ERROR_OPUNKNOWN: return "SPI_ERROR_OPUNKNOWN"; case SPI_ERROR_UNCONNECTED: return "SPI_ERROR_UNCONNECTED"; case SPI_ERROR_CURSOR: return "SPI_ERROR_CURSOR"; case SPI_ERROR_ARGUMENT: return "SPI_ERROR_ARGUMENT"; case SPI_ERROR_PARAM: return "SPI_ERROR_PARAM"; case SPI_ERROR_TRANSACTION: return "SPI_ERROR_TRANSACTION"; case SPI_ERROR_NOATTRIBUTE: return "SPI_ERROR_NOATTRIBUTE"; case SPI_ERROR_NOOUTFUNC: return "SPI_ERROR_NOOUTFUNC"; case SPI_ERROR_TYPUNKNOWN: return "SPI_ERROR_TYPUNKNOWN"; case SPI_OK_CONNECT: return "SPI_OK_CONNECT"; case SPI_OK_FINISH: return "SPI_OK_FINISH"; case SPI_OK_FETCH: return "SPI_OK_FETCH"; case SPI_OK_UTILITY: return "SPI_OK_UTILITY"; case SPI_OK_SELECT: return "SPI_OK_SELECT"; case SPI_OK_SELINTO: return "SPI_OK_SELINTO"; case SPI_OK_INSERT: return "SPI_OK_INSERT"; case SPI_OK_DELETE: return "SPI_OK_DELETE"; case SPI_OK_UPDATE: return "SPI_OK_UPDATE"; case SPI_OK_CURSOR: return "SPI_OK_CURSOR"; } /* Unrecognized code ... return something useful ... */ sprintf(buf, "Unrecognized SPI code %d", code); return buf;}/* =================== private functions =================== *//* * spi_dest_startup * Initialize to receive tuples from Executor into SPITupleTable * of current SPI procedure */voidspi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo){ SPITupleTable *tuptable; MemoryContext oldcxt; MemoryContext tuptabcxt; /* * When called by Executor _SPI_curid expected to be equal to * _SPI_connected */ if (_SPI_curid != _SPI_connected || _SPI_connected < 0) elog(ERROR, "improper call to spi_dest_startup"); if (_SPI_current != &(_SPI_stack[_SPI_curid])) elog(ERROR, "SPI stack corrupted"); if (_SPI_current->tuptable != NULL) elog(ERROR, "improper call to spi_dest_startup"); oldcxt = _SPI_procmem(); /* switch to procedure memory context */ tuptabcxt = AllocSetContextCreate(CurrentMemoryContext, "SPI TupTable", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(tuptabcxt); _SPI_current->tuptable = tuptable = (SPITupleTable *) palloc(sizeof(SPITupleTable)); tuptable->tuptabcxt = tuptabcxt; tuptable->alloced = tuptable->free = 128; tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); tuptable->tupdesc = CreateTupleDescCopy(typeinfo); MemoryContextSwitchTo(oldcxt);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -