static void tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) { TupleDesc tupdesc; MemoryContext oldcontext; TSTokenTypeStorage *st; TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid); if (!OidIsValid(prs->lextypeOid)) elog(ERROR, "method lextype isn't defined for text search parser %u", prsid); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (TSTokenTypeStorage *) palloc(sizeof(TSTokenTypeStorage)); st->cur = 0; /* lextype takes one dummy argument */ st->list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid, (Datum) 0)); funcctx->user_fctx = (void *) st; tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
Datum dbms_alert_waitone(PG_FUNCTION_ARGS) { text *name; float8 timeout; TupleDesc tupdesc; AttInMetadata *attinmeta; HeapTuple tuple; Datum result; int message_id; char *str[2] = {NULL,"1"}; char *event_name; int cycle = 0; float8 endtime; TupleDesc btupdesc; if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("event name is NULL"), errdetail("Eventname may not be NULL."))); if (PG_ARGISNULL(1)) timeout = TDAYS; else timeout = PG_GETARG_FLOAT8(1); name = PG_GETARG_TEXT_P(0); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) { if (NULL != find_event(name, false, &message_id)) { str[0] = find_and_remove_message_item(message_id, sid, false, false, false, NULL, &event_name); if (event_name != NULL) { str[1] = "0"; pfree(event_name); LWLockRelease(shmem_lock); break; } } LWLockRelease(shmem_lock); } WATCH_POST(timeout, endtime, cycle); get_call_result_type(fcinfo, NULL, &tupdesc); btupdesc = BlessTupleDesc(tupdesc); attinmeta = TupleDescGetAttInMetadata(btupdesc); tuple = BuildTupleFromCStrings(attinmeta, str); result = HeapTupleGetDatum(tuple); if (str[0]) pfree(str[0]); return result; }
/* * plphp_htup_from_zval * Build a HeapTuple from a zval (which must be an array) and a TupleDesc. * * The return HeapTuple is allocated in the current memory context and must * be freed by the caller. * * If zval doesn't contain any of the element names from the TupleDesc, * build a tuple from the first N elements. This allows us to accept * arrays in form array(1,2,3) as the result of functions with OUT arguments. * XXX -- possible optimization: keep the memory context created and only * reset it between calls. */ HeapTuple plphp_htup_from_zval(zval *val, TupleDesc tupdesc) { MemoryContext oldcxt; MemoryContext tmpcxt; HeapTuple ret; AttInMetadata *attinmeta; HashPosition pos; zval **element; char **values; int i; bool allempty = true; tmpcxt = AllocSetContextCreate(TopTransactionContext, "htup_from_zval cxt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(tmpcxt); values = (char **) palloc(tupdesc->natts * sizeof(char *)); for (i = 0; i < tupdesc->natts; i++) { char *key = SPI_fname(tupdesc, i + 1); zval *scalarval = plphp_array_get_elem(val, key); values[i] = plphp_zval_get_cstring(scalarval, true, true); /* * Reset the flag is even one of the keys actually exists, * even if it is NULL. */ if (scalarval != NULL) allempty = false; } /* None of the names from the tuple exists, * try to get 1st N array elements and assign them to the tuple */ if (allempty) for (i = 0, zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(val), &pos); (zend_hash_get_current_data_ex(Z_ARRVAL_P(val), (void **) &element, &pos) == SUCCESS) && (i < tupdesc->natts); zend_hash_move_forward_ex(Z_ARRVAL_P(val), &pos), i++) values[i] = plphp_zval_get_cstring(element[0], true, true); attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcxt); ret = BuildTupleFromCStrings(attinmeta, values); MemoryContextDelete(tmpcxt); return ret; }
static void prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) { TupleDesc tupdesc; MemoryContext oldcontext; PrsStorage *st; TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid); char *lex = NULL; int llen = 0, type = 0; void *prsdata; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (PrsStorage *) palloc(sizeof(PrsStorage)); st->cur = 0; st->len = 16; st->list = (LexemeEntry *) palloc(sizeof(LexemeEntry) * st->len); prsdata = (void *) DatumGetPointer(FunctionCall2(&prs->prsstart, PointerGetDatum(VARDATA(txt)), Int32GetDatum(VARSIZE(txt) - VARHDRSZ))); while ((type = DatumGetInt32(FunctionCall3(&prs->prstoken, PointerGetDatum(prsdata), PointerGetDatum(&lex), PointerGetDatum(&llen)))) != 0) { if (st->cur >= st->len) { st->len = 2 * st->len; st->list = (LexemeEntry *) repalloc(st->list, sizeof(LexemeEntry) * st->len); } st->list[st->cur].lexeme = palloc(llen + 1); memcpy(st->list[st->cur].lexeme, lex, llen); st->list[st->cur].lexeme[llen] = '\0'; st->list[st->cur].type = type; st->cur++; } FunctionCall1(&prs->prsend, PointerGetDatum(prsdata)); st->len = st->cur; st->cur = 0; funcctx->user_fctx = (void *) st; tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
Datum dbms_alert_waitany(PG_FUNCTION_ARGS) { float8 timeout; TupleDesc tupdesc; AttInMetadata *attinmeta; HeapTuple tuple; Datum result; char *str[3] = {NULL, NULL, "1"}; int cycle = 0; float8 endtime; TupleDesc btupdesc; if (PG_ARGISNULL(0)) timeout = TDAYS; else timeout = PG_GETARG_FLOAT8(0); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) { str[1] = find_and_remove_message_item(-1, sid, true, false, false, NULL, &str[0]); if (str[0]) { str[2] = "0"; LWLockRelease(shmem_lock); break; } LWLockRelease(shmem_lock); } WATCH_POST(timeout, endtime, cycle); get_call_result_type(fcinfo, NULL, &tupdesc); btupdesc = BlessTupleDesc(tupdesc); attinmeta = TupleDescGetAttInMetadata(btupdesc); tuple = BuildTupleFromCStrings(attinmeta, str); result = HeapTupleGetDatum(tuple); if (str[0]) pfree(str[0]); if (str[1]) pfree(str[1]); return result; }
static void ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, TSVectorStat *stat) { TupleDesc tupdesc; MemoryContext oldcontext; StatEntry *node; funcctx->user_fctx = (void *) stat; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); stat->stack = palloc0(sizeof(StatEntry *) * (stat->maxdepth + 1)); stat->stackpos = 0; node = stat->root; /* find leftmost value */ if (node == NULL) stat->stack[stat->stackpos] = NULL; else for (;;) { stat->stack[stat->stackpos] = node; if (node->left) { stat->stackpos++; node = node->left; } else break; } Assert(stat->stackpos <= stat->maxdepth); tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "nentry", INT4OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
Datum testfunc5(PG_FUNCTION_ARGS) { int64 i = PG_GETARG_INT64(0); FuncCallContext *funcctx; MemoryContext oldcontext; if (SRF_IS_FIRSTCALL()) { TupleDesc tupd; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupd = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupd, 1, "c1", INT8OID, -1, 0); TupleDescInitEntry(tupd, 2, "c2", INT8OID, -1, 0); funcctx->max_calls = 3; funcctx->user_fctx = tupd; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < funcctx->max_calls) { TupleDesc tupd; HeapTupleData tupleData; HeapTuple tuple = &tupleData; char *values[2]; Datum result; tupd = (TupleDesc)funcctx->user_fctx; values[0] = palloc(32); sprintf(values[0], INT64_FORMAT, i+1+funcctx->call_cntr); values[1] = palloc(32); sprintf(values[1], INT64_FORMAT, i+2+funcctx->call_cntr); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupd), values); result = TupleGetDatum(TupleDescGetSlot(tuple), tuple); SRF_RETURN_NEXT(funcctx, result); } else { SRF_RETURN_DONE(funcctx); } }
Datum make_new_order_info(PG_FUNCTION_ARGS) { /* result Datum */ Datum result; char** cstr_values; HeapTuple result_tuple; /* tuple manipulating variables */ TupleDesc tupdesc; TupleTableSlot *slot; AttInMetadata *attinmeta; /* loop variables. */ int i; /* get tupdesc from the type name */ tupdesc = RelationNameGetTupleDesc("new_order_info"); /* allocate a slot for a tuple with this tupdesc */ slot = TupleDescGetSlot(tupdesc); /* * generate attribute metadata needed later to produce tuples * from raw C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); cstr_values = (char **) palloc(3 * sizeof(char *)); for(i = 0; i < 3; i++) { cstr_values[i] = (char*) palloc(16 * sizeof(char)); /* 16 bytes */ snprintf(cstr_values[i], 16, "%d", PG_GETARG_INT32(i)); } /* build a tuple */ result_tuple = BuildTupleFromCStrings(attinmeta, cstr_values); /* make the tuple into a datum */ result = TupleGetDatum(slot, result_tuple); return result; }
static void setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, Oid prsid) { TupleDesc tupdesc; MemoryContext oldcontext; TypeStorage *st; WParserInfo *prs = findprs(prsid); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (TypeStorage *) palloc(sizeof(TypeStorage)); st->cur = 0; st->list = (LexDescr *) DatumGetPointer( OidFunctionCall1(prs->lextype, PointerGetDatum(prs->prs)) ); funcctx->user_fctx = (void *) st; if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupdesc = CreateTupleDescCopy(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
Datum testfunc3(PG_FUNCTION_ARGS) { TupleDesc tupd; HeapTupleData tupleData; HeapTuple tuple = &tupleData; char *values[2]; Datum result; int64 i = PG_GETARG_INT64(0); tupd = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupd, 1, "c1", INT8OID, -1, 0); TupleDescInitEntry(tupd, 2, "c2", INT8OID, -1, 0); values[0] = palloc(32); sprintf(values[0], INT64_FORMAT, i+1); values[1] = palloc(32); sprintf(values[1], INT64_FORMAT, i+2); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupd), values); result = TupleGetDatum(TupleDescGetSlot(tuple), tuple); PG_RETURN_DATUM(result); }
/* * build_pgstattuple_type -- build a pgstattuple_type tuple */ static Datum build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo) { #define NCOLUMNS 9 #define NCHARS 32 HeapTuple tuple; char *values[NCOLUMNS]; char values_buf[NCOLUMNS][NCHARS]; int i; double tuple_percent; double dead_tuple_percent; double free_percent; /* free/reusable space in % */ TupleDesc tupdesc; AttInMetadata *attinmeta; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); if (stat->table_len == 0) { tuple_percent = 0.0; dead_tuple_percent = 0.0; free_percent = 0.0; } else { tuple_percent = 100.0 * stat->tuple_len / stat->table_len; dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len; free_percent = 100.0 * stat->free_space / stat->table_len; } /* * Prepare a values array for constructing the tuple. This should be an * array of C strings which will be processed later by the appropriate * "in" functions. */ for (i = 0; i < NCOLUMNS; i++) values[i] = values_buf[i]; i = 0; snprintf(values[i++], NCHARS, INT64_FORMAT, stat->table_len); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_len); snprintf(values[i++], NCHARS, "%.2f", tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_len); snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->free_space); snprintf(values[i++], NCHARS, "%.2f", free_percent); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ return HeapTupleGetDatum(tuple); }
/* Clause 3.3.9.3 */ Datum TradeStatusFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { enum tsf1 { i_broker_name=0, i_charge, i_cust_f_name, i_cust_l_name, i_ex_name, i_exec_name, i_num_found, i_s_name, i_status_name, i_symbol, i_trade_dts, i_trade_id, i_trade_qty, i_type_name }; long acct_id = PG_GETARG_INT64(0); int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[1]; char nulls[1] = { ' ' }; /* * Prepare a values array for building the returned tuple. * This should be an array of C strings, which will * be processed later by the type input functions. */ values = (char **) palloc(sizeof(char *) * 14); values[i_charge] = (char *) palloc((VALUE_T_LEN + 1) * sizeof(char) * 50); values[i_ex_name] = (char *) palloc((EX_NAME_LEN + 3) * sizeof(char) * 50); values[i_exec_name] = (char *) palloc((T_EXEC_NAME_LEN + 3) * sizeof(char) * 50); values[i_num_found] = (char *) palloc((BIGINT_LEN + 1) * sizeof(char)); values[i_s_name] = (char *) palloc((S_NAME_LEN + 3) * sizeof(char) * 50); values[i_status_name] = (char *) palloc((ST_NAME_LEN + 3) * sizeof(char) * 50); values[i_symbol] = (char *) palloc((S_SYMB_LEN + 3) * sizeof(char) * 50); values[i_trade_dts] = (char *) palloc((MAXDATELEN + 1) * sizeof(char) * 50); values[i_trade_id] = (char *) palloc((BIGINT_LEN + 1) * sizeof(char) * 50); values[i_trade_qty] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char) * 50); values[i_type_name] = (char *) palloc((TT_NAME_LEN + 3) * sizeof(char) * 50); values[i_cust_l_name] = NULL; values[i_cust_f_name] = NULL; values[i_broker_name] = NULL; #ifdef DEBUG dump_tsf1_inputs(acct_id); #endif /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); funcctx->max_calls = 1; /* switch to memory context appropriate for multiple function calls */ TSF1_savedcxt = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(TSF1_statements); #ifdef DEBUG sprintf(sql, SQLTSF1_1, acct_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(acct_id); ret = SPI_execute_plan(TSF1_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; } else { FAIL_FRAME_SET(&funcctx->max_calls, TSF1_statements[0].sql); dump_tsf1_inputs(acct_id); } sprintf(values[i_num_found], "%d", SPI_processed); strcpy(values[i_trade_id], "{"); strcpy(values[i_trade_dts], "{"); strcpy(values[i_status_name], "{"); strcpy(values[i_type_name], "{"); strcpy(values[i_symbol], "{"); strcpy(values[i_trade_qty], "{"); strcpy(values[i_exec_name], "{"); strcpy(values[i_charge], "{"); strcpy(values[i_s_name], "{"); strcpy(values[i_ex_name], "{"); for (i = 0; i < SPI_processed; i++) { tuple = tuptable->vals[i]; if (i > 0) { strcat(values[i_trade_id], ","); strcat(values[i_trade_dts], ","); strcat(values[i_status_name], ","); strcat(values[i_type_name], ","); strcat(values[i_symbol], ","); strcat(values[i_trade_qty], ","); strcat(values[i_exec_name], ","); strcat(values[i_charge], ","); strcat(values[i_s_name], ","); strcat(values[i_ex_name], ","); } strcat(values[i_trade_id], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_trade_dts], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_status_name], "\""); strcat(values[i_status_name], SPI_getvalue(tuple, tupdesc, 3)); strcat(values[i_status_name], "\""); strcat(values[i_type_name], "\""); strcat(values[i_type_name], SPI_getvalue(tuple, tupdesc, 4)); strcat(values[i_type_name], "\""); strcat(values[i_symbol], "\""); strcat(values[i_symbol], SPI_getvalue(tuple, tupdesc, 5)); strcat(values[i_symbol], "\""); strcat(values[i_trade_qty], SPI_getvalue(tuple, tupdesc, 6)); strcat(values[i_exec_name], "\""); strcat(values[i_exec_name], SPI_getvalue(tuple, tupdesc, 7)); strcat(values[i_exec_name], "\""); strcat(values[i_charge], SPI_getvalue(tuple, tupdesc, 8)); strcat(values[i_s_name], "\""); strcat(values[i_s_name], SPI_getvalue(tuple, tupdesc, 9)); strcat(values[i_s_name], "\""); strcat(values[i_ex_name], "\""); strcat(values[i_ex_name], SPI_getvalue(tuple, tupdesc, 10)); strcat(values[i_ex_name], "\""); } strcat(values[i_trade_id], "}"); strcat(values[i_trade_dts], "}"); strcat(values[i_status_name], "}"); strcat(values[i_type_name], "}"); strcat(values[i_symbol], "}"); strcat(values[i_trade_qty], "}"); strcat(values[i_exec_name], "}"); strcat(values[i_charge], "}"); strcat(values[i_s_name], "}"); strcat(values[i_ex_name], "}"); #ifdef DEBUG sprintf(sql, SQLTSF1_2, acct_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TSF1_2, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; if (SPI_processed > 0) { tuple = tuptable->vals[0]; values[i_cust_l_name] = SPI_getvalue(tuple, tupdesc, 1); values[i_cust_f_name] = SPI_getvalue(tuple, tupdesc, 2); values[i_broker_name] = SPI_getvalue(tuple, tupdesc, 3); } } else { FAIL_FRAME_SET(&funcctx->max_calls, TSF1_statements[1].sql); dump_tsf1_inputs(acct_id); } /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(TSF1_savedcxt); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; #ifdef DEBUG for (i = 0; i < 14; i++) { elog(NOTICE, "TSF1 OUT: %d %s", i, values[i]); } #endif /* DEBUG */ /* Build a tuple. */ tuple = BuildTupleFromCStrings(attinmeta, values); /* Make the tuple into a datum. */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* Do when there is no more left. */ SPI_finish(); if (TSF1_savedcxt) MemoryContextSwitchTo(TSF1_savedcxt); SRF_RETURN_DONE(funcctx); } }
/* ----------------------------------------------- * bt_page_stats() * * Usage: SELECT * FROM bt_page_stats('t1_pkey', 1); * ----------------------------------------------- */ Datum bt_page_stats(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); Buffer buffer; Relation rel; RangeVar *relrv; Datum result; HeapTuple tuple; TupleDesc tupleDesc; int j; char *values[11]; BTPageStat stat; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* keep compiler quiet */ stat.btpo_prev = stat.btpo_next = InvalidBlockNumber; stat.btpo_flags = stat.free_size = stat.avg_item_size = 0; GetBTPageStatistics(blkno, buffer, &stat); UnlockReleaseBuffer(buffer); relation_close(rel, 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"); j = 0; values[j++] = psprintf("%d", stat.blkno); values[j++] = psprintf("%c", stat.type); values[j++] = psprintf("%d", stat.live_items); values[j++] = psprintf("%d", stat.dead_items); values[j++] = psprintf("%d", stat.avg_item_size); values[j++] = psprintf("%d", stat.page_size); values[j++] = psprintf("%d", stat.free_size); values[j++] = psprintf("%d", stat.btpo_prev); values[j++] = psprintf("%d", stat.btpo_next); values[j++] = psprintf("%d", (stat.type == 'd') ? stat.btpo.xact : stat.btpo.level); values[j++] = psprintf("%d", stat.btpo_flags); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); PG_RETURN_DATUM(result); }
/* Clause 3.3.1.3 */ Datum MarketFeedFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i, j, n; int num_updated = 0; int rows_sent; int send_len = 0; int count = 0; int nitems_pq; int *p_tq; char *p_s; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; ArrayType *price_quote_p = PG_GETARG_ARRAYTYPE_P(0); char *status_submitted_p = (char *) PG_GETARG_TEXT_P(1); ArrayType *symbol_p = PG_GETARG_ARRAYTYPE_P(2); ArrayType *trade_qty = PG_GETARG_ARRAYTYPE_P(3); char *type_limit_buy_p = (char *) PG_GETARG_TEXT_P(4); char *type_limit_sell_p = (char *) PG_GETARG_TEXT_P(5); char *type_stop_loss_p = (char *) PG_GETARG_TEXT_P(6); enum mff1 { i_num_updated=0, i_send_len, i_symbol, i_trade_id, i_price_quote, i_trade_qty, i_trade_type }; Datum *transdatums_pq; int16 typlen_s; bool typbyval_s; char typalign_s; int16 typlen_tq; bool typbyval_tq; char typalign_tq; int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[7]; char nulls[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; char price_quote[S_PRICE_T_LEN + 1]; char status_submitted[ST_ID_LEN + 1]; char symbol[S_SYMB_LEN + 1]; char type_limit_buy[TT_ID_LEN + 1]; char type_limit_sell[TT_ID_LEN + 1]; char type_stop_loss[TT_ID_LEN + 1]; char *trade_id; char *req_price_quote; char *req_trade_type; char *req_trade_qty; /* * Prepare a values array for building the returned tuple. * This should be an array of C strings, which will * be processed later by the type input functions. */ values = (char **) palloc(sizeof(char *) * 7); values[i_num_updated] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_send_len] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); /* * FIXME: We don't know how many rows could be returned. The average * is supposed to be 4. Let's be prepared for 100, just to be safe. */ values[i_symbol] = (char *) palloc(((S_SYMB_LEN + 3) * 100 + 3) * sizeof(char)); values[i_trade_id] = (char *) palloc(((IDENT_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_price_quote] = (char *) palloc(((S_PRICE_T_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_qty] = (char *) palloc(((INTEGER_LEN + 1) * 100 + 2) * sizeof(char)); values[i_trade_type] = (char *) palloc(((TT_ID_LEN + 3) * 100 + 3) * sizeof(char)); /* * This might be overkill since we always expect single dimensions * arrays. * Should probably check the count of all the arrays to make sure * they are the same... */ get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s, &typalign_s); p_s = ARR_DATA_PTR(symbol_p); get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq, &typalign_tq); p_tq = (int *) ARR_DATA_PTR(trade_qty); deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i', &transdatums_pq, NULL, &nitems_pq); strcpy(status_submitted, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(status_submitted_p)))); strcpy(type_limit_buy, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_buy_p)))); strcpy(type_limit_sell, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_limit_sell_p)))); strcpy(type_stop_loss, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(type_stop_loss_p)))); #ifdef DEBUG dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); #endif /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(MFF1_statements); strcpy(values[i_symbol], "{"); strcpy(values[i_trade_id], "{"); strcpy(values[i_price_quote], "{"); strcpy(values[i_trade_type], "{"); strcpy(values[i_trade_qty], "{"); for (i = 0; i < nitems_pq; i++) { rows_sent = 0; strcpy(price_quote, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i]))); strcpy(symbol, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(p_s)))); /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("BEGIN;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: BEGIN not ok = %d", ret); } */ #ifdef DEBUG sprintf(sql, SQLMFF1_1, DatumGetCString(DirectFunctionCall1(numeric_out, transdatums_pq[i])), p_tq[i], symbol); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Float8GetDatum(atof(price_quote)); args[1] = Int64GetDatum(p_tq[i]); args[2] = CStringGetTextDatum(symbol); ret = SPI_execute_plan(MFF1_1, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[0].sql); } num_updated += SPI_processed; #ifdef DEBUG elog(NOTICE, "%d row(s) updated", num_updated); sprintf(sql, SQLMFF1_2, symbol, type_stop_loss, price_quote, type_limit_sell, price_quote, type_limit_buy, price_quote); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(symbol); args[1] = CStringGetTextDatum(type_stop_loss); args[2] = Float8GetDatum(atof(price_quote)); args[3] = CStringGetTextDatum(type_limit_sell); args[4] = args[2]; args[5] = CStringGetTextDatum(type_limit_buy); args[6] = args[2]; ret = SPI_execute_plan(MFF1_2, args, nulls, true, 0); if (ret != SPI_OK_SELECT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[1].sql); continue; } #ifdef DEBUG elog(NOTICE, "%d row(s) returned", SPI_processed); #endif /* DEBUG */ tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; n = SPI_processed; for (j = 0; j < n; j++) { tuple = tuptable->vals[j]; trade_id = SPI_getvalue(tuple, tupdesc, 1); req_price_quote = SPI_getvalue(tuple, tupdesc, 2); req_trade_type = SPI_getvalue(tuple, tupdesc, 3); req_trade_qty = SPI_getvalue(tuple, tupdesc, 4); #ifdef DEBUG elog(NOTICE, "trade_id = %s", trade_id); sprintf(sql, SQLMFF1_3, status_submitted, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(status_submitted); args[1] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_3, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[2].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_4, trade_id); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(atoll(trade_id)); ret = SPI_execute_plan(MFF1_4, args, nulls, false, 0); if (ret != SPI_OK_DELETE) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[3].sql); } #ifdef DEBUG sprintf(sql, SQLMFF1_5, trade_id, status_submitted); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[1] = CStringGetTextDatum(status_submitted); ret = SPI_execute_plan(MFF1_5, args, nulls, false, 0); if (ret != SPI_OK_INSERT) { dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p, trade_qty, type_limit_buy_p, type_limit_sell_p, type_stop_loss_p); FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[4].sql); } ++rows_sent; #ifdef DEBUG elog(NOTICE, "%d row(s) sent", rows_sent); #endif /* DEBUG */ if (count > 0) { strcat(values[i_symbol], ","); strcat(values[i_trade_id], ","); strcat(values[i_price_quote], ","); strcat(values[i_trade_type], ","); strcat(values[i_trade_qty], ","); } strcat(values[i_symbol], symbol); strcat(values[i_trade_id], trade_id); strcat(values[i_price_quote], req_price_quote); strcat(values[i_trade_type], req_trade_type); strcat(values[i_trade_qty], req_trade_qty); ++count; } /* FIXME: BEGIN/COMMIT statements not supported with SPI. */ /* ret = SPI_exec("COMMIT;", 0); if (ret == SPI_OK_SELECT) { } else { elog(NOTICE, "ERROR: COMMIT not ok = %d", ret); } */ send_len += rows_sent; p_s = att_addlength_pointer(p_s, typlen_s, p_s); p_s = (char *) att_align_nominal(p_s, typalign_s); } strcat(values[i_symbol], "}"); strcat(values[i_trade_id], "}"); strcat(values[i_price_quote], "}"); strcat(values[i_trade_qty], "}"); strcat(values[i_trade_type], "}"); sprintf(values[i_num_updated], "%d", num_updated); sprintf(values[i_send_len], "%d", send_len); funcctx->max_calls = 1; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; #ifdef DEBUG for (i = 0; i < 7; i++) { elog(NOTICE, "MFF1 OUT: %d %s", i, values[i]); } #endif /* DEBUG */ /* Build a tuple. */ tuple = BuildTupleFromCStrings(attinmeta, values); /* Make the tuple into a datum. */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* Do when there is no more left. */ SPI_finish(); SRF_RETURN_DONE(funcctx); } }
/* Function to return the list of grammar keywords */ Datum pg_get_keywords(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", CHAROID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "catdesc", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < NumScanKeywords) { char *values[3]; HeapTuple tuple; /* cast-away-const is ugly but alternatives aren't much better */ values[0] = (char *) ScanKeywords[funcctx->call_cntr].name; switch (ScanKeywords[funcctx->call_cntr].category) { case UNRESERVED_KEYWORD: values[1] = "U"; values[2] = _("unreserved"); break; case COL_NAME_KEYWORD: values[1] = "C"; values[2] = _("unreserved (cannot be function or type name)"); break; case TYPE_FUNC_NAME_KEYWORD: values[1] = "T"; values[2] = _("reserved (can be function or type name)"); break; case RESERVED_KEYWORD: values[1] = "R"; values[2] = _("reserved"); break; default: /* shouldn't be possible */ values[1] = NULL; values[2] = NULL; break; } tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } SRF_RETURN_DONE(funcctx); }
/* * create and populate the crosstab tuplestore using the provided source query */ static Tuplestorestate * get_crosstab_tuplestore(char *sql, HTAB *crosstab_hash, TupleDesc tupdesc, MemoryContext per_query_ctx, bool randomAccess) { Tuplestorestate *tupstore; int num_categories = hash_get_num_entries(crosstab_hash); AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tupdesc); char **values; HeapTuple tuple; int ret; int proc; /* initialize our tuplestore (while still in query context!) */ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR, "get_crosstab_tuplestore: SPI_connect returned %d", ret); /* Now retrieve the crosstab source rows */ ret = SPI_execute(sql, true, 0); proc = SPI_processed; /* Check for qualifying tuples */ if ((ret == SPI_OK_SELECT) && (proc > 0)) { SPITupleTable *spi_tuptable = SPI_tuptable; TupleDesc spi_tupdesc = spi_tuptable->tupdesc; int ncols = spi_tupdesc->natts; char *rowid; char *lastrowid = NULL; bool firstpass = true; int i, j; int result_ncols; if (num_categories == 0) { /* no qualifying category tuples */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("provided \"categories\" SQL must " \ "return 1 column of at least one row"))); } /* * The provided SQL query must always return at least three columns: * * 1. rowname the label for each row - column 1 in the final result * 2. category the label for each value-column in the final result 3. * value the values used to populate the value-columns * * If there are more than three columns, the last two are taken as * "category" and "values". The first column is taken as "rowname". * Additional columns (2 thru N-2) are assumed the same for the same * "rowname", and are copied into the result tuple from the first time * we encounter a particular rowname. */ if (ncols < 3) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid source data SQL statement"), errdetail("The provided SQL must return 3 " \ " columns; rowid, category, and values."))); result_ncols = (ncols - 2) + num_categories; /* Recheck to make sure we tuple descriptor still looks reasonable */ if (tupdesc->natts != result_ncols) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid return type"), errdetail("Query-specified return " \ "tuple has %d columns but crosstab " \ "returns %d.", tupdesc->natts, result_ncols))); /* allocate space */ values = (char **) palloc(result_ncols * sizeof(char *)); /* and make sure it's clear */ memset(values, '\0', result_ncols * sizeof(char *)); for (i = 0; i < proc; i++) { HeapTuple spi_tuple; crosstab_cat_desc *catdesc; char *catname; /* get the next sql result tuple */ spi_tuple = spi_tuptable->vals[i]; /* get the rowid from the current sql result tuple */ rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1); /* * if we're on a new output row, grab the column values up to * column N-2 now */ if (firstpass || !xstreq(lastrowid, rowid)) { /* * a new row means we need to flush the old one first, unless * we're on the very first row */ if (!firstpass) { /* rowid changed, flush the previous output row */ tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); for (j = 0; j < result_ncols; j++) xpfree(values[j]); } values[0] = rowid; for (j = 1; j < ncols - 2; j++) values[j] = SPI_getvalue(spi_tuple, spi_tupdesc, j + 1); /* we're no longer on the first pass */ firstpass = false; } /* look up the category and fill in the appropriate column */ catname = SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1); if (catname != NULL) { crosstab_HashTableLookup(crosstab_hash, catname, catdesc); if (catdesc) values[catdesc->attidx + ncols - 2] = SPI_getvalue(spi_tuple, spi_tupdesc, ncols); } xpfree(lastrowid); xpstrdup(lastrowid, rowid); } /* flush the last output row */ tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); } if (SPI_finish() != SPI_OK_FINISH) /* internal error */ elog(ERROR, "get_crosstab_tuplestore: SPI_finish() failed"); tuplestore_donestoring(tupstore); return tupstore; }
Datum connection_limits(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tupdesc; AttInMetadata *attinmeta; /* init on the first call */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); reset_rules(); check_all_rules(); /* number of rules */ funcctx->max_calls = rules->n_rules; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; funcctx->tuple_desc = tupdesc; /* switch back to the old context */ MemoryContextSwitchTo(oldcontext); } /* init the context */ funcctx = SRF_PERCALL_SETUP(); /* check if we have more data */ if (funcctx->max_calls > funcctx->call_cntr) { HeapTuple tuple; Datum result; Datum values[6]; bool nulls[6]; rule_t * rule = &(rules->rules[funcctx->call_cntr]); memset(nulls, 0, sizeof(nulls)); /* rule line */ values[0] = UInt32GetDatum(rule->line); /* database */ if (rule->fields & CHECK_DBNAME) values[1] = CStringGetTextDatum(rule->database); else nulls[1] = TRUE; /* username */ if (rule->fields & CHECK_USER) values[2] = CStringGetTextDatum(rule->user); else nulls[2] = TRUE; /* hostname or IP address */ if (rule->fields & CHECK_HOST) values[3] = CStringGetTextDatum(rule->hostname); else if (rule->fields & CHECK_IP) { char buffer[256]; memset(buffer, 0, 256); format_address(buffer, 256, (struct sockaddr*)&rule->ip, (struct sockaddr*)&rule->mask); values[3] = CStringGetTextDatum(buffer); } else nulls[3] = TRUE; /* count and limit */ values[4] = UInt32GetDatum(rule->count); values[5] = UInt32GetDatum(rule->limit); /* Build and return the tuple. */ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* Here we want to return another item: */ SRF_RETURN_NEXT(funcctx, result); } else { /* lock ProcArray (serialize the processes) */ LWLockRelease(ProcArrayLock); /* Here we are done returning items and just need to clean up: */ SRF_RETURN_DONE(funcctx); } }
static Datum plperl_func_handler(PG_FUNCTION_ARGS) { plperl_proc_desc *prodesc; SV *perlret; Datum retval; ReturnSetInfo *rsi; SV *array_ret = NULL; if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false); plperl_current_prodesc = prodesc; plperl_current_caller_info = fcinfo; plperl_current_tuple_store = 0; plperl_current_tuple_desc = 0; rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (prodesc->fn_retisset) { /* Check context before allowing the call to go through */ 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"))); } perlret = plperl_call_perl_func(prodesc, fcinfo); /************************************************************ * Disconnect from SPI manager and then create the return * values datum (if the input function does a palloc for it * this must not be allocated in the SPI memory context * because SPI_finish would free it). ************************************************************/ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish() failed"); if (prodesc->fn_retisset) { /* * If the Perl function returned an arrayref, we pretend that it * called return_next() for each element of the array, to handle old * SRFs that didn't know about return_next(). Any other sort of return * value is an error. */ if (SvTYPE(perlret) == SVt_RV && SvTYPE(SvRV(perlret)) == SVt_PVAV) { int i = 0; SV **svp = 0; AV *rav = (AV *) SvRV(perlret); while ((svp = av_fetch(rav, i, FALSE)) != NULL) { plperl_return_next(*svp); i++; } } else if (SvTYPE(perlret) != SVt_NULL) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("set-returning Perl function must return " "reference to array or use return_next"))); } rsi->returnMode = SFRM_Materialize; if (plperl_current_tuple_store) { rsi->setResult = plperl_current_tuple_store; rsi->setDesc = plperl_current_tuple_desc; } retval = (Datum) 0; } else if (SvTYPE(perlret) == SVt_NULL) { /* Return NULL if Perl code returned undef */ if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = ExprEndResult; fcinfo->isnull = true; retval = (Datum) 0; } else if (prodesc->fn_retistuple) { /* Return a perl hash converted to a Datum */ TupleDesc td; AttInMetadata *attinmeta; HeapTuple tup; if (!SvOK(perlret) || SvTYPE(perlret) != SVt_RV || SvTYPE(SvRV(perlret)) != SVt_PVHV) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("composite-returning Perl function " "must return reference to hash"))); } /* XXX should cache the attinmeta data instead of recomputing */ if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } attinmeta = TupleDescGetAttInMetadata(td); tup = plperl_build_tuple_result((HV *) SvRV(perlret), attinmeta); retval = HeapTupleGetDatum(tup); } else { /* Return a perl string converted to a Datum */ char *val; if (prodesc->fn_retisarray && SvROK(perlret) && SvTYPE(SvRV(perlret)) == SVt_PVAV) { array_ret = plperl_convert_to_pg_array(perlret); SvREFCNT_dec(perlret); perlret = array_ret; } val = SvPV(perlret, PL_na); retval = FunctionCall3(&prodesc->result_in_func, CStringGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), Int32GetDatum(-1)); } if (array_ret == NULL) SvREFCNT_dec(perlret); return retval; }
static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo) { Datum result; BlockNumber nblocks; BlockNumber blkno; BTIndexStat indexStat; BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); /* * Read metapage */ { Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 0, RBM_NORMAL, bstrategy); Page page = BufferGetPage(buffer); BTMetaPageData *metad = BTPageGetMeta(page); indexStat.version = metad->btm_version; indexStat.level = metad->btm_level; indexStat.root_blkno = metad->btm_root; ReleaseBuffer(buffer); } /* -- init counters -- */ indexStat.internal_pages = 0; indexStat.leaf_pages = 0; indexStat.empty_pages = 0; indexStat.deleted_pages = 0; indexStat.max_avail = 0; indexStat.free_space = 0; indexStat.fragments = 0; /* * Scan all blocks except the metapage */ nblocks = RelationGetNumberOfBlocks(rel); for (blkno = 1; blkno < nblocks; blkno++) { Buffer buffer; Page page; BTPageOpaque opaque; CHECK_FOR_INTERRUPTS(); /* Read and lock buffer */ buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); LockBuffer(buffer, BUFFER_LOCK_SHARE); page = BufferGetPage(buffer); opaque = (BTPageOpaque) PageGetSpecialPointer(page); /* Determine page type, and update totals */ if (P_ISDELETED(opaque)) indexStat.deleted_pages++; else if (P_IGNORE(opaque)) indexStat.empty_pages++; /* this is the "half dead" state */ else if (P_ISLEAF(opaque)) { int max_avail; max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData); indexStat.max_avail += max_avail; indexStat.free_space += PageGetFreeSpace(page); indexStat.leaf_pages++; /* * If the next leaf is on an earlier block, it means a * fragmentation. */ if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno) indexStat.fragments++; } else indexStat.internal_pages++; /* Unlock and release buffer */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); } relation_close(rel, AccessShareLock); /*---------------------------- * Build a result tuple *---------------------------- */ { TupleDesc tupleDesc; int j; char *values[10]; HeapTuple tuple; /* 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"); j = 0; values[j++] = psprintf("%d", indexStat.version); values[j++] = psprintf("%d", indexStat.level); values[j++] = psprintf(INT64_FORMAT, (1 + /* include the metapage in index_size */ indexStat.leaf_pages + indexStat.internal_pages + indexStat.deleted_pages + indexStat.empty_pages) * BLCKSZ); values[j++] = psprintf("%u", indexStat.root_blkno); values[j++] = psprintf(INT64_FORMAT, indexStat.internal_pages); values[j++] = psprintf(INT64_FORMAT, indexStat.leaf_pages); values[j++] = psprintf(INT64_FORMAT, indexStat.empty_pages); values[j++] = psprintf(INT64_FORMAT, indexStat.deleted_pages); if (indexStat.max_avail > 0) values[j++] = psprintf("%.2f", 100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0); else values[j++] = pstrdup("NaN"); if (indexStat.leaf_pages > 0) values[j++] = psprintf("%.2f", (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0); else values[j++] = pstrdup("NaN"); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); } return result; }
/*------------------------------------------------------- * bt_page_items() * * Get IndexTupleData set in a btree page * * Usage: SELECT * FROM bt_page_items('t1_pkey', 1); *------------------------------------------------------- */ Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { RangeVar *relrv; Relation rel; Buffer buffer; BTPageOpaque opaque; TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum bt_page_items_bytea(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Datum result; FuncCallContext *fctx; struct user_args *uargs; int raw_page_size; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { BTPageOpaque opaque; MemoryContext mctx; TupleDesc tupleDesc; raw_page_size = VARSIZE(raw_page) - VARHDRSZ; if (raw_page_size < SizeOfPageHeaderData) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = VARDATA(raw_page); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISMETA(opaque)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("block is a meta page"))); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs); SRF_RETURN_DONE(fctx); } }
/* ------------------------------------------------ * bt_metap() * * Get a btree's meta-page information * * Usage: SELECT * FROM bt_metap('t1_pkey') * ------------------------------------------------ */ Datum bt_metap(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); Datum result; Relation rel; RangeVar *relrv; BTMetaPageData *metad; TupleDesc tupleDesc; int j; char *values[6]; Buffer buffer; Page page; HeapTuple tuple; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); buffer = ReadBuffer(rel, 0); LockBuffer(buffer, BUFFER_LOCK_SHARE); page = BufferGetPage(buffer); metad = BTPageGetMeta(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"); j = 0; values[j++] = psprintf("%d", metad->btm_magic); values[j++] = psprintf("%d", metad->btm_version); values[j++] = psprintf("%d", metad->btm_root); values[j++] = psprintf("%d", metad->btm_level); values[j++] = psprintf("%d", metad->btm_fastroot); values[j++] = psprintf("%d", metad->btm_fastlevel); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_DATUM(result); }
/* * Note: plperl_return_next is called both in Postgres and Perl contexts. * We report any errors in Postgres fashion (via ereport). If called in * Perl context, it is SPI.xs's responsibility to catch the error and * convert to a Perl error. We assume (perhaps without adequate justification) * that we need not abort the current transaction if the Perl code traps the * error. */ void plperl_return_next(SV *sv) { plperl_proc_desc *prodesc = plperl_current_prodesc; FunctionCallInfo fcinfo = plperl_current_caller_info; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext cxt; HeapTuple tuple; TupleDesc tupdesc; if (!sv) return; if (!prodesc->fn_retisset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use return_next in a non-SETOF function"))); if (prodesc->fn_retistuple && !(SvOK(sv) && SvTYPE(sv) == SVt_RV && SvTYPE(SvRV(sv)) == SVt_PVHV)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("setof-composite-returning Perl function " "must call return_next with reference to hash"))); cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); if (!plperl_current_tuple_store) plperl_current_tuple_store = tuplestore_begin_heap(true, false, work_mem); if (prodesc->fn_retistuple) { TypeFuncClass rettype; AttInMetadata *attinmeta; rettype = get_call_result_type(fcinfo, NULL, &tupdesc); tupdesc = CreateTupleDescCopy(tupdesc); attinmeta = TupleDescGetAttInMetadata(tupdesc); tuple = plperl_build_tuple_result((HV *) SvRV(sv), attinmeta); } else { Datum ret; bool isNull; tupdesc = CreateTupleDescCopy(rsi->expectedDesc); if (SvOK(sv) && SvTYPE(sv) != SVt_NULL) { char *val = SvPV(sv, PL_na); ret = FunctionCall3(&prodesc->result_in_func, PointerGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), Int32GetDatum(-1)); isNull = false; } else { ret = (Datum) 0; isNull = true; } tuple = heap_form_tuple(tupdesc, &ret, &isNull); } if (!plperl_current_tuple_desc) plperl_current_tuple_desc = tupdesc; tuplestore_puttuple(plperl_current_tuple_store, tuple); heap_freetuple(tuple); MemoryContextSwitchTo(cxt); }
/* ----------------------------------------------- * bt_page() * * Usage: SELECT * FROM bt_page('t1_pkey', 1); * ----------------------------------------------- */ datum_t bt_page_stats(PG_FUNC_ARGS) { text *relname = ARG_TEXT_P(0); uint32 blkno = ARG_UINT32(1); buf_id_t buffer; struct relation * rel; range_var_n *relrv; datum_t result; struct heap_tuple * tuple; struct tuple *tupleDesc; int j; char *values[11]; BTPageStat stat; if (!superuser()) ereport(ERROR, (errcode(E_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); relrv = nl_to_range_var(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, ACCESS_SHR_LOCK); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", REL_NAME(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (REL_IS_OTHER_TMP(rel)) ereport(ERROR, (errcode(E_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = read_buf(rel, blkno); /* keep compiler quiet */ stat.btpo_prev = stat.btpo_next = INVALID_BLK_NR; stat.btpo_flags = stat.free_size = stat.avg_item_size = 0; GetBTPageStatistics(blkno, buffer, &stat); /* 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"); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.blkno); values[j] = palloc(32); snprintf(values[j++], 32, "%c", stat.type); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.live_items); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.dead_items); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.avg_item_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.page_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.free_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_prev); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_next); values[j] = palloc(32); if (stat.type == 'd') snprintf(values[j++], 32, "%d", stat.btpo.xact); else snprintf(values[j++], 32, "%d", stat.btpo.level); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_flags); tuple = build_tuple_from_cstrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); release_buf(buffer); relation_close(rel, ACCESS_SHR_LOCK); RET_DATUM(result); }
Datum crosstab(PG_FUNCTION_ARGS) { char *sql = text_to_cstring(PG_GETARG_TEXT_PP(0)); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Tuplestorestate *tupstore; TupleDesc tupdesc; int call_cntr; int max_calls; AttInMetadata *attinmeta; SPITupleTable *spi_tuptable; TupleDesc spi_tupdesc; bool firstpass; char *lastrowid; int i; int num_categories; MemoryContext per_query_ctx; MemoryContext oldcontext; int ret; int proc; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR, "crosstab: SPI_connect returned %d", ret); /* Retrieve the desired rows */ ret = SPI_execute(sql, true, 0); proc = SPI_processed; /* If no qualifying tuples, fall out early */ if (ret != SPI_OK_SELECT || proc <= 0) { SPI_finish(); rsinfo->isDone = ExprEndResult; PG_RETURN_NULL(); } spi_tuptable = SPI_tuptable; spi_tupdesc = spi_tuptable->tupdesc; /*---------- * The provided SQL query must always return three columns. * * 1. rowname * the label or identifier for each row in the final result * 2. category * the label or identifier for each column in the final result * 3. values * the value for each column in the final result *---------- */ if (spi_tupdesc->natts != 3) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid source data SQL statement"), errdetail("The provided SQL must return 3 " "columns: rowid, category, and values."))); /* get a tuple descriptor for our result type */ switch (get_call_result_type(fcinfo, NULL, &tupdesc)) { case TYPEFUNC_COMPOSITE: /* success */ break; case TYPEFUNC_RECORD: /* failed to determine actual type of RECORD */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); break; default: /* result type isn't composite */ elog(ERROR, "return type must be a row type"); break; } /* * Check that return tupdesc is compatible with the data we got from SPI, * at least based on number and type of attributes */ if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("return and sql tuple descriptions are " \ "incompatible"))); /* * switch to long-lived memory context */ oldcontext = MemoryContextSwitchTo(per_query_ctx); /* make sure we have a persistent copy of the result tupdesc */ tupdesc = CreateTupleDescCopy(tupdesc); /* initialize our tuplestore in long-lived context */ tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem); MemoryContextSwitchTo(oldcontext); /* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); /* total number of tuples to be examined */ max_calls = proc; /* the return tuple always must have 1 rowid + num_categories columns */ num_categories = tupdesc->natts - 1; firstpass = true; lastrowid = NULL; for (call_cntr = 0; call_cntr < max_calls; call_cntr++) { bool skip_tuple = false; char **values; /* allocate and zero space */ values = (char **) palloc0((1 + num_categories) * sizeof(char *)); /* * now loop through the sql results and assign each value in sequence * to the next category */ for (i = 0; i < num_categories; i++) { HeapTuple spi_tuple; char *rowid; /* see if we've gone too far already */ if (call_cntr >= max_calls) break; /* get the next sql result tuple */ spi_tuple = spi_tuptable->vals[call_cntr]; /* get the rowid from the current sql result tuple */ rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1); /* * If this is the first pass through the values for this rowid, * set the first column to rowid */ if (i == 0) { xpstrdup(values[0], rowid); /* * Check to see if the rowid is the same as that of the last * tuple sent -- if so, skip this tuple entirely */ if (!firstpass && xstreq(lastrowid, rowid)) { xpfree(rowid); skip_tuple = true; break; } } /* * If rowid hasn't changed on us, continue building the output * tuple. */ if (xstreq(rowid, values[0])) { /* * Get the next category item value, which is always attribute * number three. * * Be careful to assign the value to the array index based on * which category we are presently processing. */ values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3); /* * increment the counter since we consume a row for each * category, but not for last pass because the outer loop will * do that for us */ if (i < (num_categories - 1)) call_cntr++; xpfree(rowid); } else { /* * We'll fill in NULLs for the missing values, but we need to * decrement the counter since this sql result row doesn't * belong to the current output tuple. */ call_cntr--; xpfree(rowid); break; } } if (!skip_tuple) { HeapTuple tuple; /* build the tuple and store it */ tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); heap_freetuple(tuple); } /* Remember current rowid */ xpfree(lastrowid); xpstrdup(lastrowid, values[0]); firstpass = false; /* Clean up */ for (i = 0; i < num_categories + 1; i++) if (values[i] != NULL) pfree(values[i]); pfree(values); } /* let the caller know we're sending back a tuplestore */ rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; /* release SPI related resources (and return to caller's context) */ SPI_finish(); return (Datum) 0; }
datum_t bt_page_items(PG_FUNC_ARGS) { text *relname = ARG_TEXT_P(0); uint32 blkno = ARG_UINT32(1); datum_t result; char *values[6]; struct heap_tuple * tuple; struct fcall_ctx *fctx; struct mctx * mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(E_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { range_var_n *relrv; struct relation * rel; buf_id_t buffer; struct bt_page_opaque * opaque; struct tuple * tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = nl_to_range_var(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, ACCESS_SHR_LOCK); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", REL_NAME(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (REL_IS_OTHER_TMP(rel)) ereport(ERROR, (errcode(E_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = read_buf(rel, blkno); /* * We copy the page into local storage to avoid holding pin on the * buffer longer than we must, and possibly failing to release it at * all if the calling query doesn't fetch all rows. */ mctx = mctx_switch(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLK_SZ); memcpy(uargs->page, BUF_PAGE(buffer), BLK_SZ); release_buf(buffer); relation_close(rel, ACCESS_SHR_LOCK); uargs->offset = FIRST_ITEM_ID; opaque = (struct bt_page_opaque *) PAGE_SPECIAL_PTR(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PAGE_MAX_ITEM_ID(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; mctx_switch(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { struct item_id * id; struct index_tuple * itup; int j; int off; int dlen; char *dump; char *ptr; id = PAGE_ITEM_ID(uargs->page, uargs->offset); if (!ITEMID_VALID(id)) elog(ERROR, "invalid ItemId"); itup = (struct index_tuple *) PAGE_GET_ITEM(uargs->page, id); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", BLK_ID_TO_BLK_NR(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) INDEX_TUPLE_SZ(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", INDEX_TUPLE_HAS_NULLS(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", INDEX_TUPLE_HAS_VAR(itup) ? 't' : 'f'); ptr = (char *) itup + INDEX_TUPLE_DATA_OFFSET(itup->t_info); dlen = INDEX_TUPLE_SZ(itup) - INDEX_TUPLE_DATA_OFFSET(itup->t_info); dump = pzalloc(dlen * 3 + 1); values[j] = dump; for (off = 0; off < dlen; off++) { if (off > 0) *dump++ = ' '; sprintf(dump, "%02x", *(ptr + off) & 0xff); dump += 2; } tuple = build_tuple_from_cstrings(fctx->attinmeta, values); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum connectby_text(PG_FUNCTION_ARGS) { char *relname = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1)); char *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2)); char *start_with = text_to_cstring(PG_GETARG_TEXT_PP(3)); int max_depth = PG_GETARG_INT32(4); char *branch_delim = NULL; bool show_branch = false; bool show_serial = false; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; AttInMetadata *attinmeta; MemoryContext per_query_ctx; MemoryContext oldcontext; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize) || rsinfo->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); if (fcinfo->nargs == 6) { branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(5)); show_branch = true; } else /* default is no show, tilde for the delimiter */ branch_delim = pstrdup("~"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* get the requested return tuple description */ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); /* does it meet our needs */ validateConnectbyTupleDesc(tupdesc, show_branch, show_serial); /* OK, use it then */ attinmeta = TupleDescGetAttInMetadata(tupdesc); /* OK, go to work */ rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = connectby(relname, key_fld, parent_key_fld, NULL, branch_delim, start_with, max_depth, show_branch, show_serial, per_query_ctx, rsinfo->allowedModes & SFRM_Materialize_Random, attinmeta); rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); /* * SFRM_Materialize mode expects us to return a NULL Datum. The actual * tuples are in our tuplestore and passed back through rsinfo->setResult. * rsinfo->setDesc is set to the tuple description that we actually used * to build our tuples with, so the caller can verify we did what it was * expecting. */ return (Datum) 0; }
/* ------------------------------------------------ * bt_metap() * * Get a btree's meta-page information * * Usage: SELECT * FROM bt_metap('t1_pkey') * ------------------------------------------------ */ datum_t bt_metap(PG_FUNC_ARGS) { text *relname = ARG_TEXT_P(0); datum_t result; struct relation * rel; range_var_n *relrv; struct bt_meta_page *metad; struct tuple * tupleDesc; int j; char *values[6]; buf_id_t buffer; page_p page; struct heap_tuple * tuple; if (!superuser()) ereport(ERROR, (errcode(E_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); relrv = nl_to_range_var(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, ACCESS_SHR_LOCK); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", REL_NAME(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (REL_IS_OTHER_TMP(rel)) ereport(ERROR, (errcode(E_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); buffer = read_buf(rel, 0); page = BUF_PAGE(buffer); metad = BT_PAGE_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"); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_magic); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_version); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_root); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_level); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_fastroot); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_fastlevel); tuple = build_tuple_from_cstrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); release_buf(buffer); relation_close(rel, ACCESS_SHR_LOCK); RET_DATUM(result); }
Datum pg_logdir_ls(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; directory_fctx *fctx; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only superuser can list the log directory")))); if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")))); if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(directory_fctx)); tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", TIMESTAMPOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); fctx->location = pstrdup(Log_directory); fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fctx->location))); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (directory_fctx *) funcctx->user_fctx; while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { char *values[2]; HeapTuple tuple; char timestampbuf[32]; char *field[MAXDATEFIELDS]; char lowstr[MAXDATELEN + 1]; int dtype; int nf, ftype[MAXDATEFIELDS]; fsec_t fsec; int tz = 0; struct pg_tm date; /* * Default format: postgresql-YYYY-MM-DD_HHMMSS.log */ if (strlen(de->d_name) != 32 || strncmp(de->d_name, "postgresql-", 11) != 0 || de->d_name[21] != '_' || strcmp(de->d_name + 28, ".log") != 0) continue; /* extract timestamp portion of filename */ strcpy(timestampbuf, de->d_name + 11); timestampbuf[17] = '\0'; /* parse and decode expected timestamp to verify it's OK format */ if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf)) continue; if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) continue; /* Seems the timestamp is OK; prepare and return tuple */ values[0] = timestampbuf; values[1] = psprintf("%s/%s", fctx->location, de->d_name); tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); }
/* * pgstattuple_real * * The real work occurs here */ static Datum pgstattuple_real(Relation rel, FunctionCallInfo fcinfo) { HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; BlockNumber block = 0; /* next block to count free space in */ BlockNumber tupblock; Buffer buffer; uint64 table_len; uint64 tuple_len = 0; uint64 dead_tuple_len = 0; uint64 tuple_count = 0; uint64 dead_tuple_count = 0; double tuple_percent; double dead_tuple_percent; uint64 free_space = 0; /* free/reusable space in bytes */ double free_percent; /* free/reusable space in % */ TupleDesc tupdesc; AttInMetadata *attinmeta; char **values; int i; Datum result; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* make sure we have a persistent copy of the tupdesc */ tupdesc = CreateTupleDescCopy(tupdesc); /* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); scan = heap_beginscan(rel, SnapshotAny, 0, NULL); nblocks = scan->rs_nblocks; /* # blocks to be scanned */ /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { /* must hold a buffer lock to call HeapTupleSatisfiesNow */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesNow(tuple->t_data, scan->rs_cbuf)) { tuple_len += tuple->t_len; tuple_count++; } else { dead_tuple_len += tuple->t_len; dead_tuple_count++; } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* * To avoid physically reading the table twice, try to do the * free-space scan in parallel with the heap scan. However, * heap_getnext may find no tuples on a given page, so we cannot * simply examine the pages returned by the heap scan. */ tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); while (block <= tupblock) { buffer = ReadBuffer(rel, block); LockBuffer(buffer, BUFFER_LOCK_SHARE); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); block++; } } heap_endscan(scan); while (block < nblocks) { buffer = ReadBuffer(rel, block); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); ReleaseBuffer(buffer); block++; } heap_close(rel, AccessShareLock); table_len = (uint64) nblocks *BLCKSZ; if (nblocks == 0) { tuple_percent = 0.0; dead_tuple_percent = 0.0; free_percent = 0.0; } else { tuple_percent = (double) tuple_len *100.0 / table_len; dead_tuple_percent = (double) dead_tuple_len *100.0 / table_len; free_percent = (double) free_space *100.0 / table_len; } /* * Prepare a values array for constructing the tuple. This should be an * array of C strings which will be processed later by the appropriate * "in" functions. */ values = (char **) palloc(NCOLUMNS * sizeof(char *)); for (i = 0; i < NCOLUMNS; i++) values[i] = (char *) palloc(NCHARS * sizeof(char)); i = 0; snprintf(values[i++], NCHARS, INT64_FORMAT, table_len); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, tuple_len); snprintf(values[i++], NCHARS, "%.2f", tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, dead_tuple_len); snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, free_space); snprintf(values[i++], NCHARS, "%.2f", free_percent); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* Clean up */ for (i = 0; i < NCOLUMNS; i++) pfree(values[i]); pfree(values); return (result); }