static void init_pkey_plan(void) { Oid types[1] = { OIDOID }; pkey_plan = SPI_saveplan(SPI_prepare(pkey_sql, 1, types)); if (pkey_plan == NULL) elog(ERROR, "pgq_triggers: SPI_prepare() failed"); }
/* * Prepare utility plans and plan cache. */ static void init_cache(void) { static int init_done = 0; Oid types[1] = { TEXTOID }; HASHCTL ctl; int flags; int max_queues = 128; if (init_done) return; /* * Init plans. */ queue_plan = SPI_saveplan(SPI_prepare(QUEUE_SQL, 1, types)); if (queue_plan == NULL) elog(ERROR, "pgq_insert: SPI_prepare() failed"); /* * init insert plan cache. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(struct InsertCacheEntry); ctl.hash = oid_hash; flags = HASH_ELEM | HASH_FUNCTION; insert_cache = hash_create("pgq_insert_raw plans cache", max_queues, &ctl, flags); init_done = 1; }
/* * helper for queue insertion. * * does not support NULL arguments. */ void pgq_simple_insert(const char *queue_name, Datum ev_type, Datum ev_data, Datum ev_extra1, Datum ev_extra2, Datum ev_extra3, Datum ev_extra4) { Datum values[7]; char nulls[7]; static void *plan = NULL; int res; if (!plan) { const char *sql; Oid types[7] = { TEXTOID, TEXTOID, TEXTOID, TEXTOID, TEXTOID, TEXTOID, TEXTOID }; sql = "select pgq.insert_event($1, $2, $3, $4, $5, $6, $7)"; plan = SPI_saveplan(SPI_prepare(sql, 7, types)); if (plan == NULL) elog(ERROR, "logtriga: SPI_prepare() failed"); } values[0] = DirectFunctionCall1(textin, (Datum)queue_name); values[1] = ev_type; values[2] = ev_data; values[3] = ev_extra1; values[4] = ev_extra2; values[5] = ev_extra3; values[6] = ev_extra4; nulls[0] = ' '; nulls[1] = ev_type ? ' ' : 'n'; nulls[2] = ev_data ? ' ' : 'n'; nulls[3] = ev_extra1 ? ' ' : 'n'; nulls[4] = ev_extra2 ? ' ' : 'n'; nulls[5] = ev_extra3 ? ' ' : 'n'; nulls[6] = ev_extra4 ? ' ' : 'n'; res = SPI_execute_plan(plan, values, nulls, false, 0); if (res != SPI_OK_SELECT) elog(ERROR, "call of pgq.insert_event failed"); }
/* prepare plan */ static SPIPlanPtr reorg_prepare(const char *src, int nargs, Oid *argtypes) { SPIPlanPtr plan = SPI_prepare(src, nargs, argtypes); if (plan == NULL) elog(ERROR, "pg_reorg: reorg_prepare failed (code=%d, query=%s)", SPI_result, src); return plan; }
static PyObject * PLy_cursor_query(const char *query) { PLyCursorObject *cursor; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; cursor->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python cursor context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); PLy_typeinfo_init(&cursor->result, cursor->mcxt); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); SPIPlanPtr plan; Portal portal; pg_verifymbstr(query, strlen(query), false); plan = SPI_prepare(query, 0, NULL); if (plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); portal = SPI_cursor_open(NULL, plan, NULL, NULL, exec_ctx->curr_proc->fn_readonly); SPI_freeplan(plan); if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed: %s", SPI_result_code_string(SPI_result)); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(cursor->portalname != NULL); return (PyObject *) cursor; }
SPIPlanPtr pgr_SPI_prepare(char* sql) { PGR_DBG("Preparing Plan"); SPIPlanPtr SPIplan; SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(ERROR, "Couldn't create query plan via SPI: %s", sql); } return SPIplan; }
Datum dbms_alert_signal(PG_FUNCTION_ARGS) { void *plan; Oid argtypes[] = {TEXTOID, TEXTOID}; Datum values[2]; char nulls[2] = {' ',' '}; if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("event name is NULL"), errdetail("Eventname may not be NULL."))); if (PG_ARGISNULL(1)) nulls[1] = 'n'; values[0] = PG_GETARG_DATUM(0); values[1] = PG_GETARG_DATUM(1); if (SPI_connect() < 0) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_connect failed"))); SPI_EXEC("SELECT 1 FROM pg_catalog.pg_class c " "WHERE pg_catalog.pg_table_is_visible(c.oid) " "AND c.relkind='r' AND c.relname = 'ora_alerts'", SELECT); if (0 == SPI_processed) { SPI_EXEC("CREATE TEMP TABLE ora_alerts(event text, message text)", UTILITY); SPI_EXEC("REVOKE ALL ON TABLE ora_alerts FROM PUBLIC", UTILITY); SPI_EXEC("CREATE CONSTRAINT TRIGGER ora_alert_signal AFTER INSERT ON ora_alerts " "INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE dbms_alert.defered_signal()", UTILITY); } if (!(plan = SPI_prepare( "INSERT INTO ora_alerts(event,message) VALUES($1, $2)", 2, argtypes))) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_prepare failed"))); if (SPI_OK_INSERT != SPI_execute_plan(plan, values, nulls, false, 1)) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("can't execute sql"))); SPI_finish(); PG_RETURN_VOID(); }
/* * utl_file_dir security .. is solved with aux. table. * * Raise exception if don't find string in table. */ static void check_secure_locality(const char *path) { static SPIPlanPtr plan = NULL; Oid argtypes[] = {TEXTOID}; Datum values[1]; char nulls[1] = {' '}; /* hack for availbility regress test */ if (strcmp(path, "/tmp/regress_orafce") == 0) return; values[0] = CStringGetTextDatum(path); /* * SELECT 1 FROM utl_file.utl_file_dir * WHERE substring($1, 1, length(dir) + 1) = dir || '/' */ if (SPI_connect() < 0) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_connect failed"))); if (!plan) { /* Don't use LIKE not to escape '_' and '%' */ SPIPlanPtr p = SPI_prepare( "SELECT 1 FROM utl_file.utl_file_dir" " WHERE substring($1, 1, length(dir) + 1) = dir || '/'", 1, argtypes); if (p == NULL || (plan = SPI_saveplan(p)) == NULL) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_prepare_failed"))); } if (SPI_OK_SELECT != SPI_execute_plan(plan, values, nulls, false, 1)) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("can't execute sql"))); if (SPI_processed == 0) ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg(INVALID_PATH), errdetail("you cannot access locality"), errhint("locality is not found in utl_file_dir table"))); SPI_finish(); }
/* * spi_exec_query * Execute given SQL command via SPI. * The plan will be cached by SPI_prepare if it hasn't been. */ static void spi_exec_query(const char *query, int nargs, Oid *argtypes, SPIPlanPtr *plan, Datum *values, const char *nulls, int result) { int ret; if (*plan == NULL) *plan = SPI_prepare(query, nargs, argtypes); ret = SPI_execute_plan(*plan, values, nulls, false, 0); if (ret != result) elog(ERROR, "pg_dbms_stats: SPI_execute_plan => %d", ret); }
int storeKeyInfo(char *cpTableName, HeapTuple tTupleData, TupleDesc tTupleDesc, Oid tableOid) { Oid saPlanArgTypes[1] = {NAMEOID}; char *insQuery = "INSERT INTO dbmirror_pendingdata (SeqId,IsKey,Data) VALUES(currval('dbmirror_pending_seqid_seq'),'t',$1)"; void *pplan; Datum saPlanData[1]; char *cpKeyData; int iRetCode; pplan = SPI_prepare(insQuery, 1, saPlanArgTypes); if (pplan == NULL) { elog(NOTICE, "could not prepare INSERT plan"); return -1; } /* pplan = SPI_saveplan(pplan); */ cpKeyData = packageData(tTupleData, tTupleDesc, tableOid, PRIMARY); if (cpKeyData == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), /* cpTableName already contains quotes... */ errmsg("there is no PRIMARY KEY for table %s", cpTableName))); debug_msg2("dbmirror:storeKeyInfo key data: %s", cpKeyData); saPlanData[0] = PointerGetDatum(cpKeyData); iRetCode = SPI_execp(pplan, saPlanData, NULL, 1); if (cpKeyData != NULL) SPI_pfree(cpKeyData); if (iRetCode != SPI_OK_INSERT) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("error inserting row in pendingDelete"))); debug_msg("insert successful"); return 0; }
Oid name2id_cfg(text *name) { Oid arg[1]; bool isnull; Datum pars[1]; int stat; Oid id = findSNMap_t(&(CList.name2id_map), name); void *plan; char *nsp; char buf[1024]; arg[0] = TEXTOID; pars[0] = PointerGetDatum(name); if (id) return id; nsp = get_namespace(TSNSP_FunctionOid); SPI_connect(); sprintf(buf, "select oid from %s.pg_ts_cfg where ts_name = $1", nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) /* internal error */ elog(ERROR, "SPI_prepare() failed"); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) /* internal error */ elog(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) { id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); if (isnull) ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("null id for tsearch config"))); } else ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("no tsearch config"))); SPI_freeplan(plan); SPI_finish(); addSNMap_t(&(CList.name2id_map), name, id); return id; }
int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes, Datum *values, const char *nulls, bool read_only, long tcount) { SPIPlanPtr plan; int ret; plan = SPI_prepare(src, nargs, argtypes); if (plan == NULL) return SPI_result; ret = SPI_execute_plan(plan, values, nulls, read_only, tcount); SPI_freeplan(plan); return ret; }
void init_dict(Oid id, DictInfo * dict) { Oid arg[1]; bool isnull; Datum pars[1]; int stat; void *plan; char buf[1024]; char *nsp = get_namespace(TSNSP_FunctionOid); arg[0] = OIDOID; pars[0] = ObjectIdGetDatum(id); memset(dict, 0, sizeof(DictInfo)); SPI_connect(); sprintf(buf, "select dict_init, dict_initoption, dict_lexize from %s.pg_ts_dict where oid = $1", nsp); pfree(nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) ts_error(ERROR, "SPI_prepare() failed"); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) ts_error(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) { Datum opt; Oid oid = InvalidOid; oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); if (!(isnull || oid == InvalidOid)) { opt = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &isnull); dict->dictionary = (void *) DatumGetPointer(OidFunctionCall1(oid, opt)); } oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &isnull)); if (isnull || oid == InvalidOid) ts_error(ERROR, "Null dict_lexize for dictonary %d", id); fmgr_info_cxt(oid, &(dict->lexize_info), TopMemoryContext); dict->dict_id = id; } else ts_error(ERROR, "No dictionary with id %d", id); SPI_freeplan(plan); SPI_finish(); }
void init_prs(Oid id, WParserInfo * prs) { Oid arg[1]; bool isnull; Datum pars[1]; int stat; void *plan; char buf[1024], *nsp; arg[0] = OIDOID; pars[0] = ObjectIdGetDatum(id); memset(prs, 0, sizeof(WParserInfo)); SPI_connect(); nsp = get_namespace(TSNSP_FunctionOid); sprintf(buf, "select prs_start, prs_nexttoken, prs_end, prs_lextype, prs_headline from %s.pg_ts_parser where oid = $1", nsp); pfree(nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) ts_error(ERROR, "SPI_prepare() failed"); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) ts_error(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) { Oid oid = InvalidOid; oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); fmgr_info_cxt(oid, &(prs->start_info), TopMemoryContext); oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &isnull)); fmgr_info_cxt(oid, &(prs->getlexeme_info), TopMemoryContext); oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &isnull)); fmgr_info_cxt(oid, &(prs->end_info), TopMemoryContext); prs->lextype = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 4, &isnull)); oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5, &isnull)); fmgr_info_cxt(oid, &(prs->headline_info), TopMemoryContext); prs->prs_id = id; } else ts_error(ERROR, "No parser with id %d", id); SPI_freeplan(plan); SPI_finish(); }
/****************************************************************************** * Stores a copy of the non-key data for the row. *****************************************************************************/ int storeData(char *cpTableName, HeapTuple tTupleData, TupleDesc tTupleDesc, Oid tableOid, int iIncludeKeyData) { Oid planArgTypes[1] = {NAMEOID}; char *insQuery = "INSERT INTO dbmirror_pendingdata (SeqId,IsKey,Data) VALUES(currval('dbmirror_pending_seqid_seq'),'f',$1)"; void *pplan; Datum planData[1]; char *cpKeyData; int iRetValue; pplan = SPI_prepare(insQuery, 1, planArgTypes); if (pplan == NULL) { elog(NOTICE, "could not prepare INSERT plan"); return -1; } /* pplan = SPI_saveplan(pplan); */ if (iIncludeKeyData == 0) cpKeyData = packageData(tTupleData, tTupleDesc, tableOid, NONPRIMARY); else cpKeyData = packageData(tTupleData, tTupleDesc, tableOid, ALL); planData[0] = PointerGetDatum(cpKeyData); iRetValue = SPI_execp(pplan, planData, NULL, 1); if (cpKeyData != 0) SPI_pfree(cpKeyData); if (iRetValue != SPI_OK_INSERT) { elog(NOTICE, "error inserting row in pendingDelete"); return -1; } debug_msg("dbmirror:storeKeyData insert successful"); return 0; }
int get_currcfg(void) { Oid arg[1] = {TEXTOID}; const char *curlocale; Datum pars[1]; bool isnull; int stat; char buf[1024]; char *nsp; void *plan; if (current_cfg_id > 0) return current_cfg_id; nsp = get_namespace(TSNSP_FunctionOid); SPI_connect(); sprintf(buf, "select oid from %s.pg_ts_cfg where locale = $1 ", nsp); pfree(nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) /* internal error */ elog(ERROR, "SPI_prepare() failed"); curlocale = setlocale(LC_CTYPE, NULL); pars[0] = PointerGetDatum(char2text((char *) curlocale)); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) /* internal error */ elog(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) current_cfg_id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); else ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not find tsearch config by locale"))); pfree(DatumGetPointer(pars[0])); SPI_freeplan(plan); SPI_finish(); return current_cfg_id; }
static void process_edge_data(Datum callgraph_buffer_id) { int ret; HASH_SEQ_STATUS hst; EdgeHashElem *elem; SPIPlanPtr planptr; Datum args[7]; Oid argtypes[] = { INT8OID, OIDOID, OIDOID, OIDOID, INT8OID, FLOAT8OID, FLOAT8OID, InvalidOid }; /* Start by freezing the hash table. This saves us some trouble. */ hash_freeze(edge_hash_table); if ((ret = SPI_connect()) < 0) elog(ERROR, "could not connect to the SPI: %d", ret); planptr = SPI_prepare("INSERT INTO " " call_graph.CallGraphBuffer (CallGraphBufferID, TopLevelFunction, Caller, Callee, " " Calls, TotalTime, SelfTime) " " VALUES ($1, $2, $3, $4, $5, $6, $7) ", 7, argtypes); if (!planptr) elog(ERROR, "could not prepare an SPI plan for the INSERT into CallGraphBuffer"); args[0] = callgraph_buffer_id; args[1] = ObjectIdGetDatum(top_level_function_oid); hash_seq_init(&hst, edge_hash_table); while ((elem = hash_seq_search(&hst)) != NULL) { args[2] = ObjectIdGetDatum(elem->key.caller); args[3] = ObjectIdGetDatum(elem->key.callee); args[4] = Int8GetDatum(elem->num_calls); args[5] = Float8GetDatum(INSTR_TIME_GET_MILLISEC(elem->total_time)); args[6] = Float8GetDatum(INSTR_TIME_GET_MILLISEC(elem->self_time)); if ((ret = SPI_execp(planptr, args, NULL, 0)) < 0) elog(ERROR, "SPI_execp() failed: %d", ret); } SPI_finish(); }
static Oid GetRelOid(Oid filenode) { int ret; Oid relid; bool isnull; Datum value[1] = { ObjectIdGetDatum(filenode) }; static SPIPlanPtr plan = NULL; /* If this is our first time here, create a plan and save it for later calls. */ if (plan == NULL) { StringInfoData buf; Oid paramType[1] = {OIDOID}; initStringInfo(&buf); appendStringInfo(&buf, "select oid from pg_class where pg_relation_filenode(oid) = $1"); plan = SPI_prepare(buf.data, 1, (Oid*)¶mType); if (plan == NULL) elog(ERROR, "SPI_prepare returned %d", SPI_result); } ret = SPI_execute_plan(plan, (Datum*)&value, NULL, true, 1); if (ret != SPI_OK_SELECT) ereport(FATAL, (errmsg("SPI_execute_plan failed: error code %d", ret))); if (SPI_processed < 1) return InvalidOid; relid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); if (isnull) return InvalidOid; return relid; }
Oid name2id_prs(text *name) { Oid arg[1]; bool isnull; Datum pars[1]; int stat; Oid id = findSNMap_t(&(PList.name2id_map), name); char buf[1024], *nsp; void *plan; arg[0] = TEXTOID; pars[0] = PointerGetDatum(name); if (id) return id; SPI_connect(); nsp = get_namespace(TSNSP_FunctionOid); sprintf(buf, "select oid from %s.pg_ts_parser where prs_name = $1", nsp); pfree(nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) ts_error(ERROR, "SPI_prepare() failed"); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) ts_error(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); else ts_error(ERROR, "No parser '%s'", text2char(name)); SPI_freeplan(plan); SPI_finish(); addSNMap_t(&(PList.name2id_map), name, id); return id; }
/* * Create new plan for insertion into current queue table. */ static void *make_plan(struct QueueState *state) { void *plan; StringInfo sql; static Oid types[10] = { INT8OID, TIMESTAMPTZOID, INT4OID, INT4OID, TEXTOID, TEXTOID, TEXTOID, TEXTOID, TEXTOID, TEXTOID }; /* * create sql */ sql = makeStringInfo(); appendStringInfo(sql, "insert into %s_%d (ev_id, ev_time, ev_owner, ev_retry," " ev_type, ev_data, ev_extra1, ev_extra2, ev_extra3, ev_extra4)" " values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", state->table_prefix, state->cur_table); /* * create plan */ plan = SPI_prepare(sql->data, 10, types); return SPI_saveplan(plan); }
/* * Prepare ProxyQuery for local execution */ void plproxy_query_prepare(ProxyFunction *func, FunctionCallInfo fcinfo, ProxyQuery *q, bool split_support) { int i; Oid types[FUNC_MAX_ARGS]; void *plan; /* create sql statement in sql */ for (i = 0; i < q->arg_count; i++) { int idx = q->arg_lookup[i]; if (split_support && IS_SPLIT_ARG(func, idx)) /* for SPLIT arguments use array element type instead */ types[i] = func->arg_types[idx]->elem_type_oid; else types[i] = func->arg_types[idx]->type_oid; } /* prepare & store plan */ plan = SPI_prepare(q->sql, q->arg_count, types); q->plan = SPI_saveplan(plan); }
static int get_nnode(PlxFn *plx_fn, FunctionCallInfo fcinfo) { PlxQuery *plx_q = plx_fn->hash_query; int err; SPIPlanPtr plan; Oid types[FUNC_MAX_ARGS]; Datum values[FUNC_MAX_ARGS]; char arg_nulls[FUNC_MAX_ARGS]; Datum val; bool isnull; int i; if ((err = SPI_connect()) != SPI_OK_CONNECT) plx_error(plx_fn, "SPI_connect: %s", SPI_result_code_string(err)); for (i = 0; i < plx_q->nargs; i++) { int idx = plx_q->plx_fn_arg_indexes[i]; types[i] = plx_fn->arg_types[idx]->oid; values[i] = PG_GETARG_DATUM(idx); } plan = SPI_prepare(plx_q->sql->data, plx_q->nargs, types); err = SPI_execute_plan(plan, values, arg_nulls, true, 0); if (err != SPI_OK_SELECT) plx_error(plx_fn, "query '%s' failed: %s", plx_q->sql->data, SPI_result_code_string(err)); val = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull); err = SPI_finish(); if (err != SPI_OK_FINISH) plx_error(plx_fn, "SPI_finish: %s", SPI_result_code_string(err)); return DatumGetInt32(val); }
SV * plperl_spi_query(char *query) { SV *cursor; /* * Execute the query inside a sub-transaction, so we can cope with errors * sanely */ MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { void *plan; Portal portal = NULL; /* Create a cursor for the query */ plan = SPI_prepare(query, 0, NULL); if (plan) portal = SPI_cursor_open(NULL, plan, NULL, NULL, false); if (portal) cursor = newSVpv(portal->name, 0); else cursor = newSV(0); /* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * AtEOSubXact_SPI() should not have popped any SPI context, but just * in case it did, make sure we remain connected. */ SPI_restore_connection(); } PG_CATCH(); { ErrorData *edata; /* Save error info */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); /* Abort the inner transaction */ RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will * have left us in a disconnected state. We need this hack to return * to connected state. */ SPI_restore_connection(); /* Punt the error to Perl */ croak("%s", edata->message); /* Can't get here, but keep compiler quiet */ return NULL; } PG_END_TRY(); return cursor; }
Datum /* have to return HeapTuple to Executor */ timetravel(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; /* to get trigger name */ int argc; char **args; /* arguments */ int attnum[MaxAttrNum]; /* fnumbers of start/stop columns */ Datum oldtimeon, oldtimeoff; Datum newtimeon, newtimeoff, newuser, nulltext; Datum *cvals; /* column values */ char *cnulls; /* column nulls */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ HeapTuple trigtuple; HeapTuple newtuple = NULL; HeapTuple rettuple; TupleDesc tupdesc; /* tuple description */ int natts; /* # of attributes */ EPlan *plan; /* prepared plan */ char ident[2 * NAMEDATALEN]; bool isnull; /* to know is some column NULL or not */ bool isinsert = false; int ret; int i; /* * Some checks first... */ /* Called by trigger manager ? */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "timetravel: not fired by trigger manager"); /* Should be called for ROW trigger */ if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) elog(ERROR, "timetravel: can't process STATEMENT events"); /* Should be called BEFORE */ if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) elog(ERROR, "timetravel: must be fired before event"); /* INSERT ? */ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) isinsert = true; if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) newtuple = trigdata->tg_newtuple; trigtuple = trigdata->tg_trigtuple; rel = trigdata->tg_relation; relname = SPI_getrelname(rel); /* check if TT is OFF for this relation */ if (0 == findTTStatus(relname)) { /* OFF - nothing to do */ pfree(relname); return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple); } trigger = trigdata->tg_trigger; argc = trigger->tgnargs; if (argc != MinAttrNum && argc != MaxAttrNum) elog(ERROR, "timetravel (%s): invalid (!= %d or %d) number of arguments %d", relname, MinAttrNum, MaxAttrNum, trigger->tgnargs); args = trigger->tgargs; tupdesc = rel->rd_att; natts = tupdesc->natts; for (i = 0; i < MinAttrNum; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); if (attnum[i] < 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != ABSTIMEOID) elog(ERROR, "timetravel (%s): attribute %s must be of abstime type", relname, args[i]); } for (; i < argc; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); if (attnum[i] < 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != TEXTOID) elog(ERROR, "timetravel (%s): attribute %s must be of text type", relname, args[i]); } /* create fields containing name */ newuser = DirectFunctionCall1(textin, CStringGetDatum(GetUserNameFromId(GetUserId()))); nulltext = (Datum) NULL; if (isinsert) { /* INSERT */ int chnattrs = 0; int chattrs[MaxAttrNum]; Datum newvals[MaxAttrNum]; char newnulls[MaxAttrNum]; oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) { newvals[chnattrs] = GetCurrentAbsoluteTime(); newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_on]; chnattrs++; } oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) { if ((chnattrs == 0 && DatumGetInt32(oldtimeon) >= NOEND_ABSTIME) || (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) >= NOEND_ABSTIME)) elog(ERROR, "timetravel (%s): %s is infinity", relname, args[a_time_on]); newvals[chnattrs] = NOEND_ABSTIME; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_off]; chnattrs++; } else { if ((chnattrs == 0 && DatumGetInt32(oldtimeon) > DatumGetInt32(oldtimeoff)) || (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) > DatumGetInt32(oldtimeoff))) elog(ERROR, "timetravel (%s): %s gt %s", relname, args[a_time_on], args[a_time_off]); } pfree(relname); if (chnattrs <= 0) return PointerGetDatum(trigtuple); if (argc == MaxAttrNum) { /* clear update_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_upd_user]; chnattrs++; /* clear delete_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_del_user]; chnattrs++; /* set insert_user value */ newvals[chnattrs] = newuser; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_ins_user]; chnattrs++; } rettuple = SPI_modifytuple(rel, trigtuple, chnattrs, chattrs, newvals, newnulls); return PointerGetDatum(rettuple); /* end of INSERT */ } /* UPDATE/DELETE: */ oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]); oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]); /* * If DELETE/UPDATE of tuple with stop_date neq INFINITY then say * upper Executor to skip operation for this tuple */ if (newtuple != NULL) { /* UPDATE */ newtimeon = SPI_getbinval(newtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]); newtimeoff = SPI_getbinval(newtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]); if (oldtimeon != newtimeon || oldtimeoff != newtimeoff) elog(ERROR, "timetravel (%s): you can't change %s and/or %s columns (use set_timetravel)", relname, args[a_time_on], args[a_time_off]); } if (oldtimeoff != NOEND_ABSTIME) { /* current record is a deleted/updated * record */ pfree(relname); return PointerGetDatum(NULL); } newtimeoff = GetCurrentAbsoluteTime(); /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) elog(ERROR, "timetravel (%s): SPI_connect returned %d", relname, ret); /* Fetch tuple values and nulls */ cvals = (Datum *) palloc(natts * sizeof(Datum)); cnulls = (char *) palloc(natts * sizeof(char)); for (i = 0; i < natts; i++) { cvals[i] = SPI_getbinval(trigtuple, tupdesc, i + 1, &isnull); cnulls[i] = (isnull) ? 'n' : ' '; } /* change date column(s) */ cvals[attnum[a_time_off] - 1] = newtimeoff; /* stop_date eq current * date */ cnulls[attnum[a_time_off] - 1] = ' '; if (!newtuple) { /* DELETE */ if (argc == MaxAttrNum) { cvals[attnum[a_del_user] - 1] = newuser; /* set delete user */ cnulls[attnum[a_del_user] - 1] = ' '; } } /* * Construct ident string as TriggerName $ TriggeredRelationId and try * to find prepared execution plan. */ snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id); plan = find_plan(ident, &Plans, &nPlans); /* if there is no plan ... */ if (plan->splan == NULL) { void *pplan; Oid *ctypes; char sql[8192]; char separ = ' '; /* allocate ctypes for preparation */ ctypes = (Oid *) palloc(natts * sizeof(Oid)); /* * Construct query: INSERT INTO _relation_ VALUES ($1, ...) */ snprintf(sql, sizeof(sql), "INSERT INTO %s VALUES (", relname); for (i = 1; i <= natts; i++) { ctypes[i - 1] = SPI_gettypeid(tupdesc, i); if (!(tupdesc->attrs[i - 1]->attisdropped)) /* skip dropped columns */ { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%c$%d", separ, i); separ = ','; } } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ")"); elog(DEBUG4, "timetravel (%s) update: sql: %s", relname, sql); /* Prepare plan for query */ pplan = SPI_prepare(sql, natts, ctypes); if (pplan == NULL) elog(ERROR, "timetravel (%s): SPI_prepare returned %d", relname, SPI_result); /* * Remember that SPI_prepare places plan in current memory context * - so, we have to save plan in Top memory context for latter * use. */ pplan = SPI_saveplan(pplan); if (pplan == NULL) elog(ERROR, "timetravel (%s): SPI_saveplan returned %d", relname, SPI_result); plan->splan = pplan; } /* * Ok, execute prepared plan. */ ret = SPI_execp(plan->splan, cvals, cnulls, 0); if (ret < 0) elog(ERROR, "timetravel (%s): SPI_execp returned %d", relname, ret); /* Tuple to return to upper Executor ... */ if (newtuple) { /* UPDATE */ int chnattrs = 0; int chattrs[MaxAttrNum]; Datum newvals[MaxAttrNum]; char newnulls[MaxAttrNum]; newvals[chnattrs] = newtimeoff; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_on]; chnattrs++; newvals[chnattrs] = NOEND_ABSTIME; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_off]; chnattrs++; if (argc == MaxAttrNum) { /* set update_user value */ newvals[chnattrs] = newuser; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_upd_user]; chnattrs++; /* clear delete_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_del_user]; chnattrs++; /* set insert_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_ins_user]; chnattrs++; } rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls); /* * SPI_copytuple allocates tmptuple in upper executor context - * have to free allocation using SPI_pfree */ /* SPI_pfree(tmptuple); */ } else /* DELETE case */ rettuple = trigtuple; SPI_finish(); /* don't forget say Bye to SPI mgr */ pfree(relname); return PointerGetDatum(rettuple); }
static TSVectorStat * ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws) { char *query = text_to_cstring(txt); int i; TSVectorStat *stat; bool isnull; Portal portal; SPIPlanPtr plan; if ((plan = SPI_prepare(query, 0, NULL)) == NULL) /* internal error */ elog(ERROR, "SPI_prepare(\"%s\") failed", query); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL) /* internal error */ elog(ERROR, "SPI_cursor_open(\"%s\") failed", query); SPI_cursor_fetch(portal, true, 100); if (SPI_tuptable == NULL || SPI_tuptable->tupdesc->natts != 1 || !IsBinaryCoercible(SPI_gettypeid(SPI_tuptable->tupdesc, 1), TSVECTOROID)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ts_stat query must return one tsvector column"))); stat = MemoryContextAllocZero(persistentContext, sizeof(TSVectorStat)); stat->maxdepth = 1; if (ws) { char *buf; buf = VARDATA(ws); while (buf - VARDATA(ws) < VARSIZE(ws) - VARHDRSZ) { if (pg_mblen(buf) == 1) { switch (*buf) { case 'A': case 'a': stat->weight |= 1 << 3; break; case 'B': case 'b': stat->weight |= 1 << 2; break; case 'C': case 'c': stat->weight |= 1 << 1; break; case 'D': case 'd': stat->weight |= 1; break; default: stat->weight |= 0; } } buf += pg_mblen(buf); } } while (SPI_processed > 0) { for (i = 0; i < SPI_processed; i++) { Datum data = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); if (!isnull) stat = ts_accum(persistentContext, stat, data); } SPI_freetuptable(SPI_tuptable); SPI_cursor_fetch(portal, true, 100); } SPI_freetuptable(SPI_tuptable); SPI_cursor_close(portal); SPI_freeplan(plan); pfree(query); return stat; }
static int compute_sql_asm_tsp(char* sql, int sourceVertexId, bool reverseCost, tspPathElementType **path, int *pathCount) { int SPIcode; void *SPIplan; Portal SPIportal; bool moredata = TRUE; int ntuples; tspEdgeType *edges = NULL; int totalTuples = 0; DBG("Sql %s source %d reverse %s",sql,sourceVertexId,reverseCost==true?"true":"false"); tspEdgeType edgeColumns = {.id= -1, .source= -1, .target= -1, .cost= -1 }; char *errMesg; int ret = -1; errMesg=palloc(sizeof(char) * 300); DBG("start compute_sql_asm_tsp %i",*pathCount); SPIcode = SPI_connect(); if (SPIcode != SPI_OK_CONNECT) { elog(ERROR, "compute_sql_asm_tsp: couldn't open a connection to SPI"); return -1; } SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(ERROR, "compute_sql_asm_tsp: couldn't create query plan via SPI"); return -1; } if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) { elog(ERROR, "compute_sql_asm_tsp: SPI_cursor_open('%s') returns NULL", sql); return -1; } while (moredata == TRUE) { SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT); if (edgeColumns.id == -1) { if (!fetchEdgeTspColumns(SPI_tuptable, &edgeColumns,reverseCost)) return finish(SPIcode, ret); } ntuples = SPI_processed; totalTuples += ntuples; if (!edges){ edges = palloc(totalTuples * sizeof(tspEdgeType)); } else { edges = repalloc(edges, totalTuples * sizeof(tspEdgeType)); } if (edges == NULL) { elog(ERROR, "Out of memory"); return finish(SPIcode, ret); } if (ntuples > 0) { int t; SPITupleTable *tuptable = SPI_tuptable; TupleDesc tupdesc = SPI_tuptable->tupdesc; for (t = 0; t < ntuples; t++) { HeapTuple tuple = tuptable->vals[t]; fetchEdgeTsp(&tuple, &tupdesc, &edgeColumns, &edges[totalTuples - ntuples + t],reverseCost); } SPI_freetuptable(tuptable); } else { moredata = FALSE; } } DBG("Total %i tuples", totalTuples); DBG("Calling tsp functions total tuples <%i> initial path count <%i>", totalTuples,*pathCount); ret=processATSPData(edges,totalTuples,sourceVertexId,reverseCost, path, pathCount,errMesg); DBG("SIZE %i elements to process",*pathCount); if (!ret ) { elog(ERROR, "Error computing path: %s", errMesg); } return finish(SPIcode, ret); } PG_FUNCTION_INFO_V1(sql_asm_tsp); Datum sql_asm_tsp(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; int callCntr; int maxCalls; TupleDesc tupleDesc; tspPathElementType *path; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; int pathCount = 0; int ret; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ret = compute_sql_asm_tsp(text2char(PG_GETARG_TEXT_P(0)), PG_GETARG_INT32(1), PG_GETARG_BOOL(2), &path, &pathCount); #ifdef DEBUG if (ret >= 0) { int i; for (i = 0; i < pathCount; i++) { DBG("Step # %i vertexId %i cost %.4f", i, path[i].vertexId,path[i].cost); } } #endif /* total number of tuples to be returned */ funcctx->max_calls = pathCount; funcctx->user_fctx = path; DBG("Path count %i", pathCount); funcctx->tuple_desc = BlessTupleDesc(RelationNameGetTupleDesc("pgr_costResult")); MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); callCntr = funcctx->call_cntr; maxCalls = funcctx->max_calls; tupleDesc = funcctx->tuple_desc; path = (tspPathElementType*) funcctx->user_fctx; if (callCntr < maxCalls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; Datum *values; char* nulls; values = palloc(4 * sizeof(Datum)); nulls = palloc(4 * sizeof(char)); values[0] = Int32GetDatum(callCntr); nulls[0] = ' '; values[1] = Int32GetDatum(path[callCntr].vertexId); nulls[1] = ' '; values[2] = Float8GetDatum(0); // edge id not supplied by this method nulls[2] = ' '; values[3] = Float8GetDatum(path[callCntr].cost); nulls[3] = ' '; tuple = heap_formtuple(tupleDesc, values, nulls); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* clean up (this is not really necessary) */ pfree(values); pfree(nulls); SRF_RETURN_NEXT(funcctx, result); } else { /* do when there is no more left */ SRF_RETURN_DONE(funcctx); } }
Datum tsquery_rewrite(PG_FUNCTION_ARGS) { QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0))); text *in = PG_GETARG_TEXT_P(1); QUERYTYPE *rewrited = query; QTNode *tree; char *buf; void *plan; Portal portal; bool isnull; int i; if (query->size == 0) { PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); } tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); QTNTernary(tree); QTNSort(tree); buf = (char *) palloc(VARSIZE(in)); memcpy(buf, VARDATA(in), VARSIZE(in) - VARHDRSZ); buf[VARSIZE(in) - VARHDRSZ] = '\0'; SPI_connect(); if (tsqOid == InvalidOid) get_tsq_Oid(); if ((plan = SPI_prepare(buf, 0, NULL)) == NULL) elog(ERROR, "SPI_prepare('%s') returns NULL", buf); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL) elog(ERROR, "SPI_cursor_open('%s') returns NULL", buf); SPI_cursor_fetch(portal, true, 100); if (SPI_tuptable->tupdesc->natts != 2) elog(ERROR, "number of fields doesn't equal to 2"); if (SPI_gettypeid(SPI_tuptable->tupdesc, 1) != tsqOid) elog(ERROR, "column #1 isn't of tsquery type"); if (SPI_gettypeid(SPI_tuptable->tupdesc, 2) != tsqOid) elog(ERROR, "column #2 isn't of tsquery type"); while (SPI_processed > 0 && tree) { for (i = 0; i < SPI_processed && tree; i++) { Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); Datum sdata; if (isnull) continue; sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull); if (!isnull) { QUERYTYPE *qtex = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(qdata)); QUERYTYPE *qtsubs = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(sdata)); QTNode *qex, *qsubs = NULL; if (qtex->size == 0) { if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) pfree(qtex); if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) pfree(qtsubs); continue; } qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex)); QTNTernary(qex); QTNSort(qex); if (qtsubs->size) qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs)); tree = findsubquery(tree, qex, SPIMemory, qsubs, NULL); QTNFree(qex); if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) pfree(qtex); QTNFree(qsubs); if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) pfree(qtsubs); } } SPI_freetuptable(SPI_tuptable); SPI_cursor_fetch(portal, true, 100); } SPI_freetuptable(SPI_tuptable); SPI_cursor_close(portal); SPI_freeplan(plan); SPI_finish(); if (tree) { QTNBinary(tree); rewrited = QTN2QT(tree, PlainMemory); QTNFree(tree); PG_FREE_IF_COPY(query, 0); } else { rewrited->len = HDRSIZEQT; rewrited->size = 0; } pfree(buf); PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); }
Datum dbms_alert_defered_signal(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; TupleDesc tupdesc; HeapTuple rettuple; char *relname; text *name; text *message; int event_col; int message_col; Datum datum; bool isnull; int cycle = 0; float8 endtime; float8 timeout = 2; if (!CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called by trigger manager"))); if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called on valid event"))); if (SPI_connect() < 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("SPI_connect failed"))); if (strcmp((relname = SPI_getrelname(trigdata->tg_relation)), "ora_alerts") != 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called with valid relation"))); rettuple = trigdata->tg_trigtuple; tupdesc = trigdata->tg_relation->rd_att; if (SPI_ERROR_NOATTRIBUTE == (event_col = SPI_fnumber(tupdesc, "event"))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("attribute event not found"))); if (SPI_ERROR_NOATTRIBUTE == (message_col = SPI_fnumber(tupdesc, "message"))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("attribute message not found"))); datum = SPI_getbinval(rettuple, tupdesc, event_col, &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("event name is NULL"), errdetail("Eventname may not be NULL."))); name = DatumGetTextP(datum); datum = SPI_getbinval(rettuple, tupdesc, message_col, &isnull); if (isnull) message = NULL; else message = DatumGetTextP(datum); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) { ItemPointer tid; Oid argtypes[1] = {TIDOID}; char nulls[1] = {' '}; Datum values[1]; void *plan; create_message(name, message); LWLockRelease(shmem_lock); tid = &rettuple->t_data->t_ctid; if (!(plan = SPI_prepare("DELETE FROM ora_alerts WHERE ctid = $1", 1, argtypes))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("SPI_prepare failed"))); values[0] = ItemPointerGetDatum(tid); if (SPI_OK_DELETE != SPI_execute_plan(plan, values, nulls, false, 1)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("can't execute sql"))); SPI_finish(); return PointerGetDatum(rettuple); } WATCH_POST(timeout, endtime, cycle); LOCK_ERROR(); PG_RETURN_NULL(); }
/* prepare(query="select * from foo") * prepare(query="select * from foo where bar = $1", params=["text"]) * prepare(query="select * from foo where bar = $1", params=["text"], limit=5) */ PyObject * PLy_spi_prepare(PyObject *self, PyObject *args) { PLyPlanObject *plan; PyObject *list = NULL; PyObject *volatile optr = NULL; char *query; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; volatile int nargs; if (!PyArg_ParseTuple(args, "s|O", &query, &list)) return NULL; if (list && (!PySequence_Check(list))) { PLy_exception_set(PyExc_TypeError, "second argument of plpy.prepare must be a sequence"); return NULL; } if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) return NULL; nargs = list ? PySequence_Length(list) : 0; plan->nargs = nargs; plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL; plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL; plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { int i; /* * the other loop might throw an exception, if PLyTypeInfo member * isn't properly initialized the Py_DECREF(plan) will go boom */ for (i = 0; i < nargs; i++) { PLy_typeinfo_init(&plan->args[i]); plan->values[i] = PointerGetDatum(NULL); } for (i = 0; i < nargs; i++) { char *sptr; HeapTuple typeTup; Oid typeId; int32 typmod; Form_pg_type typeStruct; optr = PySequence_GetItem(list, i); if (PyString_Check(optr)) sptr = PyString_AsString(optr); else if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else { ereport(ERROR, (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i))); sptr = NULL; /* keep compiler quiet */ } /******************************************************** * Resolve argument type names and then look them up by * oid in the system cache, and remember the required *information for input conversion. ********************************************************/ parseTypeString(sptr, &typeId, &typmod); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeId); Py_DECREF(optr); /* * set optr to NULL, so we won't try to unref it again in case of * an error */ optr = NULL; plan->types[i] = typeId; typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typtype != TYPTYPE_COMPOSITE) PLy_output_datum_func(&plan->args[i], typeTup); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpy.prepare does not support composite types"))); ReleaseSysCache(typeTup); } pg_verifymbstr(query, strlen(query), false); plan->plan = SPI_prepare(query, plan->nargs, plan->types); if (plan->plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); /* transfer plan from procCxt to topCxt */ if (SPI_keepplan(plan->plan)) elog(ERROR, "SPI_keepplan failed"); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { Py_DECREF(plan); Py_XDECREF(optr); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(plan->plan != NULL); return (PyObject *) plan; }
Datum ttdummy(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; /* to get trigger name */ char **args; /* arguments */ int attnum[2]; /* fnumbers of start/stop columns */ Datum oldon, oldoff; Datum newon, newoff; Datum *cvals; /* column values */ char *cnulls; /* column nulls */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ HeapTuple trigtuple; HeapTuple newtuple = NULL; HeapTuple rettuple; TupleDesc tupdesc; /* tuple description */ int natts; /* # of attributes */ bool isnull; /* to know is some column NULL or not */ int ret; int i; if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "ttdummy: not fired by trigger manager"); if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) elog(ERROR, "ttdummy: must be fired for row"); if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) elog(ERROR, "ttdummy: must be fired before event"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) elog(ERROR, "ttdummy: cannot process INSERT event"); if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) newtuple = trigdata->tg_newtuple; trigtuple = trigdata->tg_trigtuple; rel = trigdata->tg_relation; relname = SPI_getrelname(rel); /* check if TT is OFF for this relation */ if (ttoff) /* OFF - nothing to do */ { pfree(relname); return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple); } trigger = trigdata->tg_trigger; if (trigger->tgnargs != 2) elog(ERROR, "ttdummy (%s): invalid (!= 2) number of arguments %d", relname, trigger->tgnargs); args = trigger->tgargs; tupdesc = rel->rd_att; natts = tupdesc->natts; for (i = 0; i < 2; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); if (attnum[i] <= 0) elog(ERROR, "ttdummy (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != INT4OID) elog(ERROR, "ttdummy (%s): attribute %s must be of integer type", relname, args[i]); } oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull); if (isnull) elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]); oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull); if (isnull) elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]); if (newtuple != NULL) /* UPDATE */ { newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull); if (isnull) elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]); newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull); if (isnull) elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]); if (oldon != newon || oldoff != newoff) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)", relname, args[0], args[1]))); if (newoff != TTDUMMY_INFINITY) { pfree(relname); /* allocated in upper executor context */ return PointerGetDatum(NULL); } } else if (oldoff != TTDUMMY_INFINITY) /* DELETE */ { pfree(relname); return PointerGetDatum(NULL); } newoff = DirectFunctionCall1(nextval, CStringGetTextDatum("ttdummy_seq")); /* nextval now returns int64; coerce down to int32 */ newoff = Int32GetDatum((int32) DatumGetInt64(newoff)); /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) elog(ERROR, "ttdummy (%s): SPI_connect returned %d", relname, ret); /* Fetch tuple values and nulls */ cvals = (Datum *) palloc(natts * sizeof(Datum)); cnulls = (char *) palloc(natts * sizeof(char)); for (i = 0; i < natts; i++) { cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple, tupdesc, i + 1, &isnull); cnulls[i] = (isnull) ? 'n' : ' '; } /* change date column(s) */ if (newtuple) /* UPDATE */ { cvals[attnum[0] - 1] = newoff; /* start_date eq current date */ cnulls[attnum[0] - 1] = ' '; cvals[attnum[1] - 1] = TTDUMMY_INFINITY; /* stop_date eq INFINITY */ cnulls[attnum[1] - 1] = ' '; } else /* DELETE */ { cvals[attnum[1] - 1] = newoff; /* stop_date eq current date */ cnulls[attnum[1] - 1] = ' '; } /* if there is no plan ... */ if (splan == NULL) { SPIPlanPtr pplan; Oid *ctypes; char *query; /* allocate space in preparation */ ctypes = (Oid *) palloc(natts * sizeof(Oid)); query = (char *) palloc(100 + 16 * natts); /* * Construct query: INSERT INTO _relation_ VALUES ($1, ...) */ sprintf(query, "INSERT INTO %s VALUES (", relname); for (i = 1; i <= natts; i++) { sprintf(query + strlen(query), "$%d%s", i, (i < natts) ? ", " : ")"); ctypes[i - 1] = SPI_gettypeid(tupdesc, i); } /* Prepare plan for query */ pplan = SPI_prepare(query, natts, ctypes); if (pplan == NULL) elog(ERROR, "ttdummy (%s): SPI_prepare returned %s", relname, SPI_result_code_string(SPI_result)); if (SPI_keepplan(pplan)) elog(ERROR, "ttdummy (%s): SPI_keepplan failed", relname); splan = pplan; } ret = SPI_execp(splan, cvals, cnulls, 0); if (ret < 0) elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret); /* Tuple to return to upper Executor ... */ if (newtuple) /* UPDATE */ rettuple = SPI_modifytuple(rel, trigtuple, 1, &(attnum[1]), &newoff, NULL); else /* DELETE */ rettuple = trigtuple; SPI_finish(); /* don't forget say Bye to SPI mgr */ pfree(relname); return PointerGetDatum(rettuple); }