/*------------------------------------------------------- * hash_page_items() * * Get IndexTupleData set in a hash page * * Usage: SELECT * FROM hash_page_items(get_raw_page('con_hash_index', 1)); *------------------------------------------------------- */ Datum hash_page_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Page page; Datum result; Datum values[3]; bool nulls[3]; uint32 hashkey; HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); if (SRF_IS_FIRSTCALL()) { TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); page = verify_hash_page(raw_page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = page; uargs->offset = FirstOffsetNumber; fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupleDesc = BlessTupleDesc(tupleDesc); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { ItemId id; IndexTuple itup; int j; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "invalid ItemId"); itup = (IndexTuple) PageGetItem(uargs->page, id); MemSet(nulls, 0, sizeof(nulls)); j = 0; values[j++] = Int32GetDatum((int32) uargs->offset); values[j++] = PointerGetDatum(&itup->t_tid); hashkey = _hash_get_indextuple_hashkey(itup); values[j] = Int64GetDatum((int64) hashkey); tuple = heap_form_tuple(fctx->attinmeta->tupdesc, values, nulls); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs); SRF_RETURN_DONE(fctx); } }
/* Clause 3.3.9.3 */ Datum TradeStatusFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { enum tsf1 { i_broker_name=0, i_charge, i_cust_f_name, i_cust_l_name, i_ex_name, i_exec_name, i_num_found, i_s_name, i_status_name, i_symbol, i_trade_dts, i_trade_id, i_trade_qty, i_type_name }; long acct_id = PG_GETARG_INT64(0); int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[1]; char nulls[1] = { ' ' }; /* * Prepare a values array for building the returned tuple. * This should be an array of C strings, which will * be processed later by the type input functions. */ values = (char **) palloc(sizeof(char *) * 14); values[i_charge] = (char *) palloc((VALUE_T_LEN + 1) * sizeof(char) * 50); values[i_ex_name] = (char *) palloc((EX_NAME_LEN + 3) * sizeof(char) * 50); values[i_exec_name] = (char *) palloc((T_EXEC_NAME_LEN + 3) * sizeof(char) * 50); values[i_num_found] = (char *) palloc((BIGINT_LEN + 1) * sizeof(char)); values[i_s_name] = (char *) palloc((S_NAME_LEN + 3) * sizeof(char) * 50); values[i_status_name] = (char *) palloc((ST_NAME_LEN + 3) * sizeof(char) * 50); values[i_symbol] = (char *) palloc((S_SYMB_LEN + 3) * sizeof(char) * 50); values[i_trade_dts] = (char *) palloc((MAXDATELEN + 1) * sizeof(char) * 50); values[i_trade_id] = (char *) palloc((BIGINT_LEN + 1) * sizeof(char) * 50); values[i_trade_qty] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char) * 50); values[i_type_name] = (char *) palloc((TT_NAME_LEN + 3) * sizeof(char) * 50); values[i_cust_l_name] = NULL; values[i_cust_f_name] = NULL; values[i_broker_name] = NULL; #ifdef DEBUG dump_tsf1_inputs(acct_id); #endif /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); funcctx->max_calls = 1; /* switch to memory context appropriate for multiple function calls */ TSF1_savedcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(TSF1_statements); #ifdef DEBUG sprintf(sql, SQLTSF1_1, acct_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(acct_id); ret = SPI_execute_plan(TSF1_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; } else { FAIL_FRAME_SET(&funcctx->max_calls, TSF1_statements[0].sql); dump_tsf1_inputs(acct_id); } sprintf(values[i_num_found], "%d", SPI_processed); strcpy(values[i_trade_id], "{"); strcpy(values[i_trade_dts], "{"); strcpy(values[i_status_name], "{"); strcpy(values[i_type_name], "{"); strcpy(values[i_symbol], "{"); strcpy(values[i_trade_qty], "{"); strcpy(values[i_exec_name], "{"); strcpy(values[i_charge], "{"); strcpy(values[i_s_name], "{"); strcpy(values[i_ex_name], "{"); for (i = 0; i < SPI_processed; i++) { tuple = tuptable->vals[i]; if (i > 0) { strcat(values[i_trade_id], ","); strcat(values[i_trade_dts], ","); strcat(values[i_status_name], ","); strcat(values[i_type_name], ","); strcat(values[i_symbol], ","); strcat(values[i_trade_qty], ","); strcat(values[i_exec_name], ","); strcat(values[i_charge], ","); strcat(values[i_s_name], ","); strcat(values[i_ex_name], ","); } strcat(values[i_trade_id], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_trade_dts], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_status_name], "\""); strcat(values[i_status_name], SPI_getvalue(tuple, tupdesc, 3)); strcat(values[i_status_name], "\""); strcat(values[i_type_name], "\""); strcat(values[i_type_name], SPI_getvalue(tuple, tupdesc, 4)); strcat(values[i_type_name], "\""); strcat(values[i_symbol], "\""); strcat(values[i_symbol], SPI_getvalue(tuple, tupdesc, 5)); strcat(values[i_symbol], "\""); strcat(values[i_trade_qty], SPI_getvalue(tuple, tupdesc, 6)); strcat(values[i_exec_name], "\""); strcat(values[i_exec_name], SPI_getvalue(tuple, tupdesc, 7)); strcat(values[i_exec_name], "\""); strcat(values[i_charge], SPI_getvalue(tuple, tupdesc, 8)); strcat(values[i_s_name], "\""); strcat(values[i_s_name], SPI_getvalue(tuple, tupdesc, 9)); strcat(values[i_s_name], "\""); strcat(values[i_ex_name], "\""); strcat(values[i_ex_name], SPI_getvalue(tuple, tupdesc, 10)); strcat(values[i_ex_name], "\""); } strcat(values[i_trade_id], "}"); strcat(values[i_trade_dts], "}"); strcat(values[i_status_name], "}"); strcat(values[i_type_name], "}"); strcat(values[i_symbol], "}"); strcat(values[i_trade_qty], "}"); strcat(values[i_exec_name], "}"); strcat(values[i_charge], "}"); strcat(values[i_s_name], "}"); strcat(values[i_ex_name], "}"); #ifdef DEBUG sprintf(sql, SQLTSF1_2, acct_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TSF1_2, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; if (SPI_processed > 0) { tuple = tuptable->vals[0]; values[i_cust_l_name] = SPI_getvalue(tuple, tupdesc, 1); values[i_cust_f_name] = SPI_getvalue(tuple, tupdesc, 2); values[i_broker_name] = SPI_getvalue(tuple, tupdesc, 3); } } else { FAIL_FRAME_SET(&funcctx->max_calls, TSF1_statements[1].sql); dump_tsf1_inputs(acct_id); } /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(TSF1_savedcxt); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; #ifdef DEBUG for (i = 0; i < 14; i++) { elog(NOTICE, "TSF1 OUT: %d %s", i, values[i]); } #endif /* DEBUG */ /* Build a tuple. */ tuple = BuildTupleFromCStrings(attinmeta, values); /* Make the tuple into a datum. */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* Do when there is no more left. */ SPI_finish(); if (TSF1_savedcxt) MemoryContextSwitchTo(TSF1_savedcxt); SRF_RETURN_DONE(funcctx); } }
Datum generate_series_step_int4(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; generate_series_fctx *fctx; int32 result; MemoryContext oldcontext; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { int32 start = PG_GETARG_INT32(0); int32 finish = PG_GETARG_INT32(1); int32 step = 1; /* see if we were given an explicit step size */ if (PG_NARGS() == 3) step = PG_GETARG_INT32(2); if (step == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size may not equal zero"))); /* 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); /* allocate memory for user context */ fctx = (generate_series_fctx *) palloc(sizeof(generate_series_fctx)); /* * Use fctx to keep state from call to call. Seed current with the * original start value */ fctx->current = start; fctx->finish = finish; fctx->step = step; funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); /* * get the saved state and use current as the result for this iteration */ fctx = funcctx->user_fctx; result = fctx->current; if ((fctx->step > 0 && fctx->current <= fctx->finish) || (fctx->step < 0 && fctx->current >= fctx->finish)) { /* increment current in preparation for next iteration */ fctx->current += fctx->step; /* if next-value computation overflows, this is the final result */ if (SAMESIGN(result, fctx->step) && !SAMESIGN(result, fctx->current)) fctx->step = 0; /* do when there is more left to send */ SRF_RETURN_NEXT(funcctx, Int32GetDatum(result)); } else /* do when there is no more left */ SRF_RETURN_DONE(funcctx); }
Datum pg_tablespace_databases(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; ts_db_fctx *fctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; Oid tablespaceOid = PG_GETARG_OID(0); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(ts_db_fctx)); /* * size = tablespace dirname length + dir sep char + oid + terminator */ fctx->location = (char *) palloc(9 + 1 + OIDCHARS + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1); if (tablespaceOid == GLOBALTABLESPACE_OID) { fctx->dirdesc = NULL; ereport(WARNING, (errmsg("global tablespace never has databases"))); } else { if (tablespaceOid == DEFAULTTABLESPACE_OID) sprintf(fctx->location, "base"); else sprintf(fctx->location, "pg_tblspc/%u/%s", tablespaceOid, TABLESPACE_VERSION_DIRECTORY); fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) { /* the only expected error is ENOENT */ if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fctx->location))); ereport(WARNING, (errmsg("%u is not a tablespace OID", tablespaceOid))); } } funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (ts_db_fctx *) funcctx->user_fctx; if (!fctx->dirdesc) /* not a tablespace */ SRF_RETURN_DONE(funcctx); while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { char *subdir; DIR *dirdesc; Oid datOid = atooid(de->d_name); /* this test skips . and .., but is awfully weak */ if (!datOid) continue; /* if database subdir is empty, don't report tablespace as used */ /* size = path length + dir sep char + file name + terminator */ subdir = palloc(strlen(fctx->location) + 1 + strlen(de->d_name) + 1); sprintf(subdir, "%s/%s", fctx->location, de->d_name); dirdesc = AllocateDir(subdir); while ((de = ReadDir(dirdesc, subdir)) != NULL) { if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) break; } FreeDir(dirdesc); pfree(subdir); if (!de) continue; /* indeed, nothing in it */ SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(datOid)); } FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); }
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); } }
/* * pg_prepared_xact * Produce a view with one row per prepared transaction. * * This function is here so we don't have to export the * GlobalTransactionData struct definition. */ Datum pg_prepared_xact(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; Working_State *status; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* 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); /* build tupdesc for result tuples */ /* this had better match pg_prepared_xacts view in system_views.sql */ tupdesc = CreateTemplateTupleDesc(5, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepared", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "ownerid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "dbid", OIDOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the 2PC status information that we will format and send * out as a result set. */ status = (Working_State *) palloc(sizeof(Working_State)); funcctx->user_fctx = (void *) status; status->ngxacts = GetPreparedTransactionList(&status->array); status->currIdx = 0; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); status = (Working_State *) funcctx->user_fctx; while (status->array != NULL && status->currIdx < status->ngxacts) { GlobalTransaction gxact = &status->array[status->currIdx++]; Datum values[5]; bool nulls[5]; HeapTuple tuple; Datum result; if (!gxact->valid) continue; /* * Form tuple with appropriate data. */ MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); values[0] = TransactionIdGetDatum(gxact->proc.xid); values[1] = CStringGetTextDatum(gxact->gid); values[2] = TimestampTzGetDatum(gxact->prepared_at); values[3] = ObjectIdGetDatum(gxact->owner); values[4] = ObjectIdGetDatum(gxact->proc.databaseId); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } SRF_RETURN_DONE(funcctx); }
Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(13, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", INETOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_port", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "sess_id", INT4OID, -1, 0); /* GPDB */ TupleDescInitEntry(tupdesc, (AttrNumber) 13, "waiting_resource", BOOLOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->user_fctx = palloc0(sizeof(int)); if (PG_ARGISNULL(0)) { /* Get all backends */ funcctx->max_calls = pgstat_fetch_stat_numbackends(); } else { /* * Get one backend - locate by pid. * * We lookup the backend early, so we can return zero rows if it * doesn't exist, instead of returning a single row full of NULLs. */ int pid = PG_GETARG_INT32(0); int i; int n = pgstat_fetch_stat_numbackends(); for (i = 1; i <= n; i++) { PgBackendStatus *be = pgstat_fetch_stat_beentry(i); if (be) { if (be->st_procpid == pid) { *(int *) (funcctx->user_fctx) = i; break; } } } if (*(int *) (funcctx->user_fctx) == 0) /* Pid not found, return zero rows */ funcctx->max_calls = 0; else funcctx->max_calls = 1; } MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ Datum values[13]; bool nulls[13]; HeapTuple tuple; PgBackendStatus *beentry; SockAddr zero_clientaddr; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (*(int *) (funcctx->user_fctx) > 0) { /* Get specific pid slot */ beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx)); } else { /* Get the next one in the list */ beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */ } if (!beentry) { int i; for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++) nulls[i] = true; nulls[4] = false; values[4] = CStringGetTextDatum("<backend information not available>"); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } /* Values available to all callers */ values[0] = ObjectIdGetDatum(beentry->st_databaseid); values[1] = Int32GetDatum(beentry->st_procpid); values[2] = ObjectIdGetDatum(beentry->st_userid); if (beentry->st_appname) values[3] = CStringGetTextDatum(beentry->st_appname); else nulls[3] = true; /* Values only available to same user or superuser */ if (superuser() || beentry->st_userid == GetUserId()) { if (*(beentry->st_activity) == '\0') { values[4] = CStringGetTextDatum("<command string not enabled>"); } else { values[4] = CStringGetTextDatum(beentry->st_activity); } values[5] = BoolGetDatum(beentry->st_waiting); if (beentry->st_xact_start_timestamp != 0) values[6] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[6] = true; if (beentry->st_activity_start_timestamp != 0) values[7] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[7] = true; if (beentry->st_proc_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[8] = true; /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) { nulls[9] = true; nulls[10] = true; } else { if (beentry->st_clientaddr.addr.ss_family == AF_INET #ifdef HAVE_IPV6 || beentry->st_clientaddr.addr.ss_family == AF_INET6 #endif ) { char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; int ret; remote_host[0] = '\0'; remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) { nulls[9] = true; nulls[10] = true; } else { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[9] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); values[10] = Int32GetDatum(atoi(remote_port)); } } else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) { /* * Unix sockets always reports NULL for host and -1 for * port, so it's possible to tell the difference to * connections we have no permissions to view, or with * errors. */ nulls[9] = true; values[10] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[9] = true; nulls[10] = true; } } values[12] = BoolGetDatum(beentry->st_waiting_resource); } else { /* No permissions to view data about this session */ values[4] = CStringGetTextDatum("<insufficient privilege>"); nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; nulls[12] = true; } values[11] = Int32GetDatum(beentry->st_session_id); /* GPDB */ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { /* nothing left */ SRF_RETURN_DONE(funcctx); } }
Datum compute_pa_grid_cell(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tupdesc; pa_grid* grid; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; /* 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); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_SCALAR) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* * WARNING: there stil is a small change that memory is alllocated * during the next calls. This could cause problems. * My user context stuff, allocate all memory expected in this call. * Other memory allocated outside this context will be thrown away * after the first call. */ text *arg1 = PG_GETARG_TEXT_P(0); funcctx->user_fctx = create_pa_grid(VARDATA(arg1)); grid = (pa_grid*)funcctx->user_fctx; if (grid->q.keyFlag == KD_BYTE_STRING) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("pa_grid_cell does not support byte keys. Use pa_grid function!"))); } MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); grid = (pa_grid*)funcctx->user_fctx; if ( next_pa_grid(grid) ) { Datum long_result = Int64GetDatum( grid->cellKey ); SRF_RETURN_NEXT(funcctx, long_result); } else /* do when there is no more left */ { free_pa_grid(grid); // free the grid funcctx->user_fctx = NULL; // SRF_RETURN_DONE(funcctx); } }
Datum compute_pa_grid(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tupdesc; AttInMetadata *attinmeta; pa_grid* grid; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; /* 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); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; /* * WARNING: there stil is a small change that memory is alllocated * during the next calls. This could cause problems. * My user context stuff, allocate all memory expected in this call. * Other memory allocated outside this context will be thrown away * after the first call. */ text *arg1 = PG_GETARG_TEXT_P(0); // FILE* f = fopen("/tmp/LOG","a"); // fprintf(f,"KEY=[%s]\n",VARDATA(arg1)); // fclose(f); if ( !(funcctx->user_fctx = create_pa_grid(VARDATA(arg1)))) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), // INCOMPLETE errmsg("possible memory error " ))); } grid = (pa_grid*)funcctx->user_fctx; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); grid = (pa_grid*)funcctx->user_fctx; attinmeta = funcctx->attinmeta; int nextVal = next_pa_grid(grid); if ( nextVal < 0 ) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), // INCOMPLETE errmsg("possible memory error " ))); } if ( nextVal ) { HeapTuple tuple; Datum result; snprintf(grid->values[0], 24, "%ld", grid->gridKey); snprintf(grid->values[1], 24, "%ld", grid->cellKey); snprintf(grid->values[2], (grid->q.totalBytes+1) * 2, "%s", grid->cellByteKey); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, grid->values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else /* do when there is no more left */ { free_pa_grid(grid); // free the grid funcctx->user_fctx = NULL; // SRF_RETURN_DONE(funcctx); } }
PGDLLEXPORT Datum withPoints_ksp(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tuple_desc; /*******************************************************************************/ /* MODIFY AS NEEDED */ /* */ General_path_element_t *result_tuples = 0; size_t result_count = 0; /* */ /*******************************************************************************/ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /*******************************************************************************/ /* MODIFY AS NEEDED */ // CREATE OR REPLACE FUNCTION pgr_withPoint( // edges_sql TEXT, // points_sql TEXT, // start_pid INTEGER, // end_pid BIGINT, // k BIGINT, // // directed BOOLEAN -- DEFAULT true, // heap_paths BOOLEAN -- DEFAULT false, // driving_side CHAR -- DEFAULT 'b', // details BOOLEAN -- DEFAULT false, PGR_DBG("Calling process"); PGR_DBG("initial driving side:%s", text_to_cstring(PG_GETARG_TEXT_P(7))); process( text_to_cstring(PG_GETARG_TEXT_P(0)), text_to_cstring(PG_GETARG_TEXT_P(1)), PG_GETARG_INT64(2), PG_GETARG_INT64(3), PG_GETARG_INT32(4), PG_GETARG_BOOL(5), PG_GETARG_BOOL(6), text_to_cstring(PG_GETARG_TEXT_P(7)), PG_GETARG_BOOL(8), &result_tuples, &result_count); /* */ /*******************************************************************************/ #if PGSQL_VERSION > 95 funcctx->max_calls = result_count; #else funcctx->max_calls = (uint32_t)result_count; #endif funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); tuple_desc = funcctx->tuple_desc; result_tuples = (General_path_element_t*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { HeapTuple tuple; Datum result; Datum *values; bool* nulls; /*******************************************************************************/ /* MODIFY AS NEEDED */ values = palloc(7 * sizeof(Datum)); nulls = palloc(7 * sizeof(bool)); size_t i; for(i = 0; i < 7; ++i) { nulls[i] = false; } /* OUT seq INTEGER, OUT path_id INTEGER, OUT path_seq INTEGER, OUT node BIGINT, OUT edge BIGINT, OUT cost FLOAT, OUT agg_cost FLOAT) */ // postgres starts counting from 1 values[0] = Int32GetDatum(funcctx->call_cntr + 1); values[1] = Int32GetDatum((int)(result_tuples[funcctx->call_cntr].start_id + 1)); values[2] = Int32GetDatum(result_tuples[funcctx->call_cntr].seq); values[3] = Int64GetDatum(result_tuples[funcctx->call_cntr].node); values[4] = Int64GetDatum(result_tuples[funcctx->call_cntr].edge); values[5] = Float8GetDatum(result_tuples[funcctx->call_cntr].cost); values[6] = Float8GetDatum(result_tuples[funcctx->call_cntr].agg_cost); /*******************************************************************************/ tuple =heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { SRF_RETURN_DONE(funcctx); } }
Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; char *values[6]; HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { RangeVar *relrv; Relation rel; Buffer buffer; BTPageOpaque opaque; TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { ItemId id; IndexTuple itup; int j; int off; int dlen; char *dump; char *ptr; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "invalid ItemId"); itup = (IndexTuple) PageGetItem(uargs->page, id); j = 0; values[j++] = psprintf("%d", uargs->offset); values[j++] = psprintf("(%u,%u)", BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j++] = psprintf("%d", (int) IndexTupleSize(itup)); values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); dump = palloc0(dlen * 3 + 1); values[j] = dump; for (off = 0; off < dlen; off++) { if (off > 0) *dump++ = ' '; sprintf(dump, "%02x", *(ptr + off) & 0xff); dump += 2; } tuple = BuildTupleFromCStrings(fctx->attinmeta, values); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum extractGridData(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* switch to memory context appropriate for multiple function calls */ MemoryContext oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = BlessTupleDesc(tupdesc); // Initalize geos static int geosInitialized = 0; if ( ! geosInitialized ) { initGEOS(logInfo, logError); geosInitialized = 1; } // Get the data to be returned struct GridPointDataListIterator * points = getExtractGridDataReturnValues(fcinfo); // Convert data into a set of Datums funcctx->max_calls = points->list->count; Datum * returnValues = palloc(sizeof(Datum) * funcctx->max_calls); int i; for ( i = 0; i < funcctx->max_calls; ++ i ) returnValues[i] = getNextReturnTupleViaDatums(GridPointDataListIteratorNext(points), funcctx->tuple_desc); funcctx->user_fctx = (void *) returnValues; // Delete intermediate data // GridPointDataListDelete(points->list); // GridPointDataListIteratorDelete(points); MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); if ( funcctx->call_cntr < funcctx->max_calls ) { Datum * ret = (Datum *) funcctx->user_fctx; Datum result = ret[funcctx->call_cntr]; SRF_RETURN_NEXT(funcctx, result); } else { //GridPointDataListDelete(iterator->list); SRF_RETURN_DONE(funcctx); } }
PGDLLEXPORT Datum kshortest_path(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tuple_desc; General_path_element_t *path = NULL; size_t result_count = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* CREATE OR REPLACE FUNCTION _pgr_ksp( sql text, start_vid bigint, end_vid bigint, k integer, directed boolean, heap_paths boolean */ PGR_DBG("Calling process"); compute( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_INT64(1), PG_GETARG_INT64(2), PG_GETARG_INT32(3), PG_GETARG_BOOL(4), PG_GETARG_BOOL(5), &path, &result_count); PGR_DBG("Total number of tuples to be returned %ld \n", result_count); #if PGSQL_VERSION > 95 funcctx->max_calls = result_count; #else funcctx->max_calls = (uint32_t)result_count; #endif funcctx->user_fctx = path; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record\n"))); funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); tuple_desc = funcctx->tuple_desc; path = (General_path_element_t*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { HeapTuple tuple; Datum result; Datum *values; bool* nulls; values = palloc(7 * sizeof(Datum)); nulls = palloc(7 * sizeof(bool)); size_t i; for (i = 0; i < 7; ++i) { nulls[i] = false; } values[0] = Int32GetDatum(funcctx->call_cntr + 1); values[1] = Int32GetDatum(path[funcctx->call_cntr].start_id + 1); values[2] = Int32GetDatum(path[funcctx->call_cntr].seq); values[3] = Int64GetDatum(path[funcctx->call_cntr].node); values[4] = Int64GetDatum(path[funcctx->call_cntr].edge); values[5] = Float8GetDatum(path[funcctx->call_cntr].cost); values[6] = Float8GetDatum(path[funcctx->call_cntr].agg_cost); tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* do when there is no more left */ SRF_RETURN_DONE(funcctx); } }
Datum each(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AKStore *st; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; HStore *hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (AKStore *) palloc(sizeof(AKStore)); st->i = 0; st->hs = (HStore *) palloc(VARSIZE(hs)); memcpy(st->hs, hs, VARSIZE(hs)); funcctx->user_fctx = (void *) st; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); PG_FREE_IF_COPY(hs, 0); } funcctx = SRF_PERCALL_SETUP(); st = (AKStore *) funcctx->user_fctx; if (st->i < st->hs->size) { HEntry *ptr = &(ARRPTR(st->hs)[st->i]); Datum res, dvalues[2]; char nulls[] = {' ', ' '}; text *item; HeapTuple tuple; item = (text *) palloc(VARHDRSZ + ptr->keylen); SET_VARSIZE(item, VARHDRSZ + ptr->keylen); memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen); dvalues[0] = PointerGetDatum(item); if (ptr->valisnull) { dvalues[1] = (Datum) 0; nulls[1] = 'n'; } else { int vallen = ptr->vallen; item = (text *) palloc(VARHDRSZ + vallen); SET_VARSIZE(item, VARHDRSZ + vallen); memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen); dvalues[1] = PointerGetDatum(item); } st->i++; tuple = heap_formtuple(funcctx->attinmeta->tupdesc, dvalues, nulls); res = HeapTupleGetDatum(tuple); pfree(DatumGetPointer(dvalues[0])); if (nulls[1] != 'n') pfree(DatumGetPointer(dvalues[1])); SRF_RETURN_NEXT(funcctx, res); } pfree(st->hs); pfree(st); SRF_RETURN_DONE(funcctx); }
Datum #else // _MSC_VER PGDLLEXPORT Datum #endif one_to_one_withPoints(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; uint32_t call_cntr; uint32_t max_calls; TupleDesc tuple_desc; /*******************************************************************************/ /* MODIFY AS NEEDED */ /* */ General_path_element_t *result_tuples = NULL; size_t result_count = 0; /* */ /*******************************************************************************/ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /*******************************************************************************/ /* MODIFY AS NEEDED */ // CREATE OR REPLACE FUNCTION pgr_withPoint( // edges_sql TEXT, // points_sql TEXT, // start_pid BIGINT, // end_pid BIGINT, // directed BOOLEAN -- DEFAULT true, // driving_side CHAR -- DEFAULT 'b', // details BOOLEAN -- DEFAULT true, // only_cost BOOLEAN DEFAULT false, PGR_DBG("Calling process"); PGR_DBG("initial driving side:%s", pgr_text2char(PG_GETARG_TEXT_P(5))); process( pgr_text2char(PG_GETARG_TEXT_P(0)), pgr_text2char(PG_GETARG_TEXT_P(1)), PG_GETARG_INT64(2), PG_GETARG_INT64(3), PG_GETARG_BOOL(4), pgr_text2char(PG_GETARG_TEXT_P(5)), PG_GETARG_BOOL(6), PG_GETARG_BOOL(7), &result_tuples, &result_count); /* */ /*******************************************************************************/ funcctx->max_calls = (uint32_t)result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; result_tuples = (General_path_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) { HeapTuple tuple; Datum result; Datum *values; char* nulls; /*******************************************************************************/ /* MODIFY AS NEEDED */ // OUT seq BIGINT, // OUT path_seq, // OUT node BIGINT, // OUT edge BIGINT, // OUT cost FLOAT, // OUT agg_cost FLOAT) values = palloc(6 * sizeof(Datum)); nulls = palloc(6 * sizeof(char)); size_t i; for(i = 0; i < 6; ++i) { nulls[i] = ' '; } // postgres starts counting from 1 values[0] = Int32GetDatum(call_cntr + 1); values[1] = Int32GetDatum(result_tuples[call_cntr].seq); values[2] = Int64GetDatum(result_tuples[call_cntr].node); values[3] = Int64GetDatum(result_tuples[call_cntr].edge); values[4] = Float8GetDatum(result_tuples[call_cntr].cost); values[5] = Float8GetDatum(result_tuples[call_cntr].agg_cost); /*******************************************************************************/ tuple = heap_formtuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { // cleanup if (result_tuples) free(result_tuples); SRF_RETURN_DONE(funcctx); } }
/*------------------------------------------------------- * bt_page_items() * * Get IndexTupleData set in a btree page * * Usage: SELECT * FROM bt_page_items('t1_pkey', 1); *------------------------------------------------------- */ Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { RangeVar *relrv; Relation rel; Buffer buffer; BTPageOpaque opaque; TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
/* * pg_lock_status - produce a view with one row per held or awaited lock mode */ Datum pg_lock_status(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; PG_Lock_Status *mystatus; LockData *lockData; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* 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); /* build tupdesc for result tuples */ /* this had better match pg_locks view in system_views.sql */ tupdesc = CreateTemplateTupleDesc(16, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "transactionid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "classid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "objid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objsubid", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "transaction", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "pid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "mode", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 13, "granted", BOOLOID, -1, 0); /* * These next columns are specific to GPDB */ TupleDescInitEntry(tupdesc, (AttrNumber) 14, "mppSessionId", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 15, "mppIsWriter", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 16, "gp_segment_id", INT4OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the locking information that we will format and send * out as a result set. */ mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status)); funcctx->user_fctx = (void *) mystatus; mystatus->lockData = GetLockStatusData(); mystatus->currIdx = 0; mystatus->numSegLocks = 0; mystatus->numsegresults = 0; mystatus->segresults = NULL; } funcctx = SRF_PERCALL_SETUP(); mystatus = (PG_Lock_Status *) funcctx->user_fctx; lockData = mystatus->lockData; /* * This loop returns all the local lock data from the segment we are running on. */ while (mystatus->currIdx < lockData->nelements) { PROCLOCK *proclock; LOCK *lock; PGPROC *proc; bool granted; LOCKMODE mode = 0; const char *locktypename; char tnbuf[32]; Datum values[16]; bool nulls[16]; HeapTuple tuple; Datum result; proclock = &(lockData->proclocks[mystatus->currIdx]); lock = &(lockData->locks[mystatus->currIdx]); proc = &(lockData->procs[mystatus->currIdx]); /* * Look to see if there are any held lock modes in this PROCLOCK. If * so, report, and destructively modify lockData so we don't report * again. */ granted = false; if (proclock->holdMask) { for (mode = 0; mode < MAX_LOCKMODES; mode++) { if (proclock->holdMask & LOCKBIT_ON(mode)) { granted = true; proclock->holdMask &= LOCKBIT_OFF(mode); break; } } } /* * If no (more) held modes to report, see if PROC is waiting for a * lock on this lock. */ if (!granted) { if (proc->waitLock == proclock->tag.myLock) { /* Yes, so report it with proper mode */ mode = proc->waitLockMode; /* * We are now done with this PROCLOCK, so advance pointer to * continue with next one on next call. */ mystatus->currIdx++; } else { /* * Okay, we've displayed all the locks associated with this * PROCLOCK, proceed to the next one. */ mystatus->currIdx++; continue; } } /* * Form tuple with appropriate data. */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); if (lock->tag.locktag_type <= LOCKTAG_ADVISORY) locktypename = LockTagTypeNames[lock->tag.locktag_type]; else { snprintf(tnbuf, sizeof(tnbuf), "unknown %d", (int) lock->tag.locktag_type); locktypename = tnbuf; } values[0] = CStringGetTextDatum(locktypename); switch (lock->tag.locktag_type) { case LOCKTAG_RELATION: case LOCKTAG_RELATION_EXTEND: case LOCKTAG_RELATION_RESYNCHRONIZE: values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; break; case LOCKTAG_PAGE: values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); values[3] = UInt32GetDatum(lock->tag.locktag_field3); nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; break; case LOCKTAG_TUPLE: values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); values[3] = UInt32GetDatum(lock->tag.locktag_field3); values[4] = UInt16GetDatum(lock->tag.locktag_field4); nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; break; case LOCKTAG_TRANSACTION: values[5] = TransactionIdGetDatum(lock->tag.locktag_field1); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; break; case LOCKTAG_RELATION_APPENDONLY_SEGMENT_FILE: values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); values[7] = ObjectIdGetDatum(lock->tag.locktag_field3); nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[8] = true; break; case LOCKTAG_RESOURCE_QUEUE: values[1] = ObjectIdGetDatum(proc->databaseId); values[7] = ObjectIdGetDatum(lock->tag.locktag_field1); nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[8] = true; break; case LOCKTAG_OBJECT: case LOCKTAG_USERLOCK: case LOCKTAG_ADVISORY: default: /* treat unknown locktags like OBJECT */ values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); values[6] = ObjectIdGetDatum(lock->tag.locktag_field2); values[7] = ObjectIdGetDatum(lock->tag.locktag_field3); values[8] = Int16GetDatum(lock->tag.locktag_field4); nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; break; } values[9] = TransactionIdGetDatum(proc->xid); if (proc->pid != 0) values[10] = Int32GetDatum(proc->pid); else nulls[10] = true; values[11] = DirectFunctionCall1(textin, CStringGetDatum((char *) GetLockmodeName(LOCK_LOCKMETHOD(*lock), mode))); values[12] = BoolGetDatum(granted); values[13] = Int32GetDatum(proc->mppSessionId); values[14] = Int32GetDatum(proc->mppIsWriter); values[15] = Int32GetDatum(Gp_segment); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } /* * This loop only executes on the masterDB and only in dispatch mode, because that * is the only time we dispatched to the segDBs. */ while (mystatus->currIdx >= lockData->nelements && mystatus->currIdx < lockData->nelements + mystatus->numSegLocks) { HeapTuple tuple; Datum result; Datum values[16]; bool nulls[16]; int i; int whichresultset = 0; int whichelement = mystatus->currIdx - lockData->nelements; int whichrow = whichelement; Assert(Gp_role == GP_ROLE_DISPATCH); /* * Because we have one result set per segDB (rather than one big result set with everything), * we need to figure out which result set we are on, and which row within that result set * we are returning. * * So, we walk through all the result sets and all the rows in each one, in order. */ while(whichrow >= PQntuples(mystatus->segresults[whichresultset])) { whichrow -= PQntuples(mystatus->segresults[whichresultset]); whichresultset++; if (whichresultset >= mystatus->numsegresults) break; } /* * If this condition is true, we have already sent everything back, * and we just want to do the SRF_RETURN_DONE */ if (whichresultset >= mystatus->numsegresults) break; mystatus->currIdx++; /* * Form tuple with appropriate data we got from the segDBs */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); /* * For each column, extract out the value (which comes out in text). * Convert it to the appropriate datatype to match our tupledesc, * and put that in values. * The columns look like this (from select statement earlier): * * " (locktype text, database oid, relation oid, page int4, tuple int2," * " transactionid xid, classid oid, objid oid, objsubid int2," * " transaction xid, pid int4, mode text, granted boolean, " * " mppSessionId int4, mppIsWriter boolean, gp_segment_id int4) ," */ values[0] = CStringGetTextDatum(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 0)); values[1] = ObjectIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 1))); values[2] = ObjectIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 2))); values[3] = UInt32GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 3))); values[4] = UInt16GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 4))); values[5] = TransactionIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 5))); values[6] = ObjectIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 6))); values[7] = ObjectIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 7))); values[8] = UInt16GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 8))); values[9] = TransactionIdGetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow, 9))); values[10] = UInt32GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow,10))); values[11] = CStringGetTextDatum(PQgetvalue(mystatus->segresults[whichresultset], whichrow,11)); values[12] = BoolGetDatum(strncmp(PQgetvalue(mystatus->segresults[whichresultset], whichrow,12),"t",1)==0); values[13] = Int32GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow,13))); values[14] = BoolGetDatum(strncmp(PQgetvalue(mystatus->segresults[whichresultset], whichrow,14),"t",1)==0); values[15] = Int32GetDatum(atoi(PQgetvalue(mystatus->segresults[whichresultset], whichrow,15))); /* * Copy the null info over. It should all match properly. */ for (i=0; i<16; i++) { nulls[i] = PQgetisnull(mystatus->segresults[whichresultset], whichrow, i); } tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } /* * if we dispatched to the segDBs, free up the memory holding the result sets. * Otherwise we might leak this memory each time we got called (does it automatically * get freed by the pool being deleted? Probably, but this is safer). */ if (mystatus->segresults != NULL) { int i; for (i = 0; i < mystatus->numsegresults; i++) PQclear(mystatus->segresults[i]); free(mystatus->segresults); } SRF_RETURN_DONE(funcctx); }
Datum bt_page_items_bytea(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Datum result; FuncCallContext *fctx; struct user_args *uargs; int raw_page_size; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { BTPageOpaque opaque; MemoryContext mctx; TupleDesc tupleDesc; raw_page_size = VARSIZE(raw_page) - VARHDRSZ; if (raw_page_size < SizeOfPageHeaderData) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = VARDATA(raw_page); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISMETA(opaque)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("block is a meta page"))); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs); SRF_RETURN_DONE(fctx); } }
datum_t bt_page_items(PG_FUNC_ARGS) { text *relname = ARG_TEXT_P(0); uint32 blkno = ARG_UINT32(1); datum_t result; char *values[6]; struct heap_tuple * tuple; struct fcall_ctx *fctx; struct mctx * mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(E_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { range_var_n *relrv; struct relation * rel; buf_id_t buffer; struct bt_page_opaque * opaque; struct tuple * tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = nl_to_range_var(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, ACCESS_SHR_LOCK); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", REL_NAME(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (REL_IS_OTHER_TMP(rel)) ereport(ERROR, (errcode(E_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = read_buf(rel, blkno); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = mctx_switch(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLK_SZ); memcpy(uargs->page, BUF_PAGE(buffer), BLK_SZ); release_buf(buffer); relation_close(rel, ACCESS_SHR_LOCK); uargs->offset = FIRST_ITEM_ID; opaque = (struct bt_page_opaque *) PAGE_SPECIAL_PTR(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PAGE_MAX_ITEM_ID(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; mctx_switch(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { struct item_id * id; struct index_tuple * itup; int j; int off; int dlen; char *dump; char *ptr; id = PAGE_ITEM_ID(uargs->page, uargs->offset); if (!ITEMID_VALID(id)) elog(ERROR, "invalid ItemId"); itup = (struct index_tuple *) PAGE_GET_ITEM(uargs->page, id); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", BLK_ID_TO_BLK_NR(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) INDEX_TUPLE_SZ(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", INDEX_TUPLE_HAS_NULLS(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", INDEX_TUPLE_HAS_VAR(itup) ? 't' : 'f'); ptr = (char *) itup + INDEX_TUPLE_DATA_OFFSET(itup->t_info); dlen = INDEX_TUPLE_SZ(itup) - INDEX_TUPLE_DATA_OFFSET(itup->t_info); dump = pzalloc(dlen * 3 + 1); values[j] = dump; for (off = 0; off < dlen; off++) { if (off > 0) *dump++ = ' '; sprintf(dump, "%02x", *(ptr + off) & 0xff); dump += 2; } tuple = build_tuple_from_cstrings(fctx->attinmeta, values); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum kc_expand(PG_FUNCTION_ARGS) { KC_ENTRY *search; FuncCallContext *funcctx; int call_cntr; char *kbuf; size_t ksiz, vsiz; const char *cvbuf; char *kv_kbuf = NULL; size_t kv_ksiz; int done; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; /* 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); // Make sure that there are enough args. if (PG_NARGS() < MIN_ARGS) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Must run expand with at least %d args!", MIN_ARGS))); } /* Make the kcdb here. */ search = (KC_ENTRY *)palloc(sizeof(KC_ENTRY)); search->db = kcdbnew(); if (open_db (search->db, text_to_cstring(PG_GETARG_TEXT_PP(0)), text_to_cstring(PG_GETARG_TEXT_PP(1)))) { // Set the key to jump into: // Call with -- map_name, result_id, class, doctype, pop, psource // Here, map_name describes a db to open. // Otherwise, result_id:class:doctype:pop:psource (search->jump_key) = (char *) palloc(MAX_JUMP_KEY_LEN * sizeof(char)); int index_point; search->jump_key = text_to_cstring(PG_GETARG_TEXT_PP(2)); int size_left = MAX_JUMP_KEY_LEN; for (index_point = START_VARIABLE_INDEX; index_point < END_VARIABLE_INDEX; index_point++) { if (PG_NARGS() > index_point) { char *next_idx = text_to_cstring(PG_GETARG_TEXT_PP(index_point)); if (next_idx != NULL) { size_left = size_left - (2 + strlen(next_idx)); strncat (search->jump_key, CF_LABEL_SEP, size_left); strncat (search->jump_key, next_idx, size_left); } } } #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Setting jump buffer -- [%s]", search->jump_key))); #endif // Create a cursor, and set it to the base point looking for entries. search->cur = kcdbcursor(search->db); kccurjumpkey(search->cur, search->jump_key, MAX_JUMP_KEY_LEN); } else { search->db = NULL; } search->next_map = 0; search->msg = NULL; // Save the search struct for the subsequent calls. funcctx->user_fctx = search; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; search = (KC_ENTRY *) funcctx->user_fctx; // If no current msg, try to get the next one. done = 1; #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("beginning run"))); #endif if (search->msg) { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Incrementing next from map %d -- %zu", search->next_map, search->msg->n_map_entry))); #endif // Case if we are using the external cursor running over kv map. // Ready the next if (search->msg->kv_map_file) { if ((kv_kbuf = kccurgetkey(search->kv_cur, &kv_ksiz, 1)) == NULL) { done = 1; kccurdel(search->kv_cur); kcdbendtran (search->kv_db, 1); if (!kcdbclose(search->kv_db)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error Closeing db: \"%s\"", kcecodename(kcdbecode(search->kv_db))))); } // Also need to free this. cloudflare__zone_time_bucket__free_unpacked(search->msg, NULL); search->msg = NULL; } else { done = 0; } } else { if (search->next_map >= search->msg->n_map_entry) { // Done with this msg -- move on to the next one. cloudflare__zone_time_bucket__free_unpacked(search->msg, NULL); search->msg = NULL; } else { done = 0; } } } if (search->db && !search->msg) { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Getting new buf -- %s", search->jump_key))); #endif if ((kbuf = kccurget(search->cur, &ksiz, &cvbuf, &vsiz, 1)) != NULL) { // Pull up the PB and expand it. search->msg = cloudflare__zone_time_bucket__unpack(NULL, vsiz, (const uint8_t *)cvbuf); if (search->msg == NULL) { // Something failed ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("error unpacking incoming message"))); done = 1; } else { // Does the buffer match the searched for string? // @TODO -- bound this? if (strstr(search->msg->db_key, search->jump_key)) { done = 0; search->next_map = 0; // And load the kvkc if needed. if (search->msg->kv_map_file) { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Switching to kvs %s", search->msg->kv_map_file))); #endif search->kv_db = kcdbnew(); if (!kcdbopen(search->kv_db, search->msg->kv_map_file, KCOWRITER)) { #ifdef CF_NO_DB_IS_ERR ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error opening db: \"%s\", \"%s\". Make sure that the map_name is valid.", search->msg->kv_map_file, kcecodename(kcdbecode(search->kv_db))))); #endif #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Error opening db: \"%s\", \"%s\". Make sure that the map_name is valid.", search->msg->kv_map_file, kcecodename(kcdbecode(search->kv_db))))); #endif done = 1; } else { kcdbbegintran (search->kv_db, 0); search->kv_cur = kcdbcursor(search->kv_db); kccurjump(search->kv_cur); if ((kv_kbuf = kccurgetkey(search->kv_cur, &kv_ksiz, 1)) == NULL) { done = 1; kccurdel(search->kv_cur); kcdbendtran (search->kv_db, 1); if (!kcdbclose(search->kv_db)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error Closeing db: \"%s\"", kcecodename(kcdbecode(search->kv_db))))); } } else { done = 0; } } } } else { done = 1; } } kcfree(kbuf); } else { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("no msg to find"))); #endif done = 1; } } #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Done? %d -- next buf -- %d", done, search->next_map))); #endif // Take the next itteration over the cursor. If the next is NULL or else not matching the resultid passed in // End. Otherwise, parse the value, populating the next row of the returning tuple. if (!done) { KC_ROW *out; Datum result; size_t size = sizeof(KC_ROW); out = (KC_ROW *)palloc(size); memset(out, '0', size); SET_VARSIZE(out, size); out->classification = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); out->doctype = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); out->pop = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); out->psource = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); out->key = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); strncpy(out->classification, search->msg->classification, MAX_KC_ROW_ENTRY); strncpy(out->doctype, search->msg->doctype, MAX_KC_ROW_ENTRY); strncpy(out->pop, search->msg->pop, MAX_KC_ROW_ENTRY); strncpy(out->psource, search->msg->psource, MAX_KC_ROW_ENTRY); if (search->msg->kv_map_file) { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("getting val from -- [%s]", search->msg->kv_map_file))); #endif snprintf(out->key, MAX_KC_ROW_ENTRY, "%s", kv_kbuf); out->value = kcdbincrint (search->kv_db, kv_kbuf, kv_ksiz, 0); if (out->value == INT64_MIN) { ereport(NOTICE, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("ERROR Getting val from key -- [%s], %s", kv_kbuf, kcecodename(kcdbecode(search->kv_db))))); } kcfree(kv_kbuf); } else { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Loading %s %ld", search->msg->map_entry[search->next_map]->key, search->msg->map_entry[search->next_map]->value))); #endif snprintf(out->key, MAX_KC_ROW_ENTRY, "%s", search->msg->map_entry[search->next_map]->key); out->value = search->msg->map_entry[search->next_map]->value; } result = PointerGetDatum(out); /* clean up (this is not really necessary) */ pfree(out->classification); pfree(out->doctype); pfree(out->pop); pfree(out->psource); pfree(out->key); pfree(out); // Remember that we are going to the next step. search->next_map++; SRF_RETURN_NEXT(funcctx, result); } else { /* do when there is no more left */ if (search->db) { kccurdel(search->cur); if (!kcdbclose(search->db)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error Closeing db: \"%s\"", kcecodename(kcdbecode(search->db))))); } if (search->msg != NULL) { cloudflare__zone_time_bucket__free_unpacked(search->msg, NULL); } pfree(search->jump_key); } pfree(search); #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Done with run"))); #endif // Don't delete db, this leads to segfaults. SRF_RETURN_DONE(funcctx); } }
/* * pg_lock_status - produce a view with one row per held or awaited lock mode */ Datum pg_lock_status(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; PG_Lock_Status *mystatus; LockData *lockData; PredicateLockData *predLockData; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* 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); /* build tupdesc for result tuples */ /* this had better match pg_locks view in system_views.sql */ tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath", BOOLOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the locking information that we will format and send * out as a result set. */ mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status)); funcctx->user_fctx = (void *) mystatus; mystatus->lockData = GetLockStatusData(); mystatus->currIdx = 0; mystatus->predLockData = GetPredicateLockStatusData(); mystatus->predLockIdx = 0; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); mystatus = (PG_Lock_Status *) funcctx->user_fctx; lockData = mystatus->lockData; while (mystatus->currIdx < lockData->nelements) { bool granted; LOCKMODE mode = 0; const char *locktypename; char tnbuf[32]; Datum values[NUM_LOCK_STATUS_COLUMNS]; bool nulls[NUM_LOCK_STATUS_COLUMNS]; HeapTuple tuple; Datum result; LockInstanceData *instance; instance = &(lockData->locks[mystatus->currIdx]); /* * Look to see if there are any held lock modes in this PROCLOCK. If * so, report, and destructively modify lockData so we don't report * again. */ granted = false; if (instance->holdMask) { for (mode = 0; mode < MAX_LOCKMODES; mode++) { if (instance->holdMask & LOCKBIT_ON(mode)) { granted = true; instance->holdMask &= LOCKBIT_OFF(mode); break; } } } /* * If no (more) held modes to report, see if PROC is waiting for a * lock on this lock. */ if (!granted) { if (instance->waitLockMode != NoLock) { /* Yes, so report it with proper mode */ mode = instance->waitLockMode; /* * We are now done with this PROCLOCK, so advance pointer to * continue with next one on next call. */ mystatus->currIdx++; } else { /* * Okay, we've displayed all the locks associated with this * PROCLOCK, proceed to the next one. */ mystatus->currIdx++; continue; } } /* * Form tuple with appropriate data. */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE) locktypename = LockTagTypeNames[instance->locktag.locktag_type]; else { snprintf(tnbuf, sizeof(tnbuf), "unknown %d", (int) instance->locktag.locktag_type); locktypename = tnbuf; } values[0] = CStringGetTextDatum(locktypename); switch ((LockTagType) instance->locktag.locktag_type) { case LOCKTAG_RELATION: case LOCKTAG_RELATION_EXTEND: values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1); values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2); nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; break; case LOCKTAG_PAGE: values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1); values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2); values[3] = UInt32GetDatum(instance->locktag.locktag_field3); nulls[4] = true; nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; break; case LOCKTAG_TUPLE: values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1); values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2); values[3] = UInt32GetDatum(instance->locktag.locktag_field3); values[4] = UInt16GetDatum(instance->locktag.locktag_field4); nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; break; case LOCKTAG_TRANSACTION: values[6] = TransactionIdGetDatum(instance->locktag.locktag_field1); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; break; case LOCKTAG_VIRTUALTRANSACTION: values[5] = VXIDGetDatum(instance->locktag.locktag_field1, instance->locktag.locktag_field2); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; break; case LOCKTAG_OBJECT: case LOCKTAG_USERLOCK: case LOCKTAG_ADVISORY: default: /* treat unknown locktags like OBJECT */ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1); values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2); values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3); values[9] = Int16GetDatum(instance->locktag.locktag_field4); nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; break; } values[10] = VXIDGetDatum(instance->backend, instance->lxid); if (instance->pid != 0) values[11] = Int32GetDatum(instance->pid); else nulls[11] = true; values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode)); values[13] = BoolGetDatum(granted); values[14] = BoolGetDatum(instance->fastpath); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } /* * Have returned all regular locks. Now start on the SIREAD predicate * locks. */ predLockData = mystatus->predLockData; if (mystatus->predLockIdx < predLockData->nelements) { PredicateLockTargetType lockType; PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]); SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]); Datum values[NUM_LOCK_STATUS_COLUMNS]; bool nulls[NUM_LOCK_STATUS_COLUMNS]; HeapTuple tuple; Datum result; mystatus->predLockIdx++; /* * Form tuple with appropriate data. */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); /* lock type */ lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag); values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]); /* lock target */ values[1] = GET_PREDICATELOCKTARGETTAG_DB(*predTag); values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*predTag); if (lockType == PREDLOCKTAG_TUPLE) values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag); else nulls[4] = true; if ((lockType == PREDLOCKTAG_TUPLE) || (lockType == PREDLOCKTAG_PAGE)) values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*predTag); else nulls[3] = true; /* these fields are targets for other types of locks */ nulls[5] = true; /* virtualxid */ nulls[6] = true; /* transactionid */ nulls[7] = true; /* classid */ nulls[8] = true; /* objid */ nulls[9] = true; /* objsubid */ /* lock holder */ values[10] = VXIDGetDatum(xact->vxid.backendId, xact->vxid.localTransactionId); if (xact->pid != 0) values[11] = Int32GetDatum(xact->pid); else nulls[11] = true; /* * Lock mode. Currently all predicate locks are SIReadLocks, which are * always held (never waiting) and have no fast path */ values[12] = CStringGetTextDatum("SIReadLock"); values[13] = BoolGetDatum(true); values[14] = BoolGetDatum(false); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } SRF_RETURN_DONE(funcctx); }
Datum json_object_keys(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; OkeysState state; int i; if (SRF_IS_FIRSTCALL()) { text *json = PG_GETARG_TEXT_P(0); JsonLexContext *lex = makeJsonLexContext(json, true); JsonSemAction sem; MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); state = palloc(sizeof(okeysState)); sem = palloc0(sizeof(jsonSemAction)); state->lex = lex; state->result_size = 256; state->result_count = 0; state->sent_count = 0; state->result = palloc(256 * sizeof(char *)); sem->semstate = (void *) state; sem->array_start = okeys_array_start; sem->scalar = okeys_scalar; sem->object_field_start = okeys_object_field_start; /* remainder are all NULL, courtesy of palloc0 above */ pg_parse_json(lex, sem); /* keys are now in state->result */ pfree(lex->strval->data); pfree(lex->strval); pfree(lex); pfree(sem); MemoryContextSwitchTo(oldcontext); funcctx->user_fctx = (void *) state; } funcctx = SRF_PERCALL_SETUP(); state = (OkeysState) funcctx->user_fctx; if (state->sent_count < state->result_count) { char *nxt = state->result[state->sent_count++]; SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt)); } /* cleanup to reduce or eliminate memory leaks */ for (i = 0; i < state->result_count; i++) pfree(state->result[i]); pfree(state->result); pfree(state); SRF_RETURN_DONE(funcctx); }
/* Function to return the list of grammar keywords */ Datum pg_get_keywords(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", CHAROID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "catdesc", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < NumScanKeywords) { char *values[3]; HeapTuple tuple; /* cast-away-const is ugly but alternatives aren't much better */ values[0] = (char *) ScanKeywords[funcctx->call_cntr].name; switch (ScanKeywords[funcctx->call_cntr].category) { case UNRESERVED_KEYWORD: values[1] = "U"; values[2] = _("unreserved"); break; case COL_NAME_KEYWORD: values[1] = "C"; values[2] = _("unreserved (cannot be function or type name)"); break; case TYPE_FUNC_NAME_KEYWORD: values[1] = "T"; values[2] = _("reserved (can be function or type name)"); break; case RESERVED_KEYWORD: values[1] = "R"; values[2] = _("reserved"); break; default: /* shouldn't be possible */ values[1] = NULL; values[2] = NULL; break; } tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } SRF_RETURN_DONE(funcctx); }
PGDLLEXPORT Datum one_to_one_dijkstra(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; uint32_t call_cntr; uint32_t max_calls; TupleDesc tuple_desc; /**************************************************************************/ /* MODIFY AS NEEDED */ /* */ General_path_element_t *result_tuples = 0; size_t result_count = 0; /* */ /**************************************************************************/ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /**********************************************************************/ /* MODIFY AS NEEDED */ // CREATE OR REPLACE FUNCTION pgr_dijkstra( // sql text, start_vids BIGINT, // end_vid BIGINT, // directed BOOLEAN default true, PGR_DBG("Calling process"); process( pgr_text2char(PG_GETARG_TEXT_P(0)), PG_GETARG_INT64(1), PG_GETARG_INT64(2), PG_GETARG_BOOL(3), PG_GETARG_BOOL(4), &result_tuples, &result_count); /* */ /**********************************************************************/ funcctx->max_calls = (uint32_t)result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); call_cntr = (uint32_t)funcctx->call_cntr; max_calls = (uint32_t)funcctx->max_calls; tuple_desc = funcctx->tuple_desc; result_tuples = (General_path_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) { HeapTuple tuple; Datum result; Datum *values; bool* nulls; /**********************************************************************/ /* MODIFY AS NEEDED */ // OUT seq INTEGER, // OUT path_seq INTEGER, // OUT node BIGINT, // OUT edge BIGINT, // OUT cost FLOAT, // OUT agg_cost FLOAT values = palloc(6 * sizeof(Datum)); nulls = palloc(6 * sizeof(bool)); size_t i; for (i = 0; i < 6; ++i) { nulls[i] = false; } // postgres starts counting from 1 values[0] = Int32GetDatum(call_cntr + 1); values[1] = Int32GetDatum(result_tuples[call_cntr].seq); values[2] = Int64GetDatum(result_tuples[call_cntr].node); values[3] = Int64GetDatum(result_tuples[call_cntr].edge); values[4] = Float8GetDatum(result_tuples[call_cntr].cost); values[5] = Float8GetDatum(result_tuples[call_cntr].agg_cost); /**********************************************************************/ tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { // cleanup if (result_tuples) free(result_tuples); SRF_RETURN_DONE(funcctx); } }
/* Clause 3.3.1.3 */ Datum MarketFeedFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i, j, n; int num_updated = 0; int rows_sent; int send_len = 0; int count = 0; int nitems_pq; int *p_tq; char *p_s; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; ArrayType *price_quote_p = PG_GETARG_ARRAYTYPE_P(0); char *status_submitted_p = (char *) PG_GETARG_TEXT_P(1); ArrayType *symbol_p = PG_GETARG_ARRAYTYPE_P(2); ArrayType *trade_qty = PG_GETARG_ARRAYTYPE_P(3); char *type_limit_buy_p = (char *) PG_GETARG_TEXT_P(4); char *type_limit_sell_p = (char *) PG_GETARG_TEXT_P(5); char *type_stop_loss_p = (char *) PG_GETARG_TEXT_P(6); enum mff1 { i_num_updated=0, i_send_len, i_symbol, i_trade_id, i_price_quote, i_trade_qty, i_trade_type }; Datum *transdatums_pq; int16 typlen_s; bool typbyval_s; char typalign_s; int16 typlen_tq; bool typbyval_tq; char typalign_tq; int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[7]; char nulls[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; char price_quote[S_PRICE_T_LEN + 1]; char status_submitted[ST_ID_LEN + 1]; char symbol[S_SYMB_LEN + 1]; char type_limit_buy[TT_ID_LEN + 1]; char type_limit_sell[TT_ID_LEN + 1]; char type_stop_loss[TT_ID_LEN + 1]; char *trade_id; char *req_price_quote; char *req_trade_type; char *req_trade_qty; /* * Prepare a values array for building the returned tuple. * This should be an array of C strings, which will * be processed later by the type input functions. */ values = (char **) palloc(sizeof(char *) * 7); values[i_num_updated] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_send_len] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); /* * FIXME: We don't know how many rows could be returned. The average * is supposed to be 4. Let's be prepared for 100, just to be safe. */ values[i_symbol] = (char *) palloc(((S_SYMB_LEN + 3) * 100 + 3) * sizeof(char)); values[i_trade_id] = (char *) palloc(((IDENT_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_price_quote] = (char *) palloc(((S_PRICE_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_qty] = (char *) palloc(((INTEGER_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_type] = (char *) palloc(((TT_ID_LEN + 3) * 100 + 3) * sizeof(char)); /* * This might be overkill since we always expect single dimensions * arrays. * Should probably check the count of all the arrays to make sure * they are the same... */ get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s, &typalign_s); p_s = ARR_DATA_PTR(symbol_p); get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq, &typalign_tq); p_tq = (int *) ARR_DATA_PTR(trade_qty); deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i', &transdatums_pq, NULL, &nitems_pq); strcpy(status_submitted, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(status_submitted_p)))); strcpy(type_limit_buy, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_buy_p)))); strcpy(type_limit_sell, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_sell_p)))); strcpy(type_stop_loss, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_stop_loss_p)))); #ifdef DEBUG dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); #endif /* 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); SPI_connect(); plan_queries(MFF1_statements); strcpy(values[i_symbol], "{"); strcpy(values[i_trade_id], "{"); strcpy(values[i_price_quote], "{"); strcpy(values[i_trade_type], "{"); strcpy(values[i_trade_qty], "{"); for (i = 0; i < nitems_pq; i++) { rows_sent = 0; strcpy(price_quote, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i]))); strcpy(symbol, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(p_s)))); /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("BEGIN;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: BEGIN not ok = %d", ret); } */ #ifdef DEBUG sprintf(sql, SQLMFF1_1, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i])), p_tq[i], symbol); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Float8GetDatum(atof(price_quote)); args[1] = Int64GetDatum(p_tq[i]); args[2] = CStringGetTextDatum(symbol); ret = SPI_execute_plan(MFF1_1, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[0].sql); } num_updated += SPI_processed; #ifdef DEBUG elog(NOTICE, "%d row(s) updated", num_updated); sprintf(sql, SQLMFF1_2, symbol, type_stop_loss, price_quote, type_limit_sell, price_quote, type_limit_buy, price_quote); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(symbol); args[1] = CStringGetTextDatum(type_stop_loss); args[2] = Float8GetDatum(atof(price_quote)); args[3] = CStringGetTextDatum(type_limit_sell); args[4] = args[2]; args[5] = CStringGetTextDatum(type_limit_buy); args[6] = args[2]; ret = SPI_execute_plan(MFF1_2, args, nulls, true, 0); if (ret != SPI_OK_SELECT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[1].sql); continue; } #ifdef DEBUG elog(NOTICE, "%d row(s) returned", SPI_processed); #endif /* DEBUG */ tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; n = SPI_processed; for (j = 0; j < n; j++) { tuple = tuptable->vals[j]; trade_id = SPI_getvalue(tuple, tupdesc, 1); req_price_quote = SPI_getvalue(tuple, tupdesc, 2); req_trade_type = SPI_getvalue(tuple, tupdesc, 3); req_trade_qty = SPI_getvalue(tuple, tupdesc, 4); #ifdef DEBUG elog(NOTICE, "trade_id = %s", trade_id); sprintf(sql, SQLMFF1_3, status_submitted, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(status_submitted); args[1] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_3, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[2].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_4, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_4, args, nulls, false, 0); if (ret != SPI_OK_DELETE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[3].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_5, trade_id, status_submitted); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[1] = CStringGetTextDatum(status_submitted); ret = SPI_execute_plan(MFF1_5, args, nulls, false, 0); if (ret != SPI_OK_INSERT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[4].sql); } ++rows_sent; #ifdef DEBUG elog(NOTICE, "%d row(s) sent", rows_sent); #endif /* DEBUG */ if (count > 0) { strcat(values[i_symbol], ","); strcat(values[i_trade_id], ","); strcat(values[i_price_quote], ","); strcat(values[i_trade_type], ","); strcat(values[i_trade_qty], ","); } strcat(values[i_symbol], symbol); strcat(values[i_trade_id], trade_id); strcat(values[i_price_quote], req_price_quote); strcat(values[i_trade_type], req_trade_type); strcat(values[i_trade_qty], req_trade_qty); ++count; } /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("COMMIT;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: COMMIT not ok = %d", ret); } */ send_len += rows_sent; p_s = att_addlength_pointer(p_s, typlen_s, p_s); p_s = (char *) att_align_nominal(p_s, typalign_s); } strcat(values[i_symbol], "}"); strcat(values[i_trade_id], "}"); strcat(values[i_price_quote], "}"); strcat(values[i_trade_qty], "}"); strcat(values[i_trade_type], "}"); sprintf(values[i_num_updated], "%d", num_updated); sprintf(values[i_send_len], "%d", send_len); funcctx->max_calls = 1; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; #ifdef DEBUG for (i = 0; i < 7; i++) { elog(NOTICE, "MFF1 OUT: %d %s", i, values[i]); } #endif /* DEBUG */ /* Build a tuple. */ tuple = BuildTupleFromCStrings(attinmeta, values); /* Make the tuple into a datum. */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* Do when there is no more left. */ SPI_finish(); SRF_RETURN_DONE(funcctx); } }
/* * pgdatabasev - produce a view of pgdatabase to include transient state */ Datum gp_pgdatabase__(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; Working_State *mystatus; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* 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); /* build tupdesc for result tuples */ /* this had better match pg_prepared_xacts view in system_views.sql */ tupdesc = CreateTemplateTupleDesc(5, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "dbid", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "isprimary", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "content", INT2OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "valid", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "definedprimary", BOOLOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the locking information that we will format and * send out as a result set. */ mystatus = (Working_State *) palloc(sizeof(Working_State)); funcctx->user_fctx = (void *) mystatus; mystatus->master = GetMasterSegment(); mystatus->standby = GetStandbySegment(); mystatus->segments = GetSegmentList(); mystatus->idx = 0; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); mystatus = (Working_State *) funcctx->user_fctx; while (mystatus->master || mystatus->standby || (mystatus->idx < list_length(mystatus->segments))) { Datum values[6]; bool nulls[6]; HeapTuple tuple; Datum result; Segment *current = NULL; if (mystatus->master) { current = mystatus->master; mystatus->master = NULL; } else if (mystatus->standby) { current = mystatus->standby; mystatus->standby = NULL; } else { current = list_nth(mystatus->segments, mystatus->idx); mystatus->idx++; } /* * Form tuple with appropriate data. */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); //values[0] = UInt16GetDatum(current->dbid); values[1] = current->standby ? false : true;; values[2] = UInt16GetDatum(current->segindex); values[3] = BoolGetDatum(true); values[4] = values[1]; tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } SRF_RETURN_DONE(funcctx); }
datum_t heap_page_items(PG_FUNC_ARGS) { bytea *raw_page = ARG_BYTEA_P(0); heap_page_items_state *inter_call_data = NULL; struct fcall_ctx *fctx; int raw_page_size; if (!superuser()) ereport(ERROR, (errcode(E_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); raw_page_size = VLA_SZ(raw_page) - VAR_HDR_SZ; if (SRF_IS_FIRSTCALL()) { struct tuple * tupdesc; struct mctx * mctx; if (raw_page_size < PAGE_HDR_SZ) ereport(ERROR, (errcode(E_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); fctx = SRF_FIRSTCALL_INIT(); mctx = mctx_switch(fctx->multi_call_memory_ctx); inter_call_data = palloc(sizeof(heap_page_items_state)); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); inter_call_data->tupd = tupdesc; inter_call_data->offset = FIRST_ITEM_ID; inter_call_data->page = VLA_DATA(raw_page); fctx->max_calls = PAGE_MAX_ITEM_ID(inter_call_data->page); fctx->user_fctx = inter_call_data; mctx_switch(mctx); } fctx = SRF_PERCALL_SETUP(); inter_call_data = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { page_p page = inter_call_data->page; struct heap_tuple * resultTuple; datum_t result; struct item_id * id; datum_t values[13]; bool nulls[13]; uint16 lp_offset; uint16 lp_flags; uint16 lp_len; memset(nulls, 0, sizeof(nulls)); /* Extract information from the line pointer */ id = PAGE_ITEM_ID(page, inter_call_data->offset); lp_offset = ITEMID_OFFSET(id); lp_flags = ITEMID_FLAGS(id); lp_len = ITEMID_LENGTH(id); values[0] = UINT16_TO_D(inter_call_data->offset); values[1] = UINT16_TO_D(lp_offset); values[2] = UINT16_TO_D(lp_flags); values[3] = UINT16_TO_D(lp_len); /* * We do just enough validity checking to make sure we don't reference * data outside the page passed to us. The page could be corrupt in * many other ways, but at least we won't crash. */ if (ITEMID_HAS_STORAGE(id) && lp_len >= sizeof(struct htup_header *) && lp_offset == MAX_ALIGN(lp_offset) && lp_offset + lp_len <= raw_page_size) { struct htup_header * tuphdr; int bits_len; /* Extract information from the tuple header */ tuphdr = (struct htup_header *) PAGE_GET_ITEM(page, id); values[4] = UINT32_TO_D(HTH_GET_XMIN(tuphdr)); values[5] = UINT32_TO_D(HTH_GET_XMAX(tuphdr)); values[6] = UINT32_TO_D(HTH_GET_RAW_CMDID(tuphdr)); /* shared with xvac */ values[7] = PTR_TO_D(&tuphdr->t_ctid); values[8] = UINT32_TO_D(tuphdr->t_infomask2); values[9] = UINT32_TO_D(tuphdr->t_infomask); values[10] = UINT8_TO_D(tuphdr->t_hoff); /* * We already checked that the item as is completely within the * raw page passed to us, with the length given in the line * pointer.. Let's check that t_hoff doesn't point over lp_len, * before using it to access t_bits and oid. */ if (tuphdr->t_hoff >= sizeof(struct htup_header *) && tuphdr->t_hoff <= lp_len) { if (tuphdr->t_infomask & HEAP_HAS_NULL) { bits_len = tuphdr->t_hoff - (((char *) tuphdr->t_bits) -((char *) tuphdr)); values[11] = CStringGetTextDatum( bits_to_text(tuphdr->t_bits, bits_len * 8)); } else nulls[11] = true; if (tuphdr->t_infomask & HEAP_HAS_OID) values[12] = HTH_GET_OID(tuphdr); else nulls[12] = true; } else { nulls[11] = true; nulls[12] = true; } } else { /* * The line pointer is not used, or it's invalid. Set the rest of * the fields to NULL */ int i; for (i = 4; i <= 12; i++) nulls[i] = true; } /* Build and return the result tuple. */ resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); result = HeapTupleGetDatum(resultTuple); inter_call_data->offset++; SRF_RETURN_NEXT(fctx, result); } else SRF_RETURN_DONE(fctx); }
Datum heap_page_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); heap_page_items_state *inter_call_data = NULL; FuncCallContext *fctx; int raw_page_size; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); raw_page_size = VARSIZE(raw_page) - VARHDRSZ; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext mctx; if (raw_page_size < SizeOfPageHeaderData) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); inter_call_data = palloc(sizeof(heap_page_items_state)); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); inter_call_data->tupd = tupdesc; inter_call_data->offset = FirstOffsetNumber; inter_call_data->page = VARDATA(raw_page); fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page); fctx->user_fctx = inter_call_data; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); inter_call_data = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { Page page = inter_call_data->page; HeapTuple resultTuple; Datum result; ItemId id; Datum values[13]; bool nulls[13]; uint16 lp_offset; uint16 lp_flags; uint16 lp_len; memset(nulls, 0, sizeof(nulls)); /* Extract information from the line pointer */ id = PageGetItemId(page, inter_call_data->offset); lp_offset = ItemIdGetOffset(id); lp_flags = ItemIdGetFlags(id); lp_len = ItemIdGetLength(id); values[0] = UInt16GetDatum(inter_call_data->offset); values[1] = UInt16GetDatum(lp_offset); values[2] = UInt16GetDatum(lp_flags); values[3] = UInt16GetDatum(lp_len); /* * We do just enough validity checking to make sure we don't reference * data outside the page passed to us. The page could be corrupt in * many other ways, but at least we won't crash. */ if (ItemIdHasStorage(id) && lp_len >= sizeof(HeapTupleHeader) && lp_offset == MAXALIGN(lp_offset) && lp_offset + lp_len <= raw_page_size) { HeapTupleHeader tuphdr; int bits_len; /* Extract information from the tuple header */ tuphdr = (HeapTupleHeader) PageGetItem(page, id); values[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr)); values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr)); values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */ values[7] = PointerGetDatum(&tuphdr->t_ctid); values[8] = UInt32GetDatum(tuphdr->t_infomask2); values[9] = UInt32GetDatum(tuphdr->t_infomask); values[10] = UInt8GetDatum(tuphdr->t_hoff); /* * We already checked that the item as is completely within the * raw page passed to us, with the length given in the line * pointer.. Let's check that t_hoff doesn't point over lp_len, * before using it to access t_bits and oid. */ if (tuphdr->t_hoff >= sizeof(HeapTupleHeader) && tuphdr->t_hoff <= lp_len) { if (tuphdr->t_infomask & HEAP_HASNULL) { bits_len = tuphdr->t_hoff - (((char *) tuphdr->t_bits) -((char *) tuphdr)); values[11] = CStringGetTextDatum( bits_to_text(tuphdr->t_bits, bits_len * 8)); } else nulls[11] = true; if (tuphdr->t_infomask & HEAP_HASOID) values[12] = HeapTupleHeaderGetOid(tuphdr); else nulls[12] = true; } else { nulls[11] = true; nulls[12] = true; } } else { /* * The line pointer is not used, or it's invalid. Set the rest of * the fields to NULL */ int i; for (i = 4; i <= 12; i++) nulls[i] = true; } /* Build and return the result tuple. */ resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); result = HeapTupleGetDatum(resultTuple); inter_call_data->offset++; SRF_RETURN_NEXT(fctx, result); } else SRF_RETURN_DONE(fctx); }
Datum #else // _MSC_VER PGDLLEXPORT Datum #endif johnson(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; uint32_t call_cntr; uint32_t max_calls; TupleDesc tuple_desc; /**************************************************************************/ /* MODIFY AS NEEDED */ /* */ Matrix_cell_t *result_tuples = 0; size_t result_count = 0; /* */ /**************************************************************************/ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /*********************************************************************/ /* MODIFY AS NEEDED */ // CREATE OR REPLACE FUNCTION pgr_johnson( // edges_sql TEXT, // directed BOOLEAN, PGR_DBG("Calling process"); process( pgr_text2char(PG_GETARG_TEXT_P(0)), PG_GETARG_BOOL(1), &result_tuples, &result_count); /* */ /*********************************************************************/ funcctx->max_calls = (uint32_t)result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; result_tuples = (Matrix_cell_t*) funcctx->user_fctx; if (call_cntr < max_calls) { HeapTuple tuple; Datum result; Datum *values; bool* nulls; /*********************************************************************/ /* MODIFY AS NEEDED */ // OUT seq BIGINT, // OUT from_vid BIGINT, // OUT to_vid BIGINT, // OUT cost float) values = palloc(3 * sizeof(Datum)); nulls = palloc(3 * sizeof(bool)); // postgres starts counting from 1 values[0] = Int64GetDatum(result_tuples[call_cntr].from_vid); nulls[0] = false; values[1] = Int64GetDatum(result_tuples[call_cntr].to_vid); nulls[1] = false; values[2] = Float8GetDatum(result_tuples[call_cntr].cost); nulls[2] = false; /*********************************************************************/ tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { // cleanup if (result_tuples) free(result_tuples); SRF_RETURN_DONE(funcctx); } }
Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); RangeVar *relrv; Datum result; char *values[BTPAGEITEMS_NCOLUMNS]; BTPageOpaque opaque; HeapTuple tuple; ItemId id; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs = NULL; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); if (blkno == 0) elog(ERROR, "Block 0 is a meta page."); if (SRF_IS_FIRSTCALL()) { fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE); uargs->offset = FirstOffsetNumber; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); uargs->rel = relation_openrv(relrv, AccessShareLock); CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno); uargs->buffer = ReadBuffer(uargs->rel, blkno); if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel)) elog(ERROR, "bt_page_items() can be used only on b-tree index."); uargs->page = BufferGetPage(uargs->buffer); opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "bt_page_items(): this page is deleted."); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { IndexTuple itup; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "Invalid ItemId."); itup = (IndexTuple) PageGetItem(uargs->page, id); { int j = 0; BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)); values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); { int off; char *dump; char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dump = palloc(IndexTupleSize(itup) * 3); memset(dump, 0, IndexTupleSize(itup) * 3); for (off = 0; off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); off++) { if (dump[0] == '\0') sprintf(dump, "%02x", *(ptr + off) & 0xff); else { char buf[4]; sprintf(buf, " %02x", *(ptr + off) & 0xff); strcat(dump, buf); } } values[j] = dump; } tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values); result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple); } uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { ReleaseBuffer(uargs->buffer); relation_close(uargs->rel, AccessShareLock); SRF_RETURN_DONE(fctx); } }