Datum convert_to_UTF8(PG_FUNCTION_ARGS) { // things we need to deal with constructing our composite type TupleDesc tupdesc; Datum values[3]; bool nulls[3]; HeapTuple tuple; // for char_set_detect function returns text *encoding = NULL; text *lang = NULL; int32_t confidence = 0; UErrorCode status = U_ZERO_ERROR; // output buffer for conversion to Unicode UChar* uBuf = NULL; int32_t uBuf_len = 0; // output of this function text *text_out; bool converted = false; bool dropped_bytes = false; bool dropped_bytes_toU = false; bool dropped_bytes_fromU = false; // temporary buffer for converted string char* converted_buf = NULL; // input args const text *buffer = PG_GETARG_TEXT_P(0); const bool force = PG_GETARG_BOOL(1); // C string of text* buffer const char* cbuffer = NULL; int cbuffer_len = 0; // Convert output values into a PostgreSQL composite 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.\n"))); // BlessTupleDesc for Datums BlessTupleDesc(tupdesc); // return if string to convert is NULL if (NULL == buffer) { // return input string, // converted to true, // dropped_bytes to false text_out = (text *) buffer; converted = true; dropped_bytes = false; } else { // extract string from text* to C string cbuffer = text_to_cstring(buffer); cbuffer_len = strlen(cbuffer); // bail on zero-length strings // return if cbuffer has zero length or contains a blank space if ((0 == cbuffer_len) || (0 == strcmp("", cbuffer))) { text_out = (text *) buffer; converted = true; dropped_bytes = false; } else { // UTF8 output can be up to 6 bytes per input byte // palloc0 allocates and zeros bytes in array int32_t converted_buf_len = cbuffer_len * 6 * sizeof(char); converted_buf = (char *) palloc0(converted_buf_len); // int32_t converted_len = 0; // detect encoding with ICU status = detect_ICU(buffer, &encoding, &lang, &confidence); ereport(DEBUG1, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("ICU detection status: %d\n", status))); ereport(DEBUG1, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("Detected encoding: %s, language: %s, confidence: %d\n", text_to_cstring(encoding), text_to_cstring(lang), confidence))); // return without attempting a conversion if UTF8 is detected if ( (0 == strcmp("UTF-8", text_to_cstring(encoding))) || (0 == strcmp("utf-8", text_to_cstring(encoding))) || (0 == strcmp("UTF8", text_to_cstring(encoding))) || (0 == strcmp("utf8", text_to_cstring(encoding))) ) { ereport(DEBUG1, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("ICU detected %s. No conversion necessary.\n", text_to_cstring(encoding)))); text_out = (text *) buffer; converted = true; dropped_bytes = false; } else { // ICU uses UTF16 internally, so need to convert to Unicode first // then convert to UTF8 if (U_SUCCESS(status)) status = convert_to_unicode(buffer, (const text*) encoding, &uBuf, (int32_t*) &uBuf_len, force, &dropped_bytes_toU); if (U_SUCCESS(status)) status = convert_to_utf8((const UChar*) uBuf, uBuf_len, &converted_buf, (int32_t*) &converted_buf_len, force, &dropped_bytes_fromU); if (U_SUCCESS(status)) { text_out = cstring_to_text(converted_buf); converted = true; dropped_bytes = (dropped_bytes_toU || dropped_bytes_fromU); } else { ereport(WARNING, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("ICU conversion failed - returning original input"))); text_out = (text *) buffer; converted = false; dropped_bytes = false; } } // already UTF8 } // zero-length string } // return if buffer is NULL values[0] = PointerGetDatum(text_out); values[1] = BoolGetDatum(converted); values[2] = BoolGetDatum(dropped_bytes); // check if pointers are still NULL; if so Datum is NULL and // confidence is meaningless (also NULL) (text_out == NULL || ! VARSIZE_ANY_EXHDR(text_out)) ? (nulls[0] = true) : (nulls[0] = false); // converted will never be NULL nulls[1] = false; nulls[2] = false; // build tuple from datum array tuple = heap_form_tuple(tupdesc, values, nulls); // cleanup if (NULL != encoding) pfree((void *) encoding); if (NULL != lang) pfree((void *) lang); if (NULL != cbuffer) pfree((void *) cbuffer); if (NULL != converted_buf) pfree((void *) converted_buf); PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); }
/* * 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); }
/* * stat a file */ Datum pg_stat_file(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); char *filename; struct stat fst; Datum values[6]; bool isnull[6]; HeapTuple tuple; TupleDesc tupdesc; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to get file information")))); filename = convert_and_check_filename(filename_t); if (stat(filename, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", filename))); /* * This record type had better match the output parameters declared for me * in pg_proc.h. */ tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "size", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "access", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "modification", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "change", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "creation", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "isdir", BOOLOID, -1, 0); BlessTupleDesc(tupdesc); memset(isnull, false, sizeof(isnull)); values[0] = Int64GetDatum((int64) fst.st_size); values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime)); values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime)); /* Unix has file status change time, while Win32 has creation time */ #if !defined(WIN32) && !defined(__CYGWIN__) values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime)); isnull[4] = true; #else isnull[3] = true; values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime)); #endif values[5] = BoolGetDatum(S_ISDIR(fst.st_mode)); tuple = heap_form_tuple(tupdesc, values, isnull); pfree(filename); PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); }
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); } }
/* * SQL function json_populate_recordset * * set fields in a set of records from the argument json, * which must be an array of objects. * * similar to json_populate_record, but the tuple-building code * is pushed down into the semantic action handlers so it's done * per object in the array. */ Datum json_populate_recordset(PG_FUNCTION_ARGS) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); text *json = PG_GETARG_TEXT_P(1); bool use_json_as_text = PG_GETARG_BOOL(2); ReturnSetInfo *rsi; MemoryContext old_cxt; Oid tupType; int32 tupTypmod; HeapTupleHeader rec; TupleDesc tupdesc; RecordIOData *my_extra; int ncolumns; JsonLexContext *lex; JsonSemAction sem; PopulateRecordsetState state; if (!type_is_rowtype(argtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("first argument must be a rowtype"))); rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that " "cannot accept a set"))); rsi->returnMode = SFRM_Materialize; /* * get the tupdesc from the result set info - it must be a record type * because we already checked that arg1 is a record type. */ (void) get_call_result_type(fcinfo, NULL, &tupdesc); state = palloc0(sizeof(populateRecordsetState)); sem = palloc0(sizeof(jsonSemAction)); /* make these in a sufficiently long-lived memory context */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); state->ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(state->ret_tdesc); state->tuple_store = tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize, false, work_mem); MemoryContextSwitchTo(old_cxt); /* if the json is null send back an empty set */ if (PG_ARGISNULL(1)) PG_RETURN_NULL(); if (PG_ARGISNULL(0)) rec = NULL; else rec = PG_GETARG_HEAPTUPLEHEADER(0); tupType = tupdesc->tdtypeid; tupTypmod = tupdesc->tdtypmod; ncolumns = tupdesc->natts; lex = makeJsonLexContext(json, true); /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } sem->semstate = (void *) state; sem->array_start = populate_recordset_array_start; sem->array_element_start = populate_recordset_array_element_start; sem->scalar = populate_recordset_scalar; sem->object_field_start = populate_recordset_object_field_start; sem->object_field_end = populate_recordset_object_field_end; sem->object_start = populate_recordset_object_start; sem->object_end = populate_recordset_object_end; state->lex = lex; state->my_extra = my_extra; state->rec = rec; state->use_json_as_text = use_json_as_text; state->fn_mcxt = fcinfo->flinfo->fn_mcxt; pg_parse_json(lex, sem); rsi->setResult = state->tuple_store; rsi->setDesc = state->ret_tdesc; PG_RETURN_NULL(); }
/* ---------------------------------------------------------------- * ExecInitFunctionScan * ---------------------------------------------------------------- */ FunctionScanState * ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) { FunctionScanState *scanstate; RangeTblEntry *rte; Oid funcrettype; TypeFuncClass functypclass; TupleDesc tupdesc = NULL; /* * FunctionScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new ScanState for node */ scanstate = makeNode(FunctionScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); #define FUNCTIONSCAN_NSLOTS 2 /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &scanstate->ss.ps); ExecInitScanTupleSlot(estate, &scanstate->ss); /* * initialize child expressions */ scanstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->scan.plan.targetlist, (PlanState *) scanstate); scanstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); /* Check if targetlist or qual contains a var node referencing the ctid column */ scanstate->cdb_want_ctid = contain_ctid_var_reference(&node->scan); ItemPointerSet(&scanstate->cdb_fake_ctid, 0, 0); ItemPointerSet(&scanstate->cdb_mark_ctid, 0, 0); /* * get info about function */ rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); Assert(rte->rtekind == RTE_FUNCTION); /* * Now determine if the function returns a simple or composite type, and * build an appropriate tupdesc. */ functypclass = get_expr_result_type(rte->funcexpr, &funcrettype, &tupdesc); if (functypclass == TYPEFUNC_COMPOSITE) { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); /* Must copy it out of typcache for safety */ tupdesc = CreateTupleDescCopy(tupdesc); } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ char *attname = strVal(linitial(rte->eref->colnames)); tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, attname, funcrettype, -1, 0); } else if (functypclass == TYPEFUNC_RECORD) { tupdesc = BuildDescFromLists(rte->eref->colnames, rte->funccoltypes, rte->funccoltypmods); } else { /* crummy error message, but parser should have caught this */ elog(ERROR, "function in FROM has unsupported return type"); } /* * For RECORD results, make sure a typmod has been assigned. (The * function should do this for itself, but let's cover things in case it * doesn't.) */ BlessTupleDesc(tupdesc); scanstate->tupdesc = tupdesc; ExecAssignScanType(&scanstate->ss, tupdesc); /* * Other node-specific setup */ scanstate->tuplestorestate = NULL; scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr, (PlanState *) scanstate); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); initGpmonPktForFunctionScan((Plan *)node, &scanstate->ss.ps.gpmon_pkt, estate); if (gp_resqueue_memory_policy != RESQUEUE_MEMORY_POLICY_NONE) { SPI_ReserveMemory(((Plan *)node)->operatorMemKB * 1024L); } return scanstate; }
/* ------------------------------------------------ * hash_bitmap_info() * * Get bitmap information for a particular overflow page * * Usage: SELECT * FROM hash_bitmap_info('con_hash_index'::regclass, 5); * ------------------------------------------------ */ Datum hash_bitmap_info(PG_FUNCTION_ARGS) { Oid indexRelid = PG_GETARG_OID(0); uint64 ovflblkno = PG_GETARG_INT64(1); HashMetaPage metap; Buffer metabuf, mapbuf; BlockNumber bitmapblkno; Page mappage; bool bit = false; TupleDesc tupleDesc; Relation indexRel; uint32 ovflbitno; int32 bitmappage, bitmapbit; HeapTuple tuple; int i, j; Datum values[3]; bool nulls[3]; uint32 *freep; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); indexRel = index_open(indexRelid, AccessShareLock); if (!IS_HASH(indexRel)) elog(ERROR, "relation \"%s\" is not a hash index", RelationGetRelationName(indexRel)); if (RELATION_IS_OTHER_TEMP(indexRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (ovflblkno >= RelationGetNumberOfBlocks(indexRel)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("block number " UINT64_FORMAT " is out of range for relation \"%s\"", ovflblkno, RelationGetRelationName(indexRel)))); /* Read the metapage so we can determine which bitmap page to use */ metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); metap = HashPageGetMeta(BufferGetPage(metabuf)); /* * Reject attempt to read the bit for a metapage or bitmap page; this is * only meaningful for overflow pages. */ if (ovflblkno == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid overflow block number %u", (BlockNumber) ovflblkno))); for (i = 0; i < metap->hashm_nmaps; i++) if (metap->hashm_mapp[i] == ovflblkno) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid overflow block number %u", (BlockNumber) ovflblkno))); /* * Identify overflow bit number. This will error out for primary bucket * pages, and we've already rejected the metapage and bitmap pages above. */ ovflbitno = _hash_ovflblkno_to_bitno(metap, (BlockNumber) ovflblkno); bitmappage = ovflbitno >> BMPG_SHIFT(metap); bitmapbit = ovflbitno & BMPG_MASK(metap); if (bitmappage >= metap->hashm_nmaps) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid overflow block number %u", (BlockNumber) ovflblkno))); bitmapblkno = metap->hashm_mapp[bitmappage]; _hash_relbuf(indexRel, metabuf); /* Check the status of bitmap bit for overflow page */ mapbuf = _hash_getbuf(indexRel, bitmapblkno, HASH_READ, LH_BITMAP_PAGE); mappage = BufferGetPage(mapbuf); freep = HashPageGetBitmap(mappage); bit = ISSET(freep, bitmapbit) != 0; _hash_relbuf(indexRel, mapbuf); index_close(indexRel, AccessShareLock); /* 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); MemSet(nulls, 0, sizeof(nulls)); j = 0; values[j++] = Int64GetDatum((int64) bitmapblkno); values[j++] = Int32GetDatum(bitmapbit); values[j++] = BoolGetDatum(bit); tuple = heap_form_tuple(tupleDesc, values, nulls); PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); }
/* * 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 sampleNewTopics(PG_FUNCTION_ARGS) { int32 i, widx, wtopic, rtopic; ArrayType * doc_arr = PG_GETARG_ARRAYTYPE_P(0); ArrayType * topics_arr = PG_GETARG_ARRAYTYPE_P(1); ArrayType * topic_d_arr = PG_GETARG_ARRAYTYPE_P(2); ArrayType * global_count_arr = PG_GETARG_ARRAYTYPE_P(3); ArrayType * topic_counts_arr = PG_GETARG_ARRAYTYPE_P(4); int32 num_topics = PG_GETARG_INT32(5); int32 dsize = PG_GETARG_INT32(6); float8 alpha = PG_GETARG_FLOAT8(7); float8 eta = PG_GETARG_FLOAT8(8); if (ARR_NULLBITMAP(doc_arr) || ARR_NDIM(doc_arr) != 1 || ARR_ELEMTYPE(doc_arr) != INT4OID || ARR_NDIM(topics_arr) != 1 || ARR_ELEMTYPE(topics_arr) != INT4OID || ARR_NULLBITMAP(topic_d_arr) || ARR_NDIM(topic_d_arr) != 1 || ARR_ELEMTYPE(topic_d_arr) != INT4OID || ARR_NULLBITMAP(global_count_arr) || ARR_NDIM(global_count_arr) != 1 || ARR_ELEMTYPE(global_count_arr) != INT4OID || ARR_NULLBITMAP(topic_counts_arr) || ARR_NDIM(topic_counts_arr) != 1 || ARR_ELEMTYPE(topic_counts_arr) != INT4OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); // the document array int32 * doc = (int32 *)ARR_DATA_PTR(doc_arr); int32 len = ARR_DIMS(doc_arr)[0]; // array giving topic assignment to each word in document int32 * topics = (int32 *)ARR_DATA_PTR(topics_arr); // distribution of topics in document int32 * topic_d = (int32 *)ARR_DATA_PTR(topic_d_arr); // the word-topic count matrix int32 * global_count = (int32 *)ARR_DATA_PTR(global_count_arr); // total number of words assigned to each topic in the whole corpus int32 * topic_counts = (int32 *)ARR_DATA_PTR(topic_counts_arr); ArrayType * ret_topics_arr, * ret_topic_d_arr; int32 * ret_topics, * ret_topic_d; Datum * arr1 = palloc0(len * sizeof(Datum)); ret_topics_arr = construct_array(arr1,len,INT4OID,4,true,'i'); ret_topics = (int32 *)ARR_DATA_PTR(ret_topics_arr); Datum * arr2 = palloc0(num_topics * sizeof(Datum)); ret_topic_d_arr = construct_array(arr2,num_topics,INT4OID,4,true,'i'); ret_topic_d = (int32 *)ARR_DATA_PTR(ret_topic_d_arr); for (i=0; i!=len; i++) { widx = doc[i]; if (widx < 1 || widx > dsize) ereport (ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); wtopic = topics[i]; rtopic = sampleTopic(num_topics,widx,wtopic,global_count, topic_d,topic_counts,alpha,eta); // <sampleNewTopics error checking> ret_topics[i] = rtopic; ret_topic_d[rtopic-1]++; } Datum values[2]; values[0] = PointerGetDatum(ret_topics_arr); values[1] = PointerGetDatum(ret_topic_d_arr); TupleDesc tuple; if (get_call_result_type(fcinfo, NULL, &tuple) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode( ERRCODE_FEATURE_NOT_SUPPORTED ), errmsg( "function returning record called in context " "that cannot accept type record" ))); tuple = BlessTupleDesc(tuple); bool * isnulls = palloc0(2 * sizeof(bool)); HeapTuple ret = heap_form_tuple(tuple, values, isnulls); if (isnulls[0] || isnulls[1]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" produced null results", format_procedure(fcinfo->flinfo->fn_oid),i))); PG_RETURN_DATUM(HeapTupleGetDatum(ret)); }
Datum pg_control_init(PG_FUNCTION_ARGS) { Datum values[12]; bool nulls[12]; TupleDesc tupdesc; HeapTuple htup; ControlFileData *ControlFile; bool crc_ok; /* * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ tupdesc = CreateTemplateTupleDesc(12); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "float4_pass_by_value", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float8_pass_by_value", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "data_page_checksum_version", INT4OID, -1, 0); tupdesc = BlessTupleDesc(tupdesc); /* read the control file */ ControlFile = get_controlfile(DataDir, NULL, &crc_ok); if (!crc_ok) ereport(ERROR, (errmsg("calculated CRC checksum does not match value stored in file"))); values[0] = Int32GetDatum(ControlFile->maxAlign); nulls[0] = false; values[1] = Int32GetDatum(ControlFile->blcksz); nulls[1] = false; values[2] = Int32GetDatum(ControlFile->relseg_size); nulls[2] = false; values[3] = Int32GetDatum(ControlFile->xlog_blcksz); nulls[3] = false; values[4] = Int32GetDatum(ControlFile->xlog_seg_size); nulls[4] = false; values[5] = Int32GetDatum(ControlFile->nameDataLen); nulls[5] = false; values[6] = Int32GetDatum(ControlFile->indexMaxKeys); nulls[6] = false; values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size); nulls[7] = false; values[8] = Int32GetDatum(ControlFile->loblksize); nulls[8] = false; values[9] = BoolGetDatum(ControlFile->float4ByVal); nulls[9] = false; values[10] = BoolGetDatum(ControlFile->float8ByVal); nulls[10] = false; values[11] = Int32GetDatum(ControlFile->data_checksum_version); nulls[11] = false; htup = heap_form_tuple(tupdesc, values, nulls); PG_RETURN_DATUM(HeapTupleGetDatum(htup)); }
Datum pg_control_checkpoint(PG_FUNCTION_ARGS) { Datum values[19]; bool nulls[19]; TupleDesc tupdesc; HeapTuple htup; ControlFileData *ControlFile; XLogSegNo segno; char xlogfilename[MAXFNAMELEN]; bool crc_ok; /* * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ tupdesc = CreateTemplateTupleDesc(18); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_lsn", LSNOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "redo_lsn", LSNOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_wal_file", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "timeline_id", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "prev_timeline_id", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "full_page_writes", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "next_xid", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_oid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_multixact_id", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multi_offset", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "oldest_xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid_dbid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_active_xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_multi_xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_dbid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_commit_ts_xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 17, "newest_commit_ts_xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 18, "checkpoint_time", TIMESTAMPTZOID, -1, 0); tupdesc = BlessTupleDesc(tupdesc); /* Read the control file. */ ControlFile = get_controlfile(DataDir, NULL, &crc_ok); if (!crc_ok) ereport(ERROR, (errmsg("calculated CRC checksum does not match value stored in file"))); /* * Calculate name of the WAL file containing the latest checkpoint's REDO * start point. */ XLByteToSeg(ControlFile->checkPointCopy.redo, segno, wal_segment_size); XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno, wal_segment_size); /* Populate the values and null arrays */ values[0] = LSNGetDatum(ControlFile->checkPoint); nulls[0] = false; values[1] = LSNGetDatum(ControlFile->checkPointCopy.redo); nulls[1] = false; values[2] = CStringGetTextDatum(xlogfilename); nulls[2] = false; values[3] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID); nulls[3] = false; values[4] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID); nulls[4] = false; values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites); nulls[5] = false; values[6] = CStringGetTextDatum(psprintf("%u:%u", ControlFile->checkPointCopy.nextXidEpoch, ControlFile->checkPointCopy.nextXid)); nulls[6] = false; values[7] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid); nulls[7] = false; values[8] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti); nulls[8] = false; values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset); nulls[9] = false; values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid); nulls[10] = false; values[11] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB); nulls[11] = false; values[12] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid); nulls[12] = false; values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti); nulls[13] = false; values[14] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB); nulls[14] = false; values[15] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid); nulls[15] = false; values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid); nulls[16] = false; values[17] = TimestampTzGetDatum( time_t_to_timestamptz(ControlFile->checkPointCopy.time)); nulls[17] = false; htup = heap_form_tuple(tupdesc, values, nulls); PG_RETURN_DATUM(HeapTupleGetDatum(htup)); }
Datum text_unpivot(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; unpivot_fctx *fctx; MemoryContext oldcontext; Datum d[2]; bool isna[2]; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; ArrayType *labels; ArrayType *data; Oid eltype1; Oid eltype2; /* see if we were given an explicit step size */ if (PG_NARGS() != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of parameters != 2"))); /* * Type check inputs: * 0: label text[] * 1: value anyarray */ eltype1 = get_fn_expr_argtype(fcinfo->flinfo, 0); eltype2 = get_fn_expr_argtype(fcinfo->flinfo, 1); if (!OidIsValid(eltype1)) elog(ERROR, "could not determine data type of input 'label'"); if (!OidIsValid(eltype2)) elog(ERROR, "could not determine data type of input 'value'"); /* Strict function, return null on null input */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); /* 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"))); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* allocate memory for user context */ fctx = (unpivot_fctx *) palloc(sizeof(unpivot_fctx)); /* Use fctx to keep state from call to call */ labels = PG_GETARG_ARRAYTYPE_P(0); data = PG_GETARG_ARRAYTYPE_P(1); array_loop(labels, ARR_LBOUND(labels)[0], &fctx->label_iter); array_loop(data, ARR_LBOUND(labels)[0], &fctx->data_iter); 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 = (unpivot_fctx*) funcctx->user_fctx; if (array_next(&fctx->label_iter, &d[0], &isna[0])) { HeapTuple tuple; array_next(&fctx->data_iter, &d[1], &isna[1]); tuple = heap_form_tuple(funcctx->tuple_desc, d, isna); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { SRF_RETURN_DONE(funcctx); } }
/* * Similar function exec_get_datum_type is in 9nth line */ static Oid exec_get_datum_type(PLpgSQL_execstate *estate, PLpgSQL_datum *datum) { Oid typoid = InvalidOid; switch (datum->dtype) { case PLPGSQL_DTYPE_VAR: typoid = ((PLpgSQL_var *) datum)->datatype->typoid; break; case PLPGSQL_DTYPE_ROW: typoid = ((PLpgSQL_row *) datum)->rowtupdesc->tdtypeid; break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *rec = (PLpgSQL_rec *) datum; if (!HeapTupleIsValid(rec->tup)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("record \"%s\" is not assigned yet", rec->refname), errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); Assert(rec->tupdesc != NULL); /* Make sure we have a valid type/typmod setting */ BlessTupleDesc(rec->tupdesc); typoid = rec->tupdesc->tdtypeid; } break; case PLPGSQL_DTYPE_RECFIELD: { PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum; PLpgSQL_rec *rec; int fno; rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]); if (!HeapTupleIsValid(rec->tup)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("record \"%s\" is not assigned yet", rec->refname), errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); if (fno == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("record \"%s\" has no field \"%s\"", rec->refname, recfield->fieldname))); typoid = SPI_gettypeid(rec->tupdesc, fno); } break; case PLPGSQL_DTYPE_TRIGARG: typoid = TEXTOID; break; } return typoid; }
Datum pg_stat_get_archiver(PG_FUNCTION_ARGS) { TupleDesc tupdesc; Datum values[7]; bool nulls[7]; PgStat_ArchiverStats *archiver_stats; /* Initialise values and NULL flags arrays */ MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); /* Initialise attributes information in the tuple descriptor */ tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "archived_count", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "last_archived_wal", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "last_archived_time", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "failed_count", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "last_failed_wal", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "last_failed_time", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stats_reset", TIMESTAMPTZOID, -1, 0); BlessTupleDesc(tupdesc); /* Get statistics about the archiver process */ archiver_stats = pgstat_fetch_stat_archiver(); /* Fill values and NULLs */ values[0] = Int64GetDatum(archiver_stats->archived_count); if (*(archiver_stats->last_archived_wal) == '\0') nulls[1] = true; else values[1] = CStringGetTextDatum(archiver_stats->last_archived_wal); if (archiver_stats->last_archived_timestamp == 0) nulls[2] = true; else values[2] = TimestampTzGetDatum(archiver_stats->last_archived_timestamp); values[3] = Int64GetDatum(archiver_stats->failed_count); if (*(archiver_stats->last_failed_wal) == '\0') nulls[4] = true; else values[4] = CStringGetTextDatum(archiver_stats->last_failed_wal); if (archiver_stats->last_failed_timestamp == 0) nulls[5] = true; else values[5] = TimestampTzGetDatum(archiver_stats->last_failed_timestamp); if (archiver_stats->stat_reset_timestamp == 0) nulls[6] = true; else values[6] = TimestampTzGetDatum(archiver_stats->stat_reset_timestamp); /* Returns the record as Datum */ PG_RETURN_DATUM(HeapTupleGetDatum( heap_form_tuple(tupdesc, values, nulls))); }
/* * master_get_active_worker_nodes returns a set of active worker host names and * port numbers in deterministic order. Currently we assume that all worker * nodes in pg_worker_list.conf are active. */ Datum master_get_active_worker_nodes(PG_FUNCTION_ARGS) { FuncCallContext *functionContext = NULL; uint32 workerNodeIndex = 0; uint32 workerNodeCount = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldContext = NULL; List *workerNodeList = NIL; uint32 workerNodeCount = 0; TupleDesc tupleDescriptor = NULL; bool hasOid = false; /* create a function context for cross-call persistence */ functionContext = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldContext = MemoryContextSwitchTo(functionContext->multi_call_memory_ctx); workerNodeList = WorkerNodeList(); workerNodeCount = (uint32) list_length(workerNodeList); functionContext->user_fctx = workerNodeList; functionContext->max_calls = workerNodeCount; /* * This tuple descriptor must match the output parameters declared for * the function in pg_proc. */ tupleDescriptor = CreateTemplateTupleDesc(WORKER_NODE_FIELDS, hasOid); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, "node_name", TEXTOID, -1, 0); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, "node_port", INT8OID, -1, 0); functionContext->tuple_desc = BlessTupleDesc(tupleDescriptor); MemoryContextSwitchTo(oldContext); } functionContext = SRF_PERCALL_SETUP(); workerNodeIndex = functionContext->call_cntr; workerNodeCount = functionContext->max_calls; if (workerNodeIndex < workerNodeCount) { List *workerNodeList = functionContext->user_fctx; WorkerNode *workerNode = list_nth(workerNodeList, workerNodeIndex); Datum workerNodeDatum = WorkerNodeGetDatum(workerNode, functionContext->tuple_desc); SRF_RETURN_NEXT(functionContext, workerNodeDatum); } else { SRF_RETURN_DONE(functionContext); } }
static int compute_driving_distance(char* sql, int source_vertex_id, float8 distance, bool directed, bool has_reverse_cost, path_element_t **path, int *path_count) { int SPIcode; void *SPIplan; Portal SPIportal; bool moredata = TRUE; int ntuples; edge_t *edges = NULL; int total_tuples = 0; edge_columns_t edge_columns = {.id= -1, .source= -1, .target= -1, .cost= -1, .reverse_cost= -1}; int v_max_id=0; int v_min_id=INT_MAX; char *err_msg; int ret = -1; int s_count = 0; register int z; DBG("start driving_distance\n"); SPIcode = SPI_connect(); if (SPIcode != SPI_OK_CONNECT) { elog(ERROR, "driving_distance: couldn't open a connection to SPI"); return -1; } SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(ERROR, "driving_distance: couldn't create query plan via SPI"); return -1; } if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) { elog(ERROR, "driving_distance: SPI_cursor_open('%s') returns NULL", sql); return -1; } while (moredata == TRUE) { SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT); if (edge_columns.id == -1) { if (fetch_edge_columns(SPI_tuptable, &edge_columns, has_reverse_cost) == -1) return finish(SPIcode, ret); } ntuples = SPI_processed; total_tuples += ntuples; if (!edges) edges = palloc(total_tuples * sizeof(edge_t)); else edges = repalloc(edges, total_tuples * sizeof(edge_t)); 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]; fetch_edge(&tuple, &tupdesc, &edge_columns, &edges[total_tuples - ntuples + t]); } SPI_freetuptable(tuptable); } else { moredata = FALSE; } } //defining min and max vertex id DBG("Total %i tuples", total_tuples); for(z=0; z<total_tuples; z++) { if(edges[z].source<v_min_id) v_min_id=edges[z].source; if(edges[z].source>v_max_id) v_max_id=edges[z].source; if(edges[z].target<v_min_id) v_min_id=edges[z].target; if(edges[z].target>v_max_id) v_max_id=edges[z].target; DBG("%i <-> %i", v_min_id, v_max_id); } //:::::::::::::::::::::::::::::::::::: //:: reducing vertex id (renumbering) //:::::::::::::::::::::::::::::::::::: for(z=0; z<total_tuples; z++) { //check if edges[] contains source if(edges[z].source == source_vertex_id || edges[z].target == source_vertex_id) ++s_count; edges[z].source-=v_min_id; edges[z].target-=v_min_id; DBG("%i - %i", edges[z].source, edges[z].target); } if(s_count == 0) { elog(ERROR, "Start vertex was not found."); return -1; } source_vertex_id -= v_min_id; profstop("extract", prof_extract); profstart(prof_dijkstra); DBG("Calling boost_dijkstra\n"); ret = boost_dijkstra_dist(edges, total_tuples, source_vertex_id, distance, directed, has_reverse_cost, path, path_count, &err_msg); DBG("Back from boost_dijkstra\n"); if (ret < 0) { elog(ERROR, "Error computing path: %s", err_msg); } profstop("dijkstra", prof_dijkstra); profstart(prof_store); //:::::::::::::::::::::::::::::::: //:: restoring original vertex id //:::::::::::::::::::::::::::::::: for(z=0; z<*path_count; z++) { //DBG("vetex %i\n",(*path)[z].vertex_id); (*path)[z].vertex_id+=v_min_id; } return finish(SPIcode, ret); } PG_FUNCTION_INFO_V1(driving_distance); Datum driving_distance(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; int call_cntr; int max_calls; TupleDesc tuple_desc; path_element_t *path = 0; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; int path_count = 0; int ret; // XXX profiling messages are not thread safe profstart(prof_total); profstart(prof_extract); /* 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_driving_distance(text2char(PG_GETARG_TEXT_P(0)), // sql PG_GETARG_INT32(1), // source vertex PG_GETARG_FLOAT8(2), // distance or time PG_GETARG_BOOL(3), PG_GETARG_BOOL(4), &path, &path_count); if (ret < 0) { elog(ERROR, "Error computing path"); } #ifdef DEBUG DBG("Ret is %i", ret); int i; for (i = 0; i < path_count; i++) { DBG("Step %i vertex_id %i ", i, path[i].vertex_id); DBG(" edge_id %i ", path[i].edge_id); DBG(" cost %f ", path[i].cost); } #endif /* total number of tuples to be returned */ funcctx->max_calls = path_count; funcctx->user_fctx = path; funcctx->tuple_desc = BlessTupleDesc( RelationNameGetTupleDesc("pgr_costResult")); MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; path = (path_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) { /* 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(call_cntr); nulls[0] = ' '; values[1] = Int32GetDatum(path[call_cntr].vertex_id); nulls[1] = ' '; values[2] = Int32GetDatum(path[call_cntr].edge_id); nulls[2] = ' '; values[3] = Float8GetDatum(path[call_cntr].cost); nulls[3] = ' '; tuple = heap_formtuple(tuple_desc, 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 */ if (path) free(path); profstop("store", prof_store); profstop("total", prof_total); #ifdef PROFILE elog(NOTICE, "_________"); #endif DBG("Returning value"); SRF_RETURN_DONE(funcctx); } }
/* ---------------------------------------------------------------- * ExecInitFunctionScan * ---------------------------------------------------------------- */ FunctionScanState * ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) { FunctionScanState *scanstate; Oid funcrettype; TypeFuncClass functypclass; TupleDesc tupdesc = NULL; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* * FunctionScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new ScanState for node */ scanstate = makeNode(FunctionScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->eflags = eflags; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &scanstate->ss.ps); ExecInitScanTupleSlot(estate, &scanstate->ss); /* * initialize child expressions */ scanstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->scan.plan.targetlist, (PlanState *) scanstate); scanstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); /* * Now determine if the function returns a simple or composite type, and * build an appropriate tupdesc. */ functypclass = get_expr_result_type(node->funcexpr, &funcrettype, &tupdesc); if (functypclass == TYPEFUNC_COMPOSITE) { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); /* Must copy it out of typcache for safety */ tupdesc = CreateTupleDescCopy(tupdesc); } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ char *attname = strVal(linitial(node->funccolnames)); tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, attname, funcrettype, -1, 0); } else if (functypclass == TYPEFUNC_RECORD) { tupdesc = BuildDescFromLists(node->funccolnames, node->funccoltypes, node->funccoltypmods); } else { /* crummy error message, but parser should have caught this */ elog(ERROR, "function in FROM has unsupported return type"); } /* * For RECORD results, make sure a typmod has been assigned. (The * function should do this for itself, but let's cover things in case it * doesn't.) */ BlessTupleDesc(tupdesc); scanstate->tupdesc = tupdesc; ExecAssignScanType(&scanstate->ss, tupdesc); /* * Other node-specific setup */ scanstate->tuplestorestate = NULL; scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr, (PlanState *) scanstate); scanstate->ss.ps.ps_TupFromTlist = false; /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); return scanstate; }
Datum readindex(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; readindexinfo *info; Relation irel = NULL; Relation hrel = NULL; MIRROREDLOCK_BUFMGR_DECLARE; if (SRF_IS_FIRSTCALL()) { Oid irelid = PG_GETARG_OID(0); TupleDesc tupdesc; MemoryContext oldcontext; AttrNumber outattnum; TupleDesc itupdesc; int i; AttrNumber attno; irel = index_open(irelid, AccessShareLock); itupdesc = RelationGetDescr(irel); outattnum = FIXED_COLUMN + itupdesc->natts; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(outattnum, false); attno = 1; TupleDescInitEntry(tupdesc, attno++, "ictid", TIDOID, -1, 0); TupleDescInitEntry(tupdesc, attno++, "hctid", TIDOID, -1, 0); TupleDescInitEntry(tupdesc, attno++, "aotid", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, attno++, "istatus", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, attno++, "hstatus", TEXTOID, -1, 0); for (i = 0; i < itupdesc->natts; i++) { Form_pg_attribute attr = itupdesc->attrs[i]; TupleDescInitEntry(tupdesc, attno++, NameStr(attr->attname), attr->atttypid, attr->atttypmod, 0); } funcctx->tuple_desc = BlessTupleDesc(tupdesc); info = (readindexinfo *) palloc(sizeof(readindexinfo)); funcctx->user_fctx = (void *) info; info->outattnum = outattnum; info->ireloid = irelid; hrel = relation_open(irel->rd_index->indrelid, AccessShareLock); if (hrel->rd_rel != NULL && (hrel->rd_rel->relstorage == 'a' || hrel->rd_rel->relstorage == 'c')) { relation_close(hrel, AccessShareLock); hrel = NULL; info->hreloid = InvalidOid; } else info->hreloid = irel->rd_index->indrelid; info->num_pages = RelationGetNumberOfBlocks(irel); info->blkno = BTREE_METAPAGE + 1; info->page = NULL; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); info = (readindexinfo *) funcctx->user_fctx; /* * Open the relations (on first call, we did that above already). * We unfortunately have to look up the relcache entry on every call, * because if we store it in the cross-call context, we won't get a * chance to release it if the function isn't run to completion, * e.g. because of a LIMIT clause. We only lock the relation on the * first call, and keep the lock until completion, however. */ if (!irel) irel = index_open(info->ireloid, NoLock); if (!hrel && info->hreloid != InvalidOid) hrel = heap_open(info->hreloid, NoLock); while (info->blkno < info->num_pages) { Datum values[255]; bool nulls[255]; ItemPointerData itid; HeapTuple tuple; Datum result; if (info->page == NULL) { Buffer buf; /* * Make copy of the page, because we cannot hold a buffer pin * across calls (we wouldn't have a chance to release it, if the * function isn't run to completion.) */ info->page = palloc(BLCKSZ); MIRROREDLOCK_BUFMGR_LOCK; buf = ReadBuffer(irel, info->blkno); memcpy(info->page, BufferGetPage(buf), BLCKSZ); ReleaseBuffer(buf); MIRROREDLOCK_BUFMGR_UNLOCK; info->opaque = (BTPageOpaque) PageGetSpecialPointer(info->page); info->minoff = P_FIRSTDATAKEY(info->opaque); info->maxoff = PageGetMaxOffsetNumber(info->page); info->offnum = info->minoff; } if (!P_ISLEAF(info->opaque) || info->offnum > info->maxoff) { pfree(info->page); info->page = NULL; info->blkno++; continue; } MemSet(nulls, false, info->outattnum * sizeof(bool)); ItemPointerSet(&itid, info->blkno, info->offnum); values[0] = ItemPointerGetDatum(&itid); readindextuple(info, irel, hrel, values, nulls); info->offnum = OffsetNumberNext(info->offnum); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); if (hrel != NULL) heap_close(hrel, NoLock); index_close(irel, NoLock); SRF_RETURN_NEXT(funcctx, result); } if (hrel != NULL) heap_close(hrel, AccessShareLock); index_close(irel, AccessShareLock); SRF_RETURN_DONE(funcctx); }
/*------------------------------------------------------- * 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); } }
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(14, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "state", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "query", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "waiting", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "act_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "query_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "backend_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "state_change", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_addr", INETOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 13, "client_hostname", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port", INT4OID, -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[14]; bool nulls[14]; 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[5] = false; values[5] = 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()) { switch (beentry->st_state) { case STATE_IDLE: values[4] = CStringGetTextDatum("idle"); break; case STATE_RUNNING: values[4] = CStringGetTextDatum("active"); break; case STATE_IDLEINTRANSACTION: values[4] = CStringGetTextDatum("idle in transaction"); break; case STATE_FASTPATH: values[4] = CStringGetTextDatum("fastpath function call"); break; case STATE_IDLEINTRANSACTION_ABORTED: values[4] = CStringGetTextDatum("idle in transaction (aborted)"); break; case STATE_DISABLED: values[4] = CStringGetTextDatum("disabled"); break; case STATE_UNDEFINED: nulls[4] = true; break; } if (beentry->st_state == STATE_UNDEFINED || beentry->st_state == STATE_DISABLED) { values[5] = CStringGetTextDatum(""); } else { values[5] = CStringGetTextDatum(beentry->st_activity); } values[6] = BoolGetDatum(beentry->st_waiting); if (beentry->st_xact_start_timestamp != 0) values[7] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[7] = true; if (beentry->st_activity_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[8] = true; if (beentry->st_proc_start_timestamp != 0) values[9] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[9] = true; if (beentry->st_state_start_timestamp != 0) values[10] = TimestampTzGetDatum(beentry->st_state_start_timestamp); else nulls[10] = 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[11] = true; nulls[12] = true; nulls[13] = 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 == 0) { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[11] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); if (beentry->st_clienthostname) values[12] = CStringGetTextDatum(beentry->st_clienthostname); else nulls[12] = true; values[13] = Int32GetDatum(atoi(remote_port)); } else { nulls[11] = true; nulls[12] = true; nulls[13] = true; } } 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[11] = true; nulls[12] = true; values[13] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[11] = true; nulls[12] = true; nulls[13] = true; } } } else { /* No permissions to view data about this session */ values[5] = CStringGetTextDatum("<insufficient privilege>"); nulls[4] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; nulls[11] = true; nulls[12] = true; nulls[13] = true; } tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { /* nothing left */ SRF_RETURN_DONE(funcctx); } }
/* ------------------------------------------------ * hash_metapage_info() * * Get the meta-page information for a hash index * * Usage: SELECT * FROM hash_metapage_info(get_raw_page('con_hash_index', 0)) * ------------------------------------------------ */ Datum hash_metapage_info(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Page page; HashMetaPageData *metad; TupleDesc tupleDesc; HeapTuple tuple; int i, j; Datum values[16]; bool nulls[16]; Datum spares[HASH_MAX_SPLITPOINTS]; Datum mapp[HASH_MAX_BITMAPS]; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); page = verify_hash_page(raw_page, LH_META_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); metad = HashPageGetMeta(page); MemSet(nulls, 0, sizeof(nulls)); j = 0; values[j++] = Int64GetDatum((int64) metad->hashm_magic); values[j++] = Int64GetDatum((int64) metad->hashm_version); values[j++] = Float8GetDatum(metad->hashm_ntuples); values[j++] = Int32GetDatum((int32) metad->hashm_ffactor); values[j++] = Int32GetDatum((int32) metad->hashm_bsize); values[j++] = Int32GetDatum((int32) metad->hashm_bmsize); values[j++] = Int32GetDatum((int32) metad->hashm_bmshift); values[j++] = Int64GetDatum((int64) metad->hashm_maxbucket); values[j++] = Int64GetDatum((int64) metad->hashm_highmask); values[j++] = Int64GetDatum((int64) metad->hashm_lowmask); values[j++] = Int64GetDatum((int64) metad->hashm_ovflpoint); values[j++] = Int64GetDatum((int64) metad->hashm_firstfree); values[j++] = Int64GetDatum((int64) metad->hashm_nmaps); values[j++] = ObjectIdGetDatum((Oid) metad->hashm_procid); for (i = 0; i < HASH_MAX_SPLITPOINTS; i++) spares[i] = Int64GetDatum((int64) metad->hashm_spares[i]); values[j++] = PointerGetDatum(construct_array(spares, HASH_MAX_SPLITPOINTS, INT8OID, 8, FLOAT8PASSBYVAL, 'd')); for (i = 0; i < HASH_MAX_BITMAPS; i++) mapp[i] = Int64GetDatum((int64) metad->hashm_mapp[i]); values[j++] = PointerGetDatum(construct_array(mapp, HASH_MAX_BITMAPS, INT8OID, 8, FLOAT8PASSBYVAL, 'd')); tuple = heap_form_tuple(tupleDesc, values, nulls); PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); }
Datum summarize_variant( PG_FUNCTION_ARGS ) { if( PG_ARGISNULL(0) ) { ereport( ERROR, (errmsg("summarize_variant: array of values must be non-null")) ); } TupleDesc tuple_desc; if( get_call_result_type( fcinfo, NULL, &tuple_desc ) != TYPEFUNC_COMPOSITE ) { ereport( ERROR, (errmsg("summarize_variant: function returning composite type called in context that cannot accept composite type")) ); } tuple_desc = BlessTupleDesc( tuple_desc ); ArrayType* values = PG_GETARG_ARRAYTYPE_P( 0 ); Oid values_type = ARR_ELEMTYPE( values ); int16 values_width; bool values_passbyvalue; char values_alignmentcode; Datum* values_content; bool* values_nullflags; int values_length; get_typlenbyvalalign( values_type, &values_width, &values_passbyvalue, &values_alignmentcode ); deconstruct_array( values, values_type, values_width, values_passbyvalue, values_alignmentcode, &values_content, &values_nullflags, &values_length ); size_t composite_type_elements = 9; Datum* results_content = (Datum*)palloc0( sizeof(Datum) * composite_type_elements ); bool* results_nullflags = (bool*)palloc0( sizeof(bool) * composite_type_elements ); // FORMAT // [0] - call rate // [1] - subset call rate // [2] - alternate allele frequency // [3] - sample alternate allele frequency if( !PG_ARGISNULL(1) ) { ArrayType* indices = PG_GETARG_ARRAYTYPE_P(1); Oid indices_type = ARR_ELEMTYPE( indices ); int16 indices_width; bool indices_passbyvalue; char indices_alignmentcode; Datum* indices_content; bool* indices_nullflags; int indices_length; get_typlenbyvalalign( indices_type, &indices_width, &indices_passbyvalue, &indices_alignmentcode ); deconstruct_array( indices, indices_type, indices_width, indices_passbyvalue, indices_alignmentcode, &indices_content, &indices_nullflags, &indices_length ); int count = 0; int nonnull_count = 0; int alternate_count = 0; int i; for( i = 0; i < indices_length; ++i ) { if( !indices_nullflags[i] && indices_content[i] - 1 < (long unsigned)values_length ) { ++count; if( !values_nullflags[ indices_content[i] - 1 ] ) { ++nonnull_count; alternate_count += values_content[ indices_content[i] - 1 ]; } } } results_content[1] = Float4GetDatum( nonnull_count / (float4)count ); results_content[3] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[3] = nonnull_count == 0; } else { results_nullflags[1] = true; results_nullflags[3] = true; } int count = values_length; unsigned int nonnull_count = 0; unsigned int alternate_count = 0; int i; for( i = 0; i < values_length; ++i ) { if( !values_nullflags[i] ) { ++nonnull_count; alternate_count += values_content[i]; } } results_content[0] = Float4GetDatum( nonnull_count / (float4)count ); results_content[2] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[2] = nonnull_count == 0; HeapTuple tuple = heap_form_tuple( tuple_desc, results_content, results_nullflags ); Datum result = HeapTupleGetDatum( tuple ); PG_RETURN_DATUM( result ); }
/* * SQL function json_array_elements * * get the elements from a json array * * a lot of this processing is similar to the json_each* functions */ Datum json_array_elements(PG_FUNCTION_ARGS) { text *json = PG_GETARG_TEXT_P(0); /* elements doesn't need any escaped strings, so use false here */ JsonLexContext *lex = makeJsonLexContext(json, false); JsonSemAction sem; ReturnSetInfo *rsi; MemoryContext old_cxt; TupleDesc tupdesc; ElementsState state; state = palloc0(sizeof(elementsState)); sem = palloc0(sizeof(jsonSemAction)); rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that " "cannot accept a set"))); rsi->returnMode = SFRM_Materialize; /* it's a simple type, so don't use get_call_result_type() */ tupdesc = rsi->expectedDesc; /* make these in a sufficiently long-lived memory context */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); state->ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(state->ret_tdesc); state->tuple_store = tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize, false, work_mem); MemoryContextSwitchTo(old_cxt); sem->semstate = (void *) state; sem->object_start = elements_object_start; sem->scalar = elements_scalar; sem->array_element_start = elements_array_element_start; sem->array_element_end = elements_array_element_end; state->lex = lex; state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext, "json_array_elements temporary cxt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); pg_parse_json(lex, sem); rsi->setResult = state->tuple_store; rsi->setDesc = state->ret_tdesc; PG_RETURN_NULL(); }
static int compute_apsp_warshall(char* sql, bool directed, bool has_reverse_cost, apsp_element_t **pair, int *pair_count) { int i; int SPIcode; void *SPIplan; Portal SPIportal; bool moredata = TRUE; int ntuples; edge_t *edges = NULL; int total_tuples = 0; edge_columns_t edge_columns = {.id= -1, .source= -1, .target= -1, .cost= -1, .reverse_cost= -1}; int v_max_id=0; int v_min_id=INT_MAX; int s_count = 0; int t_count = 0; char *err_msg; int ret = -1; register int z; // set<int> vertices; DBG("start compute_apsp_warshall\n"); SPIcode = SPI_connect(); if (SPIcode != SPI_OK_CONNECT) { elog(ERROR, "compute_apsp_warshall: couldn't open a connection to SPI"); return -1; } SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(ERROR, "compute_apsp_warshall: couldn't create query plan via SPI"); return -1; } if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) { elog(ERROR, "compute_apsp_warshall: SPI_cursor_open('%s') returns NULL", sql); return -1; } while (moredata == TRUE) { SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT); if (edge_columns.id == -1) { if (fetch_edge_columns(SPI_tuptable, &edge_columns, has_reverse_cost) == -1) return finish(SPIcode, ret); } ntuples = SPI_processed; total_tuples += ntuples; if (!edges) edges = palloc(total_tuples * sizeof(edge_t)); else edges = repalloc(edges, total_tuples * sizeof(edge_t)); if (edges == NULL) { elog(ERROR, "Out of memory"); return finish(SPIcode, ret); } DBG("Number of tuples fetched: %i",ntuples); 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]; fetch_edge(&tuple, &tupdesc, &edge_columns, &edges[total_tuples - ntuples + t]); // vertices.insert(edges[total_tuples - ntuples + t].source); // vertices.insert(edges[total_tuples - ntuples + t].target); } SPI_freetuptable(tuptable); } else { moredata = FALSE; } } #ifdef DEBUG for (i = 0; i < total_tuples; i++) { DBG("Step %i src_vertex_id %i ", i, edges[i].source); DBG(" dest_vertex_id %i ", edges[i].target); DBG(" cost %f ", edges[i].cost); } #endif DBG("Calling boost_apsp\n"); //start_vertex -= v_min_id; //end_vertex -= v_min_id; ret = boost_apsp(edges, total_tuples, 0, //vertices.size() directed, has_reverse_cost, pair, pair_count, &err_msg); DBG("Boost message: \n%s",err_msg); DBG("SIZE %i\n",*pair_count); /* //:::::::::::::::::::::::::::::::: //:: restoring original vertex id //:::::::::::::::::::::::::::::::: for(z=0;z<*path_count;z++) { //DBG("vetex %i\n",(*path)[z].vertex_id); (*path)[z].vertex_id+=v_min_id; } DBG("ret = %i\n", ret); DBG("*path_count = %i\n", *path_count); DBG("ret = %i\n", ret); */ if (ret < 0) { //elog(ERROR, "Error computing path: %s", err_msg); ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED), errmsg("Error computing path: %s", err_msg))); } return finish(SPIcode, ret); } PG_FUNCTION_INFO_V1(apsp_warshall); Datum apsp_warshall(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; int call_cntr; int max_calls; TupleDesc tuple_desc; apsp_element_t *pair; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; int pair_count = 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_apsp_warshall(text2char(PG_GETARG_TEXT_P(0)), PG_GETARG_BOOL(1), PG_GETARG_BOOL(2), &pair, &pair_count); #ifdef DEBUG DBG("Ret is %i", ret); if (ret >= 0) { int i; for (i = 0; i < pair_count; i++) { DBG("Step: %i, source_id: %i, target_id: %i, cost: %f ", i, pair[i].src_vertex_id, pair[i].dest_vertex_id, pair[i].cost); } } #endif /* total number of tuples to be returned */ funcctx->max_calls = pair_count; funcctx->user_fctx = pair; funcctx->tuple_desc = BlessTupleDesc(RelationNameGetTupleDesc("pgr_costResult")); MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; pair = (apsp_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) /* do when there is more left to send */ { HeapTuple tuple; Datum result; Datum *values; char* nulls; /* This will work for some compilers. If it crashes with segfault, try to change the following block with this one values = palloc(4 * sizeof(Datum)); nulls = palloc(4 * sizeof(char)); values[0] = call_cntr; nulls[0] = ' '; values[1] = Int32GetDatum(path[call_cntr].vertex_id); nulls[1] = ' '; values[2] = Int32GetDatum(path[call_cntr].edge_id); nulls[2] = ' '; values[3] = Float8GetDatum(path[call_cntr].cost); nulls[3] = ' '; */ values = palloc(4 * sizeof(Datum)); nulls = palloc(4 * sizeof(char)); values[0] = Int32GetDatum(call_cntr); nulls[0] = ' '; values[1] = Int32GetDatum(pair[call_cntr].src_vertex_id); nulls[1] = ' '; values[2] = Int32GetDatum(pair[call_cntr].dest_vertex_id); nulls[2] = ' '; values[3] = Float8GetDatum(pair[call_cntr].cost); nulls[3] = ' '; tuple = heap_formtuple(tuple_desc, 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); } }
static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text) { text *json = PG_GETARG_TEXT_P(0); JsonLexContext *lex = makeJsonLexContext(json, true); JsonSemAction sem; ReturnSetInfo *rsi; MemoryContext old_cxt; TupleDesc tupdesc; EachState state; state = palloc0(sizeof(eachState)); sem = palloc0(sizeof(jsonSemAction)); rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that " "cannot accept a set"))); rsi->returnMode = SFRM_Materialize; (void) get_call_result_type(fcinfo, NULL, &tupdesc); /* make these in a sufficiently long-lived memory context */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); state->ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(state->ret_tdesc); state->tuple_store = tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize, false, work_mem); MemoryContextSwitchTo(old_cxt); sem->semstate = (void *) state; sem->array_start = each_array_start; sem->scalar = each_scalar; sem->object_field_start = each_object_field_start; sem->object_field_end = each_object_field_end; state->normalize_results = as_text; state->next_scalar = false; state->lex = lex; state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext, "json_each temporary cxt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); pg_parse_json(lex, sem); rsi->setResult = state->tuple_store; rsi->setDesc = state->ret_tdesc; PG_RETURN_NULL(); }
/* * gp_read_error_log * * Returns set of error log tuples. */ Datum gp_read_error_log(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ReadErrorLogContext *context; HeapTuple tuple; Datum result; /* * First call setup */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; FILE *fp; text *relname; funcctx = SRF_FIRSTCALL_INIT(); relname = PG_GETARG_TEXT_P(0); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); context = palloc0(sizeof(ReadErrorLogContext)); funcctx->user_fctx = (void *) context; funcctx->tuple_desc = BlessTupleDesc(GetErrorTupleDesc()); /* * Though this function is usually executed on segment, we dispatch * the execution if it happens to be on QD, and combine the results * into one set. */ if (Gp_role == GP_ROLE_DISPATCH) { struct CdbPgResults cdb_pgresults = {NULL, 0}; StringInfoData sql; int i; initStringInfo(&sql); /* * construct SQL */ appendStringInfo(&sql, "SELECT * FROM pg_catalog.gp_read_error_log(%s) ", quote_literal_internal(text_to_cstring(relname))); CdbDispatchCommand(sql.data, DF_WITH_SNAPSHOT, &cdb_pgresults); for (i = 0; i < cdb_pgresults.numResults; i++) { if (PQresultStatus(cdb_pgresults.pg_results[i]) != PGRES_TUPLES_OK) { cdbdisp_clearCdbPgResults(&cdb_pgresults); elog(ERROR, "unexpected result from segment: %d", PQresultStatus(cdb_pgresults.pg_results[i])); } context->numTuples += PQntuples(cdb_pgresults.pg_results[i]); } pfree(sql.data); context->segResults = cdb_pgresults.pg_results; context->numSegResults = cdb_pgresults.numResults; } else { /* * In QE, read the error log. */ RangeVar *relrv; Oid relid; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); relid = RangeVarGetRelid(relrv, true); /* * If the relation has gone, silently return no tuples. */ if (OidIsValid(relid)) { AclResult aclresult; /* * Requires SELECT priv to read error log. */ aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, relrv->relname); ErrorLogFileName(context->filename, MyDatabaseId, relid); fp = AllocateFile(context->filename, "r"); context->fp = fp; } } MemoryContextSwitchTo(oldcontext); if (Gp_role != GP_ROLE_DISPATCH && !context->fp) { pfree(context); SRF_RETURN_DONE(funcctx); } } funcctx = SRF_PERCALL_SETUP(); context = (ReadErrorLogContext *) funcctx->user_fctx; /* * Read error log, probably on segments. We don't check Gp_role, however, * in case master also wants to read the file. */ if (context->fp) { pg_crc32 crc, written_crc; tuple = ErrorLogRead(context->fp, &written_crc); /* * CRC check. */ if (HeapTupleIsValid(tuple)) { INIT_CRC32C(crc); COMP_CRC32C(crc, tuple->t_data, tuple->t_len); FIN_CRC32C(crc); if (!EQ_CRC32C(crc, written_crc)) { elog(LOG, "incorrect checksum in error log %s", context->filename); tuple = NULL; } } /* * If we found a valid tuple, return it. Otherwise, fall through * in the DONE routine. */ if (HeapTupleIsValid(tuple)) { /* * We need to set typmod for the executor to understand * its type we just blessed. */ HeapTupleHeaderSetTypMod(tuple->t_data, funcctx->tuple_desc->tdtypmod); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } } /* * If we got results from dispatch, return all the tuples. */ while (context->currentResult < context->numSegResults) { Datum values[NUM_ERRORTABLE_ATTR]; bool isnull[NUM_ERRORTABLE_ATTR]; PGresult *segres = context->segResults[context->currentResult]; int row = context->currentRow; if (row >= PQntuples(segres)) { context->currentRow = 0; context->currentResult++; continue; } context->currentRow++; MemSet(isnull, false, sizeof(isnull)); values[0] = ResultToDatum(segres, row, 0, timestamptz_in, &isnull[0]); values[1] = ResultToDatum(segres, row, 1, textin, &isnull[1]); values[2] = ResultToDatum(segres, row, 2, textin, &isnull[2]); values[3] = ResultToDatum(segres, row, 3, int4in, &isnull[3]); values[4] = ResultToDatum(segres, row, 4, int4in, &isnull[4]); values[5] = ResultToDatum(segres, row, 5, textin, &isnull[5]); values[6] = ResultToDatum(segres, row, 6, textin, &isnull[6]); values[7] = ResultToDatum(segres, row, 7, byteain, &isnull[7]); tuple = heap_form_tuple(funcctx->tuple_desc, values, isnull); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } if (context->segResults != NULL) { int i; for (i = 0; i < context->numSegResults; i++) PQclear(context->segResults[i]); /* XXX: better to copy to palloc'ed area */ free(context->segResults); } /* * Close the file, if we have opened it. */ if (context->fp != NULL) { FreeFile(context->fp); context->fp = NULL; } 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; 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); }
/* * master_get_local_first_candidate_nodes returns a set of candidate host names * and port numbers on which to place new shards. The function makes sure to * always allocate the first candidate node as the node the caller is connecting * from; and allocates additional nodes until the shard replication factor is * met. The function errors if the caller's remote node name is not found in the * membership list, or if the number of available nodes falls short of the * replication factor. */ Datum master_get_local_first_candidate_nodes(PG_FUNCTION_ARGS) { FuncCallContext *functionContext = NULL; uint32 desiredNodeCount = 0; uint32 currentNodeCount = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldContext = NULL; TupleDesc tupleDescriptor = NULL; uint32 liveNodeCount = 0; bool hasOid = false; /* create a function context for cross-call persistence */ functionContext = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldContext = MemoryContextSwitchTo(functionContext->multi_call_memory_ctx); functionContext->user_fctx = NIL; functionContext->max_calls = ShardReplicationFactor; /* if enough live nodes, return an extra candidate node as backup */ liveNodeCount = WorkerGetLiveNodeCount(); if (liveNodeCount > ShardReplicationFactor) { functionContext->max_calls = ShardReplicationFactor + 1; } /* * This tuple descriptor must match the output parameters declared for * the function in pg_proc. */ tupleDescriptor = CreateTemplateTupleDesc(CANDIDATE_NODE_FIELDS, hasOid); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, "node_name", TEXTOID, -1, 0); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, "node_port", INT8OID, -1, 0); functionContext->tuple_desc = BlessTupleDesc(tupleDescriptor); MemoryContextSwitchTo(oldContext); } functionContext = SRF_PERCALL_SETUP(); desiredNodeCount = functionContext->max_calls; currentNodeCount = functionContext->call_cntr; if (currentNodeCount < desiredNodeCount) { MemoryContext oldContext = NULL; List *currentNodeList = NIL; WorkerNode *candidateNode = NULL; Datum candidateDatum = 0; /* switch to memory context appropriate for multiple function calls */ oldContext = MemoryContextSwitchTo(functionContext->multi_call_memory_ctx); currentNodeList = functionContext->user_fctx; candidateNode = WorkerGetLocalFirstCandidateNode(currentNodeList); if (candidateNode == NULL) { ereport(ERROR, (errmsg("could only find %u of %u required nodes", currentNodeCount, desiredNodeCount))); } currentNodeList = lappend(currentNodeList, candidateNode); functionContext->user_fctx = currentNodeList; MemoryContextSwitchTo(oldContext); candidateDatum = WorkerNodeGetDatum(candidateNode, functionContext->tuple_desc); SRF_RETURN_NEXT(functionContext, candidateDatum); } else { SRF_RETURN_DONE(functionContext); } }
/* * 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); }
/* * topn is a user-facing UDF which returns the top items and their frequencies. * It first gets the top-n structure and converts it into the ordered array of * FrequentTopnItem which keeps Datums and the frequencies in the first call. * Then, it returns an item and its frequency according to call counter. This * function requires a parameter for the type because PostgreSQL has strongly * typed system and the type of frequent items in returning rows has to be given. */ Datum topn(PG_FUNCTION_ARGS) { FuncCallContext *functionCallContext = NULL; TupleDesc tupleDescriptor = NULL; Oid returningItemType = get_fn_expr_argtype(fcinfo->flinfo, 1); CmsTopn *cmsTopn = NULL; ArrayType *topnArray = NULL; int topnArrayLength = 0; Datum topnItem = 0; bool isNull = false; ArrayIterator topnIterator = NULL; bool hasMoreItem = false; int callCounter = 0; int maxCallCounter = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext = NULL; Size topnArraySize = 0; TypeCacheEntry *itemTypeCacheEntry = NULL; int topnIndex = 0; Oid itemType = InvalidOid; FrequentTopnItem *sortedTopnArray = NULL; TupleDesc completeTupleDescriptor = NULL; functionCallContext = SRF_FIRSTCALL_INIT(); if (PG_ARGISNULL(0)) { SRF_RETURN_DONE(functionCallContext); } cmsTopn = (CmsTopn *) PG_GETARG_VARLENA_P(0); topnArray = TopnArray(cmsTopn); topnArrayLength = ARR_DIMS(topnArray)[0]; /* if there is not any element in the array just return */ if (topnArrayLength == 0) { SRF_RETURN_DONE(functionCallContext); } itemType = ARR_ELEMTYPE(topnArray); if (itemType != returningItemType) { elog(ERROR, "not a proper cms_topn for the result type"); } /* switch to consistent context for multiple calls */ oldcontext = MemoryContextSwitchTo(functionCallContext->multi_call_memory_ctx); itemTypeCacheEntry = lookup_type_cache(itemType, 0); functionCallContext->max_calls = topnArrayLength; /* create an array to copy top-n items and sort them later */ topnArraySize = sizeof(FrequentTopnItem) * topnArrayLength; sortedTopnArray = palloc0(topnArraySize); topnIterator = array_create_iterator(topnArray, 0); hasMoreItem = array_iterate(topnIterator, &topnItem, &isNull); while (hasMoreItem) { FrequentTopnItem frequentTopnItem; frequentTopnItem.topnItem = topnItem; frequentTopnItem.topnItemFrequency = CmsTopnEstimateItemFrequency(cmsTopn, topnItem, itemTypeCacheEntry); sortedTopnArray[topnIndex] = frequentTopnItem; hasMoreItem = array_iterate(topnIterator, &topnItem, &isNull); topnIndex++; } SortTopnItems(sortedTopnArray, topnArrayLength); functionCallContext->user_fctx = sortedTopnArray; get_call_result_type(fcinfo, &returningItemType, &tupleDescriptor); completeTupleDescriptor = BlessTupleDesc(tupleDescriptor); functionCallContext->tuple_desc = completeTupleDescriptor; MemoryContextSwitchTo(oldcontext); } functionCallContext = SRF_PERCALL_SETUP(); maxCallCounter = functionCallContext->max_calls; callCounter = functionCallContext->call_cntr; if (callCounter < maxCallCounter) { Datum *tupleValues = (Datum *) palloc(2 * sizeof(Datum)); HeapTuple topnItemTuple; Datum topnItemDatum = 0; char *tupleNulls = (char *) palloc0(2 * sizeof(char)); FrequentTopnItem *sortedTopnArray = NULL; TupleDesc completeTupleDescriptor = NULL; sortedTopnArray = (FrequentTopnItem *) functionCallContext->user_fctx; tupleValues[0] = sortedTopnArray[callCounter].topnItem; tupleValues[1] = sortedTopnArray[callCounter].topnItemFrequency; /* non-null attributes are indicated by a ' ' (space) */ tupleNulls[0] = ' '; tupleNulls[1] = ' '; completeTupleDescriptor = functionCallContext->tuple_desc; topnItemTuple = heap_formtuple(completeTupleDescriptor, tupleValues, tupleNulls); topnItemDatum = HeapTupleGetDatum(topnItemTuple); SRF_RETURN_NEXT(functionCallContext, topnItemDatum); } else { SRF_RETURN_DONE(functionCallContext); } }