Datum dbms_pipe_list_pipes(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tupdesc; AttInMetadata *attinmeta; PipesFctx *fctx; float8 endtime; int cycle = 0; int timeout = 10; if (SRF_IS_FIRSTCALL()) { int i; MemoryContext oldcontext; bool has_lock = false; WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) { has_lock = true; break; } WATCH_POST(timeout, endtime, cycle); if (!has_lock) LOCK_ERROR(); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(PipesFctx)); funcctx->user_fctx = fctx; fctx->pipe_nth = 0; #if PG_VERSION_NUM >= 120000 tupdesc = CreateTemplateTupleDesc(DB_PIPES_COLS); #else tupdesc = CreateTemplateTupleDesc(DB_PIPES_COLS, false); #endif i = 0; TupleDescInitEntry(tupdesc, ++i, "name", VARCHAROID, -1, 0); TupleDescInitEntry(tupdesc, ++i, "items", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, ++i, "size", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, ++i, "limit", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, ++i, "private", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, ++i, "owner", VARCHAROID, -1, 0); Assert(i == DB_PIPES_COLS); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (PipesFctx *) funcctx->user_fctx; while (fctx->pipe_nth < MAX_PIPES) { if (pipes[fctx->pipe_nth].is_valid) { Datum result; HeapTuple tuple; char *values[DB_PIPES_COLS]; char items[16]; char size[16]; char limit[16]; /* name */ values[0] = pipes[fctx->pipe_nth].pipe_name; /* items */ snprintf(items, lengthof(items), "%d", pipes[fctx->pipe_nth].count); values[1] = items; /* items */ snprintf(size, lengthof(size), "%d", pipes[fctx->pipe_nth].size); values[2] = size; /* limit */ if (pipes[fctx->pipe_nth].limit != -1) { snprintf(limit, lengthof(limit), "%d", pipes[fctx->pipe_nth].limit); values[3] = limit; } else values[3] = NULL; /* private */ values[4] = (pipes[fctx->pipe_nth].creator ? "true" : "false"); /* owner */ values[5] = pipes[fctx->pipe_nth].creator; tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); result = HeapTupleGetDatum(tuple); fctx->pipe_nth += 1; SRF_RETURN_NEXT(funcctx, result); } fctx->pipe_nth += 1; } LWLockRelease(shmem_lockid); SRF_RETURN_DONE(funcctx); }
Datum xlogviewer(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; XLogViewerContext *context; int call_cntr; int max_calls; TupleDesc tupdesc; AttInMetadata *attinmeta; int fd; int logRecOff; int32 logPageOff; XLogRecord *record; XLogRecPtr curRecPtr; uint32 logId; uint32 logSeg; TimeLineID logTLI; char *fnamebase; bool ignore_errors = (PG_NARGS() == 2?PG_GETARG_BOOL(1):false); const char * const RM_names[RM_MAX_ID+1] = { "XLOG ", /* 0 */ "XACT ", /* 1 */ "SMGR ", /* 2 */ "CLOG ", /* 3 */ "DBASE", /* 4 */ "TBSPC", /* 5 */ "MXACT", /* 6 */ "RM 7", /* 7 */ "RM 8", /* 8 */ "RM 9", /* 9 */ "HEAP ", /* 10 */ "BTREE", /* 11 */ "HASH ", /* 12 */ "RTREE", /* 13 */ "GIST ", /* 14 */ "SEQ " /* 15 */ }; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; char *xlog_file = GET_STR(PG_GETARG_TEXT_P(0)); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 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; /* Try to open XLOG file */ if ((fd = open(xlog_file, O_RDONLY | PG_BINARY, 0)) < 0) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Couldn't open xlog-file: %s", xlog_file))); SRF_RETURN_DONE(funcctx); } /* * Extract logfile id and segment from file name */ fnamebase = strrchr(xlog_file, '/'); if (fnamebase) fnamebase++; else fnamebase = xlog_file; if (sscanf(fnamebase, "%8x%8x%8x", &logTLI, &logId, &logSeg) != 3) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Can't recognize logfile name '%s'", fnamebase))); SRF_RETURN_DONE(funcctx); } context = palloc(sizeof(XLogViewerContext)); context->logFd = fd; context->logId = logId; context->logSeg = logSeg; context->logPageOff = -BLCKSZ; context->logRecOff = 0; funcctx->user_fctx = (void *) context; 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; context = (XLogViewerContext *) funcctx->user_fctx; fd = context->logFd; logId = context->logId; logSeg = context->logSeg; logRecOff = context->logRecOff; logPageOff = context->logPageOff; curRecPtr = context->curRecPtr; if (call_cntr < max_calls) /* do when there is more left to send */ { char **values; HeapTuple tuple; Datum result; if((record = readRecord(&fd, &logRecOff, &logPageOff, &curRecPtr, logId, logSeg, ignore_errors))) { funcctx->max_calls += 1; context->logRecOff = logRecOff; context->logPageOff = logPageOff; context->curRecPtr = curRecPtr; /* build a tuple */ values = (char **) palloc(7 * sizeof(char *)); values[0] = (char *) palloc(16 * sizeof(char)); values[1] = (char *) palloc(16 * sizeof(char)); values[2] = (char *) palloc(5 * sizeof(char)); values[3] = (char *) palloc(16 * sizeof(char)); values[4] = (char *) palloc(16 * sizeof(char)); values[5] = (char *) palloc(16 * sizeof(char)); values[6] = (char *) palloc(16 * sizeof(char)); snprintf(values[0], 16, "%d", record->xl_rmid); snprintf(values[1], 16, "%d", record->xl_xid); sprintf(values[2], "%s", RM_names[record->xl_rmid]); snprintf(values[3], 16, "%2X", record->xl_info); snprintf(values[4], 16, "%d", record->xl_len); snprintf(values[5], 16, "%d", record->xl_tot_len); snprintf(values[6], 16, "%s", getXactName(record->xl_rmid, record->xl_info)); tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { close(fd); SRF_RETURN_DONE(funcctx); } } else /* do when there is no more left */ { close(fd); SRF_RETURN_DONE(funcctx); } }
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 read 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); }
Datum parse_uri(PG_FUNCTION_ARGS) { TupleDesc tupledesc; AttInMetadata *attinmeta; text* input=PG_GETARG_TEXT_P(0); /* Function is defined STRICT in SQL, so no NULL check is needed. */ bool normalize=PG_GETARG_BOOL(1); bool parse_query=PG_GETARG_BOOL(2); char* inp=palloc((1+VARSIZE(input)-VARHDRSZ)*sizeof(char)); if (!inp) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpy(inp,VARDATA(input),VARSIZE(input)-VARHDRSZ); inp[VARSIZE(input)-VARHDRSZ]='\0'; /* Function internals start here */ int i; int memctr; UriPathSegmentA* pathseg; int quit; char* writehere; UriParserStateA state; UriUriA uri; state.uri = &uri; if (uriParseUriA(&state, inp) != URI_SUCCESS) { uriFreeUriMembersA(&uri); /* ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unable to parse URI."))); */ PG_RETURN_NULL(); } if (normalize) { if (uriNormalizeSyntaxA(&uri) != URI_SUCCESS) { uriFreeUriMembersA(&uri); PG_RETURN_NULL(); } } UriQueryListA* queryList=NULL; int itemCount; if (parse_query&&(uri.query.afterLast!=uri.query.first)) { if (uriDissectQueryMallocA(&queryList, &itemCount, uri.query.first, uri.query.afterLast) != URI_SUCCESS) { uriFreeUriMembersA(&uri); uriFreeQueryListA(queryList); PG_RETURN_NULL(); } } /* Function internals finish here */ if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); PG_RETURN_NULL(); } /* This error should never happen, because the SQL function is defined as returning a uri_type. */ attinmeta = TupleDescGetAttInMetadata(tupledesc); char** retval=(char**) palloc(13*sizeof(char*)); if (!retval) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } if (uri.scheme.afterLast==uri.scheme.first) { retval[0]=NULL; } else { retval[0]=(char*) palloc((1+(uri.scheme.afterLast-uri.scheme.first))*sizeof(char)); /* scheme, e.g. "http" */ if (!retval[0]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[0],uri.scheme.first,uri.scheme.afterLast-uri.scheme.first); } if (uri.userInfo.afterLast==uri.userInfo.first) { retval[1]=NULL; } else { retval[1]=(char*) palloc((1+(uri.userInfo.afterLast-uri.userInfo.first))*sizeof(char)); /* userInfo, e.g. "gpadmin" */ if (!retval[1]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[1],uri.userInfo.first,uri.userInfo.afterLast-uri.userInfo.first); } if (uri.hostText.afterLast==uri.hostText.first) { retval[2]=NULL; } else { retval[2]=(char*) palloc((1+(uri.hostText.afterLast-uri.hostText.first))*sizeof(char)); /* hostText, e.g. "192.165.0.0" */ if (!retval[2]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[2],uri.hostText.first,uri.hostText.afterLast-uri.hostText.first); } if (uri.hostData.ip4==NULL) { retval[3]=NULL; } else { retval[3]=(char*) palloc(17*sizeof(char)); /* IPv4 */ if (!retval[3]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpy(retval[3],"\\000\\000\\000\\000",17); for(i=0;i<4;++i) { retval[3][1+4*i]+=uri.hostData.ip4->data[i]>> 6; retval[3][2+4*i]+=(uri.hostData.ip4->data[i]>> 3)&7; retval[3][3+4*i]+=uri.hostData.ip4->data[i]&7; } } if (uri.hostData.ip6==NULL) { retval[4]=NULL; } else { retval[4]=(char*) palloc(65*sizeof(char)); /* IPv6 */ if (!retval[4]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpy(retval[4],"\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000",65); for(i=0;i<16;++i) { retval[4][1+4*i]+=uri.hostData.ip6->data[i]>> 6; retval[4][2+4*i]+=(uri.hostData.ip6->data[i]>> 3)&7; retval[4][3+4*i]+=uri.hostData.ip6->data[i]&7; } } if (uri.hostData.ipFuture.afterLast==uri.hostData.ipFuture.first) { retval[5]=NULL; } else { retval[5]=(char*) palloc((1+(uri.hostData.ipFuture.afterLast-uri.hostData.ipFuture.first))*sizeof(char)); /* ipFuture, text field */ if (!retval[5]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[5],uri.hostData.ipFuture.first,uri.hostData.ipFuture.afterLast-uri.hostData.ipFuture.first); } if (uri.portText.afterLast==uri.portText.first) { retval[6]=NULL; } else { retval[6]=(char*) palloc((1+(uri.portText.afterLast-uri.portText.first))*sizeof(char)); /* portText, e.g. "80" */ if (!retval[6]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[6],uri.portText.first,uri.portText.afterLast-uri.portText.first); } if (uri.pathHead==NULL) { retval[7]=NULL; } else { memctr=2; pathseg=uri.pathHead; do { quit=((pathseg==uri.pathTail)||(pathseg->next==NULL)); memctr+=3+2*(pathseg->text.afterLast-pathseg->text.first); pathseg=pathseg->next; } while (!quit); if (memctr==2) { ++memctr; } retval[7]=(char*) palloc(memctr*sizeof(char)); /* path */ /* e.g. "{usr,local,lib}" */ if (!retval[7]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } writehere=retval[7]; *writehere='{'; ++writehere; pathseg=uri.pathHead; do { quit=((pathseg==uri.pathTail)||(pathseg->next==NULL)); writehere=memenc(writehere,pathseg->text.first,pathseg->text.afterLast-pathseg->text.first); *writehere=','; ++writehere; pathseg=pathseg->next; } while (!quit); if (memctr!=3) { --writehere; } memcpy(writehere,"}",2); } if (uri.query.afterLast==uri.query.first) { retval[8]=NULL; } else { retval[8]=(char*) palloc((1+(uri.query.afterLast-uri.query.first))*sizeof(char)); /* query without leading "?" */ if (!retval[8]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[8],uri.query.first,uri.query.afterLast-uri.query.first); } if (uri.fragment.afterLast==uri.fragment.first) { retval[9]=NULL; } else { retval[9]=(char*) palloc((1+(uri.fragment.afterLast-uri.fragment.first))*sizeof(char)); /* fragment without leading "#" */ if (!retval[9]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpz(retval[9],uri.fragment.first,uri.fragment.afterLast-uri.fragment.first); } if (uri.absolutePath) { retval[10]=(char*) palloc(5*sizeof(char)); /* absolutePath */ if (!retval[10]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpy(retval[10],"true",5); } else { retval[10]=(char*) palloc(6*sizeof(char)); /* absolutePath */ if (!retval[10]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } memcpy(retval[10],"false",6); } if (parse_query) { int key_counter=2; int val_counter=2; int counter=0; for(UriQueryListA* it=queryList;(counter!=itemCount)&&(it!=NULL); it=it->next,++counter) { if (it->key==NULL) { key_counter+=3; /* should never reach here. */ } else { key_counter+=3+2*strlen(it->key); } if (it->value==NULL) { val_counter+=3; /* currently no way to distinguish empty string value (?a=) from null value (?a). This is a GPDB limitation. */ } else { val_counter+=3+2*strlen(it->value); } } if (key_counter==2) { ++key_counter; } if (val_counter==2) { ++val_counter; } retval[11]=palloc(key_counter*sizeof(char)); if (!retval[11]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } retval[12]=palloc(val_counter*sizeof(char)); if (!retval[12]) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Memory allocation failed."))); PG_RETURN_NULL(); } retval[11][0]='{'; retval[12][0]='{'; char* key_ptr=retval[11]+1; char* val_ptr=retval[12]+1; counter=0; for(UriQueryListA* it=queryList;(counter!=itemCount)&&(it!=NULL); it=it->next,++counter) { if (it->key==NULL) { *key_ptr='"'; ++key_ptr; *key_ptr='"'; ++key_ptr; *key_ptr=','; ++key_ptr; /* should never reach here. */ } else { key_ptr=strenc(key_ptr,it->key); *key_ptr=','; ++key_ptr; } if (it->value==NULL) { *val_ptr='"'; ++val_ptr; *val_ptr='"'; ++val_ptr; *val_ptr=','; ++val_ptr; /* currently no way to distinguish empty string value (?a=) from null value (?a). This is a GPDB limitation. */ } else { val_ptr=strenc(val_ptr,it->value); *val_ptr=','; ++val_ptr; } } if (key_counter!=3) { --key_ptr; } memcpy(key_ptr,"}",2); if (val_counter!=3) { --val_ptr; } memcpy(val_ptr,"}",2); uriFreeQueryListA(queryList); } else { retval[11]=NULL; retval[12]=NULL; } /* There is no need to call pfree. It's called automatically. */ HeapTuple tuple; Datum result; tuple=BuildTupleFromCStrings(attinmeta,retval); result=HeapTupleGetDatum(tuple); /* Free memory start */ uriFreeUriMembersA(&uri); /* Free memory finish */ PG_RETURN_DATUM(result); }
/* * pgstattuple_real * * The real work occurs here */ static Datum pgstattuple_real(Relation rel) { 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; TupleTableSlot *slot; AttInMetadata *attinmeta; char **values; int i; Datum result; /* * Build a tuple description for a pgstattupe_type tuple */ tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE); /* 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); nblocks = RelationGetNumberOfBlocks(rel); scan = heap_beginscan(rel, SnapshotAny, 0, NULL); /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { uint16 sv_infomask; sv_infomask = tuple->t_data->t_infomask; if (HeapTupleSatisfiesNow(tuple->t_data)) { tuple_len += tuple->t_len; tuple_count++; } else { dead_tuple_len += tuple->t_len; dead_tuple_count++; } if (sv_infomask != tuple->t_data->t_infomask) SetBufferCommitInfoNeedsSave(scan->rs_cbuf); /* * 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); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); 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 storage in our slot. 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 = TupleGetDatum(slot, tuple); /* Clean up */ for (i = 0; i < NCOLUMNS; i++) pfree(values[i]); pfree(values); return (result); }
static Tuplestorestate * build_tuplestore_recursively(char *key_fld, char *parent_key_fld, char *relname, char *orderby_fld, char *branch_delim, char *start_with, char *branch, int level, int *serial, int max_depth, bool show_branch, bool show_serial, MemoryContext per_query_ctx, AttInMetadata *attinmeta, Tuplestorestate *tupstore) { TupleDesc tupdesc = attinmeta->tupdesc; int ret; int proc; int serial_column; StringInfoData sql; char **values; char *current_key; char *current_key_parent; char current_level[INT32_STRLEN]; char serial_str[INT32_STRLEN]; char *current_branch; HeapTuple tuple; if (max_depth > 0 && level > max_depth) return tupstore; initStringInfo(&sql); /* Build initial sql statement */ if (!show_serial) { appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s", key_fld, parent_key_fld, relname, parent_key_fld, quote_literal_cstr(start_with), key_fld, key_fld, parent_key_fld); serial_column = 0; } else { appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s ORDER BY %s", key_fld, parent_key_fld, relname, parent_key_fld, quote_literal_cstr(start_with), key_fld, key_fld, parent_key_fld, orderby_fld); serial_column = 1; } if (show_branch) values = (char **) palloc((CONNECTBY_NCOLS + serial_column) * sizeof(char *)); else values = (char **) palloc((CONNECTBY_NCOLS_NOBRANCH + serial_column) * sizeof(char *)); /* First time through, do a little setup */ if (level == 0) { /* root value is the one we initially start with */ values[0] = start_with; /* root value has no parent */ values[1] = NULL; /* root level is 0 */ sprintf(current_level, "%d", level); values[2] = current_level; /* root branch is just starting root value */ if (show_branch) values[3] = start_with; /* root starts the serial with 1 */ if (show_serial) { sprintf(serial_str, "%d", (*serial)++); if (show_branch) values[4] = serial_str; else values[3] = serial_str; } /* construct the tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* now store it */ tuplestore_puttuple(tupstore, tuple); /* increment level */ level++; } /* Retrieve the desired rows */ ret = SPI_execute(sql.data, true, 0); proc = SPI_processed; /* Check for qualifying tuples */ if ((ret == SPI_OK_SELECT) && (proc > 0)) { HeapTuple spi_tuple; SPITupleTable *tuptable = SPI_tuptable; TupleDesc spi_tupdesc = tuptable->tupdesc; int i; StringInfoData branchstr; StringInfoData chk_branchstr; StringInfoData chk_current_key; /* First time through, do a little more setup */ if (level == 0) { /* * Check that return tupdesc is compatible with the one we got * from the query, but only at level 0 -- no need to check more * than once */ if (!compatConnectbyTupleDescs(tupdesc, spi_tupdesc)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid return type"), errdetail("Return and SQL tuple descriptions are " \ "incompatible."))); } initStringInfo(&branchstr); initStringInfo(&chk_branchstr); initStringInfo(&chk_current_key); for (i = 0; i < proc; i++) { /* initialize branch for this pass */ appendStringInfo(&branchstr, "%s", branch); appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim); /* get the next sql result tuple */ spi_tuple = tuptable->vals[i]; /* get the current key and parent */ current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1); appendStringInfo(&chk_current_key, "%s%s%s", branch_delim, current_key, branch_delim); current_key_parent = pstrdup(SPI_getvalue(spi_tuple, spi_tupdesc, 2)); /* get the current level */ sprintf(current_level, "%d", level); /* check to see if this key is also an ancestor */ if (strstr(chk_branchstr.data, chk_current_key.data)) elog(ERROR, "infinite recursion detected"); /* OK, extend the branch */ appendStringInfo(&branchstr, "%s%s", branch_delim, current_key); current_branch = branchstr.data; /* build a tuple */ values[0] = pstrdup(current_key); values[1] = current_key_parent; values[2] = current_level; if (show_branch) values[3] = current_branch; if (show_serial) { sprintf(serial_str, "%d", (*serial)++); if (show_branch) values[4] = serial_str; else values[3] = serial_str; } tuple = BuildTupleFromCStrings(attinmeta, values); xpfree(current_key); xpfree(current_key_parent); /* store the tuple for later use */ tuplestore_puttuple(tupstore, tuple); heap_freetuple(tuple); /* recurse using current_key_parent as the new start_with */ tupstore = build_tuplestore_recursively(key_fld, parent_key_fld, relname, orderby_fld, branch_delim, values[0], current_branch, level + 1, serial, max_depth, show_branch, show_serial, per_query_ctx, attinmeta, tupstore); /* reset branch for next pass */ resetStringInfo(&branchstr); resetStringInfo(&chk_branchstr); resetStringInfo(&chk_current_key); } xpfree(branchstr.data); xpfree(chk_branchstr.data); xpfree(chk_current_key.data); } return tupstore; }
/* * 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 pg_config(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Tuplestorestate *tupstore; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; MemoryContext per_query_ctx; MemoryContext oldcontext; ConfigData *configdata; size_t configdata_len; char *values[2]; int i = 0; /* check to see if caller supports us returning a tuplestore */ if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("materialize mode required, but it is not " "allowed in this context"))); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* get the requested return tuple description */ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); /* * Check to make sure we have a reasonable tuple descriptor */ if (tupdesc->natts != 2 || tupdesc->attrs[0]->atttypid != TEXTOID || tupdesc->attrs[1]->atttypid != TEXTOID) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("query-specified return tuple and " "function return type are not compatible"))); /* OK to use it */ attinmeta = TupleDescGetAttInMetadata(tupdesc); /* let the caller know we're sending back a tuplestore */ rsinfo->returnMode = SFRM_Materialize; /* initialize our tuplestore */ tupstore = tuplestore_begin_heap(true, false, work_mem); configdata = get_configdata(my_exec_path, &configdata_len); for (i = 0; i < configdata_len; i++) { values[0] = configdata[i].name; values[1] = configdata[i].setting; tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); } /* * no longer need the tuple descriptor reference created by * TupleDescGetAttInMetadata() */ ReleaseTupleDesc(tupdesc); tuplestore_donestoring(tupstore); rsinfo->setResult = tupstore; /* * 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. */ rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); return (Datum) 0; }
Datum pgrowlocks(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HeapScanDesc scan; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; Datum result; MyData *mydata; Relation rel; if (SRF_IS_FIRSTCALL()) { text *relname; RangeVar *relrv; MemoryContext oldcontext; AclResult aclresult; funcctx = SRF_FIRSTCALL_INIT(); 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) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; relname = PG_GETARG_TEXT_PP(0); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a partitioned table", RelationGetRelationName(rel)), errdetail("Partitioned tables do not contain rows."))); else if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); /* * check permissions: must have SELECT on table or be in * pg_stat_scan_tables */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV; if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), RelationGetRelationName(rel)); scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL); mydata = palloc(sizeof(*mydata)); mydata->rel = rel; mydata->scan = scan; mydata->ncolumns = tupdesc->natts; funcctx->user_fctx = mydata; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); attinmeta = funcctx->attinmeta; mydata = (MyData *) funcctx->user_fctx; scan = mydata->scan; /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { HTSU_Result htsu; TransactionId xmax; uint16 infomask; /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); htsu = HeapTupleSatisfiesUpdate(tuple, GetCurrentCommandId(false), scan->rs_cbuf); xmax = HeapTupleHeaderGetRawXmax(tuple->t_data); infomask = tuple->t_data->t_infomask; /* * A tuple is locked if HTSU returns BeingUpdated. */ if (htsu == HeapTupleBeingUpdated) { char **values; values = (char **) palloc(mydata->ncolumns * sizeof(char *)); values[Atnum_tid] = (char *) DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self)); values[Atnum_xmax] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_xmax], NCHARS, "%d", xmax); if (infomask & HEAP_XMAX_IS_MULTI) { MultiXactMember *members; int nmembers; bool first = true; bool allow_old; values[Atnum_ismulti] = pstrdup("true"); allow_old = HEAP_LOCKED_UPGRADED(infomask); nmembers = GetMultiXactIdMembers(xmax, &members, allow_old, false); if (nmembers == -1) { values[Atnum_xids] = "{0}"; values[Atnum_modes] = "{transient upgrade status}"; values[Atnum_pids] = "{0}"; } else { int j; values[Atnum_xids] = palloc(NCHARS * nmembers); values[Atnum_modes] = palloc(NCHARS * nmembers); values[Atnum_pids] = palloc(NCHARS * nmembers); strcpy(values[Atnum_xids], "{"); strcpy(values[Atnum_modes], "{"); strcpy(values[Atnum_pids], "{"); for (j = 0; j < nmembers; j++) { char buf[NCHARS]; if (!first) { strcat(values[Atnum_xids], ","); strcat(values[Atnum_modes], ","); strcat(values[Atnum_pids], ","); } snprintf(buf, NCHARS, "%d", members[j].xid); strcat(values[Atnum_xids], buf); switch (members[j].status) { case MultiXactStatusUpdate: snprintf(buf, NCHARS, "Update"); break; case MultiXactStatusNoKeyUpdate: snprintf(buf, NCHARS, "No Key Update"); break; case MultiXactStatusForUpdate: snprintf(buf, NCHARS, "For Update"); break; case MultiXactStatusForNoKeyUpdate: snprintf(buf, NCHARS, "For No Key Update"); break; case MultiXactStatusForShare: snprintf(buf, NCHARS, "Share"); break; case MultiXactStatusForKeyShare: snprintf(buf, NCHARS, "Key Share"); break; } strcat(values[Atnum_modes], buf); snprintf(buf, NCHARS, "%d", BackendXidGetPid(members[j].xid)); strcat(values[Atnum_pids], buf); first = false; } strcat(values[Atnum_xids], "}"); strcat(values[Atnum_modes], "}"); strcat(values[Atnum_pids], "}"); } } else { values[Atnum_ismulti] = pstrdup("false"); values[Atnum_xids] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_xids], NCHARS, "{%d}", xmax); values[Atnum_modes] = palloc(NCHARS); if (infomask & HEAP_XMAX_LOCK_ONLY) { if (HEAP_XMAX_IS_SHR_LOCKED(infomask)) snprintf(values[Atnum_modes], NCHARS, "{For Share}"); else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask)) snprintf(values[Atnum_modes], NCHARS, "{For Key Share}"); else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask)) { if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) snprintf(values[Atnum_modes], NCHARS, "{For Update}"); else snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}"); } else /* neither keyshare nor exclusive bit it set */ snprintf(values[Atnum_modes], NCHARS, "{transient upgrade status}"); } else { if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) snprintf(values[Atnum_modes], NCHARS, "{Update}"); else snprintf(values[Atnum_modes], NCHARS, "{No Key Update}"); } values[Atnum_pids] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_pids], NCHARS, "{%d}", BackendXidGetPid(xmax)); } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* * no need to pfree what we allocated; it's on a short-lived * memory context anyway */ SRF_RETURN_NEXT(funcctx, result); } else { LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); } } heap_endscan(scan); table_close(mydata->rel, AccessShareLock); SRF_RETURN_DONE(funcctx); }
/* Clause 3.3.9.4 */ Datum TradeUpdateFrame2(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i; int j; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; enum tuf2 { i_bid_price=0, i_cash_transaction_amount, i_cash_transaction_dts, i_cash_transaction_name, i_exec_name, i_is_cash, i_num_found, i_num_updated, i_settlement_amount, i_settlement_cash_due_date, i_settlement_cash_type, i_trade_history_dts, i_trade_history_status_id, i_trade_list, i_trade_price }; long acct_id = PG_GETARG_INT64(0); Timestamp end_trade_dts_ts = PG_GETARG_TIMESTAMP(1); int max_trades = PG_GETARG_INT32(2); int max_updates = PG_GETARG_INT32(3); Timestamp start_trade_dts_ts = PG_GETARG_TIMESTAMP(4); int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; struct pg_tm tt, *tm = &tt; fsec_t fsec; char *tzn = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[4]; char nulls[4] = { ' ', ' ', ' ', ' ' }; char end_trade_dts[MAXDATELEN + 1]; char start_trade_dts[MAXDATELEN + 1]; int num_found; int num_updated = 0; int num_cash = 0; if (timestamp2tm(end_trade_dts_ts, NULL, tm, &fsec, NULL, NULL) == 0) { EncodeDateTimeM(tm, fsec, tzn, end_trade_dts); } if (timestamp2tm(start_trade_dts_ts, NULL, tm, &fsec, NULL, NULL) == 0) { EncodeDateTimeM(tm, fsec, tzn, start_trade_dts); } #ifdef DEBUG dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); #endif /* * 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. * Don't forget to factor in commas (,) and braces ({}) for the arrays. */ values = (char **) palloc(sizeof(char *) * 15); values[i_bid_price] = (char *) palloc(((S_PRICE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_amount] = (char *) palloc(((VALUE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_dts] = (char *) palloc(((MAXDATELEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_name] = (char *) palloc(((CT_NAME_LEN + 3) * 20 + 2) * sizeof(char)); values[i_exec_name] = (char *) palloc(((T_EXEC_NAME_LEN + 3) * 20 + 2) * sizeof(char)); values[i_is_cash] = (char *) palloc(((SMALLINT_LEN + 1) * 20 + 2) * sizeof(char)); values[i_num_found] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_num_updated] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_settlement_amount] = (char *) palloc(((VALUE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_settlement_cash_due_date] = (char *) palloc(((MAXDATELEN + 1) * 20 + 2) * sizeof(char)); values[i_settlement_cash_type] = (char *) palloc(((SE_CASH_TYPE_LEN + 3) * 20 + 2) * sizeof(char)); values[i_trade_history_dts] = (char *) palloc((((MAXDATELEN + 2) * 3 + 2) * 20 + 5) * sizeof(char)); values[i_trade_history_status_id] = (char *) palloc((((ST_ID_LEN + 2) * 3 + 2) * 20 + 5) * sizeof(char)); values[i_trade_list] = (char *) palloc(((BIGINT_LEN + 1) * 20 + 2) * sizeof(char)); values[i_trade_price] = (char *) palloc(((S_PRICE_T_LEN + 1) * 20 + 2) * sizeof(char)); /* 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 */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(TUF2_statements); #ifdef DEBUG sprintf(sql, SQLTUF2_1, acct_id, start_trade_dts, end_trade_dts, max_trades); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(acct_id); args[1] = TimestampGetDatum(start_trade_dts_ts); args[2] = TimestampGetDatum(end_trade_dts_ts); args[3] = Int32GetDatum(max_trades); ret = SPI_execute_plan(TUF2_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; } num_found = SPI_processed; strcpy(values[i_bid_price], "{"); strcpy(values[i_exec_name], "{"); strcpy(values[i_is_cash], "{"); strcpy(values[i_trade_list], "{"); strcpy(values[i_trade_price], "{"); strcpy(values[i_settlement_amount], "{"); strcpy(values[i_settlement_cash_due_date], "{"); strcpy(values[i_settlement_cash_type], "{"); strcpy(values[i_cash_transaction_amount], "{"); strcpy(values[i_cash_transaction_dts], "{"); strcpy(values[i_cash_transaction_name], "{"); strcpy(values[i_trade_history_dts], "{"); strcpy(values[i_trade_history_status_id], "{"); for (i = 0; i < num_found; i++) { TupleDesc l_tupdesc; SPITupleTable *l_tuptable = NULL; HeapTuple l_tuple = NULL; char *is_cash_str; char *trade_list; char cash_type[41]; tuple = tuptable->vals[i]; if (i > 0) { strcat(values[i_bid_price], ","); strcat(values[i_exec_name], ","); strcat(values[i_is_cash], ","); strcat(values[i_trade_list], ","); strcat(values[i_trade_price], ","); strcat(values[i_settlement_amount], ","); strcat(values[i_settlement_cash_due_date], ","); strcat(values[i_settlement_cash_type], ","); strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } strcat(values[i_bid_price], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_exec_name], "\""); strcat(values[i_exec_name], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_exec_name], "\""); is_cash_str = SPI_getvalue(tuple, tupdesc, 3); strcat(values[i_is_cash], (is_cash_str[0] == 't' ? "0" : "1")); trade_list = SPI_getvalue(tuple, tupdesc, 4); strcat(values[i_trade_list], trade_list); strcat(values[i_trade_price], SPI_getvalue(tuple, tupdesc, 5)); if (num_updated < max_updates) { char *old_cash_type; #ifdef DEBUG sprintf(sql, SQLTUF2_2, trade_list); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(atoll(trade_list)); ret = SPI_execute_plan(TUF2_2, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { l_tupdesc = SPI_tuptable->tupdesc; l_tuptable = SPI_tuptable; l_tuple = l_tuptable->vals[0]; old_cash_type = SPI_getvalue(l_tuple, l_tupdesc, 1); } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF2_statements[1].sql); dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); continue; } #ifdef DEBUG elog(NOTICE, "cash_type = '%s'", old_cash_type); #endif /* DEBUG */ if (is_cash_str[0] == 't') { if (strcmp(old_cash_type, "Cash Account") == 0) { strcpy(cash_type, "Cash"); } else { strcpy(cash_type, "Cash Account"); } } else { if (strcmp(old_cash_type, "Margin Account") == 0) { strcpy(cash_type, "Margin"); } else { strcpy(cash_type, "Margin Account"); } } #ifdef DEBUG sprintf(sql, SQLTUF2_3, cash_type, trade_list); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(cash_type); args[1] = Int64GetDatum(atoll(trade_list)); ret = SPI_execute_plan(TUF2_3, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { FAIL_FRAME_SET(&funcctx->max_calls, TUF2_statements[2].sql); dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); continue; } num_updated += SPI_processed; } #ifdef DEBUG sprintf(sql, SQLTUF2_4, trade_list); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(atoll(trade_list)); ret = SPI_execute_plan(TUF2_4, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { l_tupdesc = SPI_tuptable->tupdesc; l_tuptable = SPI_tuptable; l_tuple = l_tuptable->vals[0]; strcat(values[i_settlement_amount], SPI_getvalue(l_tuple, l_tupdesc, 1)); strcat(values[i_settlement_cash_due_date], SPI_getvalue(l_tuple, l_tupdesc, 2)); strcat(values[i_settlement_cash_type], "\""); strcat(values[i_settlement_cash_type], SPI_getvalue(l_tuple, l_tupdesc, 3)); strcat(values[i_settlement_cash_type], "\""); } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF2_statements[3].sql); dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); continue; } if (is_cash_str[0] == 't') { #ifdef DEBUG sprintf(sql, SQLTUF2_5, trade_list); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TUF2_5, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { l_tupdesc = SPI_tuptable->tupdesc; l_tuptable = SPI_tuptable; l_tuple = l_tuptable->vals[0]; if (num_cash > 0) { strcat(values[i_cash_transaction_amount], ","); strcat(values[i_cash_transaction_dts], ","); strcat(values[i_cash_transaction_name], ","); } strcat(values[i_cash_transaction_amount], SPI_getvalue(l_tuple, l_tupdesc, 1)); strcat(values[i_cash_transaction_dts], SPI_getvalue(l_tuple, l_tupdesc, 2)); strcat(values[i_cash_transaction_name], "\""); strcat(values[i_cash_transaction_name], SPI_getvalue(l_tuple, l_tupdesc, 3)); strcat(values[i_cash_transaction_name], "\""); ++num_cash; } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF2_statements[4].sql); dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); continue; } } else { if (num_cash > 0) { strcat(values[i_cash_transaction_amount], ","); strcat(values[i_cash_transaction_dts], ","); strcat(values[i_cash_transaction_name], ","); } strcat(values[i_cash_transaction_amount], "0"); strcat(values[i_cash_transaction_dts], "1970-1-1 0:0:0"); strcat(values[i_cash_transaction_name], "\"\""); ++num_cash; } #ifdef DEBUG sprintf(sql, SQLTUF2_6, trade_list); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TUF2_6, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { l_tupdesc = SPI_tuptable->tupdesc; l_tuptable = SPI_tuptable; } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF2_statements[5].sql); dump_tuf2_inputs(acct_id, end_trade_dts, max_trades, max_updates, start_trade_dts); continue; } strcat(values[i_trade_history_dts], "{"); strcat(values[i_trade_history_status_id], "{"); for (j = 0; j < SPI_processed; j ++) { if (j > 0) { strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } l_tuple = l_tuptable->vals[j]; strcat(values[i_trade_history_dts], SPI_getvalue(l_tuple, l_tupdesc, 1)); strcat(values[i_trade_history_status_id], "\""); strcat(values[i_trade_history_status_id], SPI_getvalue(l_tuple, l_tupdesc, 2)); strcat(values[i_trade_history_status_id], "\""); } for (j = SPI_processed; j < 3; j++) { if (j > 0) { strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } strcat(values[i_trade_history_dts], "NULL"); strcat(values[i_trade_history_status_id], "\"\""); } strcat(values[i_trade_history_dts], "}"); strcat(values[i_trade_history_status_id], "}"); } strcat(values[i_bid_price], "}"); strcat(values[i_exec_name], "}"); strcat(values[i_is_cash], "}"); strcat(values[i_trade_list], "}"); strcat(values[i_trade_price], "}"); strcat(values[i_settlement_amount], "}"); strcat(values[i_settlement_cash_due_date], "}"); strcat(values[i_settlement_cash_type], "}"); strcat(values[i_cash_transaction_amount], "}"); strcat(values[i_cash_transaction_dts], "}"); strcat(values[i_cash_transaction_name], "}"); strcat(values[i_trade_history_dts], "}"); strcat(values[i_trade_history_status_id], "}"); sprintf(values[i_num_found], "%d", num_found); sprintf(values[i_num_updated], "%d", num_updated); /* 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 < 15; i++) { elog(NOTICE, "TUF2 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); } }
Datum pgrowlocks(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HeapScanDesc scan; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; Datum result; MyData *mydata; Relation rel; if (SRF_IS_FIRSTCALL()) { text *relname; RangeVar *relrv; MemoryContext oldcontext; AclResult aclresult; funcctx = SRF_FIRSTCALL_INIT(); 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) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; relname = PG_GETARG_TEXT_P(0); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = heap_openrv(relrv, AccessShareLock); /* check permissions: must have SELECT on table */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); mydata = palloc(sizeof(*mydata)); mydata->rel = rel; mydata->scan = scan; mydata->ncolumns = tupdesc->natts; funcctx->user_fctx = mydata; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); attinmeta = funcctx->attinmeta; mydata = (MyData *) funcctx->user_fctx; scan = mydata->scan; /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesUpdate(rel, tuple->t_data, GetCurrentCommandId(/*false*/), scan->rs_cbuf) == HeapTupleBeingUpdated) { char **values; int i; values = (char **) palloc(mydata->ncolumns * sizeof(char *)); i = 0; values[i++] = (char *) DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self)); if (tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK) values[i++] = pstrdup("Shared"); else values[i++] = pstrdup("Exclusive"); values[i] = palloc(NCHARS * sizeof(char)); snprintf(values[i++], NCHARS, "%d", HeapTupleHeaderGetXmax(tuple->t_data)); if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId *xids; int nxids; int j; int isValidXid = 0; /* any valid xid ever exists? */ values[i++] = pstrdup("true"); nxids = GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple->t_data), &xids); if (nxids == -1) { elog(ERROR, "GetMultiXactIdMembers returns error"); } values[i] = palloc(NCHARS * nxids); values[i + 1] = palloc(NCHARS * nxids); strcpy(values[i], "{"); strcpy(values[i + 1], "{"); for (j = 0; j < nxids; j++) { char buf[NCHARS]; if (TransactionIdIsInProgress(xids[j])) { if (isValidXid) { strcat(values[i], ","); strcat(values[i + 1], ","); } snprintf(buf, NCHARS, "%d", xids[j]); strcat(values[i], buf); snprintf(buf, NCHARS, "%d", BackendXidGetPid(xids[j])); strcat(values[i + 1], buf); isValidXid = 1; } } strcat(values[i], "}"); strcat(values[i + 1], "}"); i++; } else { values[i++] = pstrdup("false"); values[i] = palloc(NCHARS * sizeof(char)); snprintf(values[i++], NCHARS, "{%d}", HeapTupleHeaderGetXmax(tuple->t_data)); values[i] = palloc(NCHARS * sizeof(char)); snprintf(values[i++], NCHARS, "{%d}", BackendXidGetPid(HeapTupleHeaderGetXmax(tuple->t_data))); } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* Clean up */ for (i = 0; i < mydata->ncolumns; i++) pfree(values[i]); pfree(values); SRF_RETURN_NEXT(funcctx, result); } else { LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); } } heap_endscan(scan); heap_close(mydata->rel, AccessShareLock); SRF_RETURN_DONE(funcctx); }
/* Clause 3.3.9.3 */ Datum TradeUpdateFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i; int j; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; enum tuf1 { i_bid_price=0, i_cash_transaction_amount, i_cash_transaction_dts, i_cash_transaction_name, i_exec_name, i_is_cash, i_is_market, i_num_found, i_num_updated, i_settlement_amount, i_settlement_cash_due_date, i_settlement_cash_type, i_trade_history_dts, i_trade_history_status_id, i_trade_price }; int max_trades = PG_GETARG_INT32(0); int max_updates = PG_GETARG_INT32(1); ArrayType *trade_id_p = PG_GETARG_ARRAYTYPE_P(2); int16 typlen; bool typbyval; char typalign; int ndim, nitems; int *dim; long *trade_id; int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif Datum args[2]; char nulls[2] = { ' ', ' ' }; int num_found = max_trades; int num_updated = 0; int num_updated4 = 0; int num_updated5 = 0; int num_updated7 = 0; int num_cash = 0; ndim = ARR_NDIM(trade_id_p); dim = ARR_DIMS(trade_id_p); nitems = ArrayGetNItems(ndim, dim); get_typlenbyvalalign(ARR_ELEMTYPE(trade_id_p), &typlen, &typbyval, &typalign); trade_id = (long *) ARR_DATA_PTR(trade_id_p); /* * 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. * Don't forget to factor in commas (,) and braces ({}) for the arrays. */ values = (char **) palloc(sizeof(char *) * 15); values[i_bid_price] = (char *) palloc(((S_PRICE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_amount] = (char *) palloc(((VALUE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_dts] = (char *) palloc(((MAXDATELEN + 1) * 20 + 2) * sizeof(char)); values[i_cash_transaction_name] = (char *) palloc(((CT_NAME_LEN + 3) * 20 + 2) * sizeof(char)); values[i_exec_name] = (char *) palloc(((T_EXEC_NAME_LEN + 3) * 20 + 2) * sizeof(char)); values[i_is_cash] = (char *) palloc(((SMALLINT_LEN + 1) * 20 + 2) * sizeof(char)); values[i_is_market] = (char *) palloc(((SMALLINT_LEN + 1) * 20 + 2) * sizeof(char)); values[i_num_found] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_num_updated] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char)); values[i_settlement_amount] = (char *) palloc(((VALUE_T_LEN + 1) * 20 + 2) * sizeof(char)); values[i_settlement_cash_due_date] = (char *) palloc(((MAXDATELEN + 1) * 20 + 2) * sizeof(char)); values[i_settlement_cash_type] = (char *) palloc(((SE_CASH_TYPE_LEN + 3) * 20 + 2) * sizeof(char)); values[i_trade_history_dts] = (char *) palloc(((MAXDATELEN * 3 + 4) * 20 + 22) * sizeof(char)); values[i_trade_history_status_id] = (char *) palloc((((ST_ID_LEN + 2) * 3 + 4) * 20 + 22) * sizeof(char)); values[i_trade_price] = (char *) palloc(((S_PRICE_T_LEN + 1) * 20 + 2) * sizeof(char)); #ifdef DEBUG dump_tuf1_inputs(max_trades, max_updates, trade_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 */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(TUF1_statements); strcpy(values[i_bid_price], "{"); strcpy(values[i_exec_name], "{"); strcpy(values[i_is_cash], "{"); strcpy(values[i_is_market], "{"); strcpy(values[i_trade_price], "{"); strcpy(values[i_settlement_amount], "{"); strcpy(values[i_settlement_cash_due_date], "{"); strcpy(values[i_settlement_cash_type], "{"); strcpy(values[i_cash_transaction_amount], "{"); strcpy(values[i_cash_transaction_dts], "{"); strcpy(values[i_cash_transaction_name], "{"); strcpy(values[i_trade_history_dts], "{"); strcpy(values[i_trade_history_status_id], "{"); for (i = 0; i < max_trades; i++) { char *is_cash_str = NULL; char *is_market_str; if (num_updated < max_updates) { char *ex_name; #ifdef DEBUG sprintf(sql, SQLTUF1_1, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(trade_id[i]); ret = SPI_execute_plan(TUF1_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; ex_name = SPI_getvalue(tuple, tupdesc, 1); } else { continue; } #ifdef DEBUG elog(NOTICE, "ex_name = %s", ex_name); if (strstr(ex_name, " X ")) { sprintf(sql, SQLTUF1_2a, ex_name); } else { sprintf(sql, SQLTUF1_2b, ex_name); } elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = CStringGetTextDatum(ex_name); if (strstr(ex_name, " X ")) { ret = SPI_execute_plan(TUF1_2a, args, nulls, true, 0); } else { ret = SPI_execute_plan(TUF1_2b, args, nulls, true, 0); } if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; ex_name = SPI_getvalue(tuple, tupdesc, 1); } else { FAIL_FRAME_SET(&funcctx->max_calls, strstr(ex_name, " X ")? TUF1_statements[1].sql: TUF1_statements[2].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } #ifdef DEBUG elog(NOTICE, "ex_name = %s", ex_name); sprintf(sql, SQLTUF1_3, ex_name, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[1] = Int64GetDatum(trade_id[i]); ret = SPI_execute_plan(TUF1_3, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { FAIL_FRAME_SET(&funcctx->max_calls, TUF1_statements[4].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } num_updated += SPI_processed; } #ifdef DEBUG sprintf(sql, SQLTUF1_4, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = Int64GetDatum(trade_id[i]); ret = SPI_execute_plan(TUF1_4, args, nulls, true, 0); if (ret != SPI_OK_SELECT) { FAIL_FRAME_SET(&funcctx->max_calls, TUF1_statements[5].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; for (j = 0; j < SPI_processed; j++) { char *trade_price; if (num_updated4 > 0) { strcat(values[i_bid_price], ","); strcat(values[i_exec_name], ","); strcat(values[i_is_cash], ","); strcat(values[i_is_market], ","); strcat(values[i_trade_price], ","); } tuple = tuptable->vals[j]; strcat(values[i_bid_price], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_exec_name], "\""); strcat(values[i_exec_name], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_exec_name], "\""); is_cash_str = SPI_getvalue(tuple, tupdesc, 3); strcat(values[i_is_cash], (is_cash_str[0] == 't' ? "1": "0")); is_market_str = SPI_getvalue(tuple, tupdesc, 4); strcat(values[i_is_market], (is_market_str[0] == 't' ? "0" : "1")); trade_price = SPI_getvalue(tuple, tupdesc, 5); if (trade_price != NULL) strcat(values[i_trade_price], SPI_getvalue(tuple, tupdesc, 5)); else strcat(values[i_trade_price], "NULL"); num_updated4++; } #ifdef DEBUG sprintf(sql, SQLTUF1_5, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TUF1_5, args, nulls, true, 0); if (ret != SPI_OK_SELECT) { FAIL_FRAME_SET(&funcctx->max_calls, TUF1_statements[6].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; for (j = 0; j < SPI_processed; j++) { if (num_updated5 > 0) { strcat(values[i_settlement_amount], ","); strcat(values[i_settlement_cash_due_date], ","); strcat(values[i_settlement_cash_type], ","); } tuple = tuptable->vals[j]; strcat(values[i_settlement_amount], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_settlement_cash_due_date], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_settlement_cash_type], "\""); strcat(values[i_settlement_cash_type], SPI_getvalue(tuple, tupdesc, 3)); strcat(values[i_settlement_cash_type], "\""); num_updated5++; } if (is_cash_str[0] == 't') { if (num_cash > 0) { strcat(values[i_cash_transaction_amount], ","); strcat(values[i_cash_transaction_dts], ","); strcat(values[i_cash_transaction_name], ","); } #ifdef DEBUG sprintf(sql, SQLTUF1_6, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TUF1_6, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; strcat(values[i_cash_transaction_amount], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_cash_transaction_dts], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_cash_transaction_name], "\""); strcat(values[i_cash_transaction_name], SPI_getvalue(tuple, tupdesc, 3)); strcat(values[i_cash_transaction_name], "\""); ++num_cash; } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF1_statements[7].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } } #ifdef DEBUG sprintf(sql, SQLTUF1_7, trade_id[i]); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ ret = SPI_execute_plan(TUF1_7, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; } else { FAIL_FRAME_SET(&funcctx->max_calls, TUF1_statements[8].sql); dump_tuf1_inputs(max_trades, max_updates, trade_id); continue; } if (num_updated7 > 0) { strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } strcat(values[i_trade_history_dts], "{"); strcat(values[i_trade_history_status_id], "{"); for (j = 0; j < SPI_processed; j++) { if (j > 0) { strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } tuple = tuptable->vals[j]; strcat(values[i_trade_history_dts], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_trade_history_status_id], "\""); strcat(values[i_trade_history_status_id], SPI_getvalue(tuple, tupdesc, 2)); strcat(values[i_trade_history_status_id], "\""); num_updated7++; } for (j = SPI_processed; j < 3; j++) { if (j > 0) { strcat(values[i_trade_history_dts], ","); strcat(values[i_trade_history_status_id], ","); } strcat(values[i_trade_history_dts], "NULL"); strcat(values[i_trade_history_status_id], "\"\""); num_updated7++; } strcat(values[i_trade_history_dts], "}"); strcat(values[i_trade_history_status_id], "}"); } strcat(values[i_bid_price], "}"); strcat(values[i_exec_name], "}"); strcat(values[i_is_cash], "}"); strcat(values[i_is_market], "}"); strcat(values[i_trade_price], "}"); strcat(values[i_settlement_amount], "}"); strcat(values[i_settlement_cash_due_date], "}"); strcat(values[i_settlement_cash_type], "}"); strcat(values[i_cash_transaction_amount], "}"); strcat(values[i_cash_transaction_dts], "}"); strcat(values[i_cash_transaction_name], "}"); strcat(values[i_trade_history_dts], "}"); strcat(values[i_trade_history_status_id], "}"); sprintf(values[i_num_found], "%d", num_found); sprintf(values[i_num_updated], "%d", num_updated); /* 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 < 15; i++) { elog(NOTICE, "TUF1 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); } }
//Mandar: //This is where we return tuple according to it's offset in the file. HeapTuple csvindex_fetch_heap(IndexScanDesc scan) { //HeapTuple tuple; ItemPointer tid = &scan->xs_ctup.t_self; char *filename = (char *) palloc0(FILENAME_MAX); //This is shaky.. we are putting while path in sprintf(filename,"%s/csvinput/%s",getenv("HOME"),RelationGetRelationName(scan->heapRelation)); FILE *file = fopen(filename,"r"); int64 offset=0; char *chartuple = NULL; int iNumofAttr = scan->heapRelation->rd_rel->relnatts; int iAttr_size = 500+1; int32 size = iNumofAttr*iAttr_size + iNumofAttr + 10; char *linearr[iNumofAttr]; offset = tid->ip_blkid.bi_hi * 65535 * 65535; offset += tid->ip_blkid.bi_lo * 65535; offset += (tid->ip_posid-1); //substracting 1 as it was added to make tuplepointer valid assertion work //Allocate memory int i1=0; for(i1=0; i1<iNumofAttr; i1++) linearr[i1]=(char *)palloc0(iAttr_size); chartuple = (char *)palloc0(size); if(fseek(file,offset, SEEK_SET) != -1) { //First read row. if(fgets(chartuple,size,file)==NULL) { //No data in the file..return null tuple scan->xs_cbuf = InvalidBuffer; scan->xs_ctup.t_data = NULL; //scan->xs_ctup->t_len= newTuple->t_len; } else { //Parse and store in the array. if(!strlen(chartuple) && chartuple[0]!='\n') { //Return null tuple.. scan->xs_cbuf = InvalidBuffer; scan->xs_ctup.t_data = NULL; //scan->xs_ctup->t_len= newTuple->t_len; } else { //Form tuple. int attr=0; int i=0; while(attr < iNumofAttr && chartuple[i]!='\n') { int j=0; //printf("readme-%s---%c\n",record,record[i]); while(chartuple[i]!=',' && chartuple[i]!='\n') { linearr[attr][j++]=chartuple[i++]; } i++; linearr[attr][j]='\0'; attr++; } TupleDesc td = RelationNameGetTupleDesc(RelationGetRelationName(scan->heapRelation)); AttInMetadata *md = TupleDescGetAttInMetadata(td); HeapTuple newTuple = BuildTupleFromCStrings(md,linearr); scan->xs_cbuf = InvalidBuffer; scan->xs_ctup.t_data = newTuple->t_data; scan->xs_ctup.t_len= newTuple->t_len; //tuple->t_data = newTuple->t_data; //tuple->t_len = newTuple->t_len; //tuple->t_csvoffset = offset; //check if tuple is correct as per index definition. //if correct tuple then return the tuple else return null tuple. } } } else //Seek failed return null tuple.. { scan->xs_cbuf = InvalidBuffer; scan->xs_ctup.t_data = NULL; //scan->xs_ctup->t_len= newTuple->t_len; } if(file) fclose(file); return &scan->xs_ctup; }
/* Clause 3.3.1.3 */ Datum BrokerVolumeFrame1(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; AttInMetadata *attinmeta; int call_cntr; int max_calls; int i; int ndim, nitems; int *dim; char *broker_list; char **values = NULL; /* Stuff done only on the first call of the function. */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; ArrayType *broker_list_p = PG_GETARG_ARRAYTYPE_P(0); text *sector_name_p = PG_GETARG_TEXT_P(1); enum bvf1 { i_broker_name=0, i_list_len, i_volume }; int16 typlen; bool typbyval; char typalign; int ret; TupleDesc tupdesc; SPITupleTable *tuptable = NULL; HeapTuple tuple = NULL; #ifdef DEBUG char sql[2048]; #endif char broker_list_array[(B_NAME_LEN + 3) * 40 + 5] = "'{"; Datum args[2]; char nulls[2] = { ' ', ' ' }; /* * 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 *) * 3); values[i_list_len] = (char *) palloc((SMALLINT_LEN + 1) * sizeof(char)); /* * This might be overkill since we always expect single dimensions * arrays. */ ndim = ARR_NDIM(broker_list_p); dim = ARR_DIMS(broker_list_p); nitems = ArrayGetNItems(ndim, dim); get_typlenbyvalalign(ARR_ELEMTYPE(broker_list_p), &typlen, &typbyval, &typalign); broker_list = ARR_DATA_PTR(broker_list_p); /* Turn the broker_list input into an array format. */ if (nitems > 0) { strcat(broker_list_array, "\""); strcat(broker_list_array, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(broker_list)))); broker_list = att_addlength_pointer(broker_list, typlen, broker_list); broker_list = (char *) att_align_nominal(broker_list, typalign); strcat(broker_list_array, "\""); } for (i = 1; i < nitems; i++) { strcat(broker_list_array, ",\""); strcat(broker_list_array, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(broker_list)))); broker_list = att_addlength_pointer(broker_list, typlen, broker_list); broker_list = (char *) att_align_nominal(broker_list, typalign); strcat(broker_list_array, "\""); } strcat(broker_list_array, "}'"); #ifdef DEBUG dump_bvf1_inputs(broker_list_p, sector_name_p); #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 */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); SPI_connect(); plan_queries(BVF1_statements); #ifdef DEBUG sprintf(sql, SQLBVF1_1, broker_list_array, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(sector_name_p)))); elog(NOTICE, "SQL\n%s", sql); #endif /* DEBUG */ args[0] = PointerGetDatum(broker_list_p); args[1] = PointerGetDatum(sector_name_p); ret = SPI_execute_plan(BVF1_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; } else { dump_bvf1_inputs(broker_list_p, sector_name_p); FAIL_FRAME_SET(&funcctx->max_calls, BVF1_statements[0].sql); } sprintf(values[i_list_len], "%d", SPI_processed); values[i_broker_name] = (char *) palloc(((B_NAME_LEN + 2) * (SPI_processed + 1) + 3) * sizeof(char)); values[i_volume] = (char *) palloc((INTEGER_LEN * (SPI_processed + 1) + 3) * sizeof(char)); if (SPI_processed == 0) { strcpy(values[i_broker_name], "{}"); strcpy(values[i_volume], "{}"); } else { strcpy(values[i_broker_name], "{"); strcpy(values[i_volume], "{"); if (SPI_processed > 0) { strcat(values[i_broker_name], "\""); strcat(values[i_broker_name], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_broker_name], "\""); strcat(values[i_volume], SPI_getvalue(tuple, tupdesc, 2)); } for (i = 1; i < SPI_processed; i++) { tuple = tuptable->vals[i]; strcat(values[i_broker_name], ","); strcat(values[i_broker_name], "\""); strcat(values[i_broker_name], SPI_getvalue(tuple, tupdesc, 1)); strcat(values[i_broker_name], "\""); strcat(values[i_volume], ","); strcat(values[i_volume], SPI_getvalue(tuple, tupdesc, 2)); } strcat(values[i_broker_name], "}"); strcat(values[i_volume], "}"); } /* 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 < 3; i++) { elog(NOTICE, "BVF1 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); } }
/* plphp_srf_htup_from_zval * Build a tuple from a zval and a TupleDesc, for a SRF. * * Like above, but we don't use the names of the array attributes; * rather we build the tuple in order. Also, we get a MemoryContext * from the caller and just clean it at return, rather than building it each * time. */ HeapTuple plphp_srf_htup_from_zval(zval *val, AttInMetadata *attinmeta, MemoryContext cxt) { MemoryContext oldcxt; HeapTuple ret; HashPosition pos; char **values; zval **element; int i = 0; oldcxt = MemoryContextSwitchTo(cxt); /* * Use palloc0 to initialize values to NULL, just in case the user does * not pass all needed attributes */ values = (char **) palloc0(attinmeta->tupdesc->natts * sizeof(char *)); /* * If the input zval is an array, build a tuple using each element as an * attribute. Exception: if the return tuple has a single element and * it's an array type, use the whole array as a single value. * * If the input zval is a scalar, use it as an element directly. */ if (Z_TYPE_P(val) == IS_ARRAY) { if (attinmeta->tupdesc->natts == 1) { /* Is it an array? */ if (attinmeta->tupdesc->attrs[0]->attndims != 0 || !OidIsValid(get_element_type(attinmeta->tupdesc->attrs[0]->atttypid))) { zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(val), &pos); zend_hash_get_current_data_ex(Z_ARRVAL_P(val), (void **) &element, &pos); values[0] = plphp_zval_get_cstring(element[0], true, true); } else values[0] = plphp_zval_get_cstring(val, true, true); } else { /* * Ok, it's an array and the return tuple has more than one * attribute, so scan each array element. */ for (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; zend_hash_move_forward_ex(Z_ARRVAL_P(val), &pos)) { /* avoid overrunning the palloc'ed chunk */ if (i >= attinmeta->tupdesc->natts) { elog(WARNING, "more elements in array than attributes in return type"); break; } values[i++] = plphp_zval_get_cstring(element[0], true, true); } } } else { /* The passed zval is not an array -- use as the only attribute */ if (attinmeta->tupdesc->natts != 1) ereport(ERROR, (errmsg("returned array does not correspond to " "declared return value"))); values[0] = plphp_zval_get_cstring(val, true, true); } MemoryContextSwitchTo(oldcxt); ret = BuildTupleFromCStrings(attinmeta, values); MemoryContextReset(cxt); return ret; }
/* ------------------------------------------------------ * pgstatindex() * * Usage: SELECT * FROM pgstatindex('t1_pkey'); * ------------------------------------------------------ */ Datum pgstatindex(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); Relation rel; RangeVar *relrv; Datum result; uint32 nblocks; uint32 blkno; BTIndexStat indexStat; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "pgstatindex() can be used only on b-tree index."); /* * 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 (isOtherTempNamespace(RelationGetNamespace(rel))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); /*------------------- * Read a metapage *------------------- */ { Buffer buffer = ReadBuffer(rel, 0); Page page = BufferGetPage(buffer); BTMetaPageData *metad = BTPageGetMeta(page); indexStat.magic = metad->btm_magic; indexStat.version = metad->btm_version; indexStat.root_blkno = metad->btm_root; indexStat.level = metad->btm_level; indexStat.fastroot = metad->btm_fastroot; indexStat.fastlevel = metad->btm_fastlevel; ReleaseBuffer(buffer); } nblocks = RelationGetNumberOfBlocks(rel); /* -- init stat -- */ indexStat.fragments = 0; indexStat.root_pages = 0; indexStat.leaf_pages = 0; indexStat.internal_pages = 0; indexStat.empty_pages = 0; indexStat.deleted_pages = 0; indexStat.max_avail = 0; indexStat.free_space = 0; /*----------------------- * Scan all blocks *----------------------- */ for (blkno = 1; blkno < nblocks; blkno++) { Buffer buffer = ReadBuffer(rel, blkno); BTPageStat stat; CHECK_FOR_INTERRUPTS(); /* scan one page */ stat.blkno = blkno; GetBTPageStatistics(blkno, buffer, &stat); /*--------------------- * page status (type) *--------------------- */ switch (stat.type) { case 'd': indexStat.deleted_pages++; break; case 'l': indexStat.leaf_pages++; break; case 'i': indexStat.internal_pages++; break; case 'e': indexStat.empty_pages++; break; case 'r': indexStat.root_pages++; break; default: elog(ERROR, "unknown page status."); } /* -- leaf fragmentation -- */ indexStat.fragments += stat.fragments; if (stat.type == 'l') { indexStat.max_avail += stat.max_avail; indexStat.free_space += stat.free_size; } ReleaseBuffer(buffer); } relation_close(rel, AccessShareLock); /*---------------------------- * Build a result tuple *---------------------------- */ { TupleDesc tupleDesc; int j; char *values[PGSTATINDEX_NCOLUMNS]; HeapTuple tuple; tupleDesc = RelationNameGetTupleDesc(PGSTATINDEX_TYPE); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.version); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.level); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (indexStat.root_pages + indexStat.leaf_pages + indexStat.internal_pages + indexStat.deleted_pages + indexStat.empty_pages) * BLCKSZ); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.root_blkno); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.internal_pages); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.leaf_pages); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.empty_pages); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.deleted_pages); values[j] = palloc(32); if (indexStat.max_avail > 0) snprintf(values[j++], 32, "%.2f", 100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0); else snprintf(values[j++], 32, "NaN"); values[j] = palloc(32); if (indexStat.leaf_pages > 0) snprintf(values[j++], 32, "%.2f", (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0); else snprintf(values[j++], 32, "NaN"); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); } PG_RETURN_DATUM(result); }
Datum plr_environ(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Tuplestorestate *tupstore; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; MemoryContext per_query_ctx; MemoryContext oldcontext; char *var_name; char *var_val; char *values[2]; #ifndef WIN32 char **current_env; #else char *buf; LPTSTR envstr; int count = 0; int i; #endif /* check to see if caller supports us returning a tuplestore */ if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("materialize mode required, but it is not " "allowed in this context"))); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* get the requested return tuple description */ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); /* * Check to make sure we have a reasonable tuple descriptor */ if (tupdesc->natts != 2 || tupdesc->attrs[0]->atttypid != TEXTOID || tupdesc->attrs[1]->atttypid != TEXTOID) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("query-specified return tuple and " "function return type are not compatible"))); /* OK to use it */ attinmeta = TupleDescGetAttInMetadata(tupdesc); /* let the caller know we're sending back a tuplestore */ rsinfo->returnMode = SFRM_Materialize; /* initialize our tuplestore */ tupstore = TUPLESTORE_BEGIN_HEAP; #ifndef WIN32 for (current_env = environ; current_env != NULL && *current_env != NULL; current_env++) { Size name_len; var_val = strchr(*current_env, '='); if (!var_val) continue; name_len = var_val - *current_env; var_name = (char *) palloc0(name_len + 1); memcpy(var_name, *current_env, name_len); values[0] = var_name; values[1] = var_val + 1; tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); pfree(var_name); } #else buf = GetEnvironmentStrings(); envstr = buf; while (true) { if (*envstr == 0) break; while (*envstr != 0) envstr++; envstr++; count++; } /* reset pointer to the environment buffer */ envstr = buf; while(*buf == '=') buf++; for (i = 0; i < count; i++) { Size name_len; var_val = strchr(buf, '='); if (!var_val) continue; name_len = var_val - buf; var_name = (char *) palloc0(name_len + 1); memcpy(var_name, buf, name_len); values[0] = var_name; values[1] = var_val + 1; tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, tuple); pfree(var_name); while(*buf != '\0') buf++; buf++; } FreeEnvironmentStrings(envstr); #endif /* * no longer need the tuple descriptor reference created by * TupleDescGetAttInMetadata() */ ReleaseTupleDesc(tupdesc); tuplestore_donestoring(tupstore); rsinfo->setResult = tupstore; /* * 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. */ rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); return (Datum) 0; }
/* ----------------------------------------------- * bt_page() * * Usage: SELECT * FROM bt_page('t1_pkey', 0); * ----------------------------------------------- */ Datum bt_page_stats(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Buffer buffer; Relation rel; RangeVar *relrv; Datum result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "bt_page_stats() can be used only on b-tree index."); /* * 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 (isOtherTempNamespace(RelationGetNamespace(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); { HeapTuple tuple; TupleDesc tupleDesc; int j; char *values[BTPAGESTATS_NCOLUMNS]; BTPageStat stat; GetBTPageStatistics(blkno, buffer, &stat); tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_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 = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); } ReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_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 bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); RangeVar *relrv; Datum result; char *values[BTPAGEITEMS_NCOLUMNS]; BTPageOpaque opaque; HeapTuple tuple; ItemId id; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs = NULL; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); if (SRF_IS_FIRSTCALL()) { fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); uargs = palloc(sizeof(struct user_args)); uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE); uargs->offset = FirstOffsetNumber; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); uargs->rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel)) elog(ERROR, "bt_page_items() can be used only on b-tree index."); /* * 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 (isOtherTempNamespace(RelationGetNamespace(uargs->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(uargs->rel, blkno); uargs->buffer = ReadBuffer(uargs->rel, blkno); uargs->page = BufferGetPage(uargs->buffer); opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "bt_page_items(): this page is deleted."); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { IndexTuple itup; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "Invalid ItemId."); itup = (IndexTuple) PageGetItem(uargs->page, id); { int j = 0; BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)); values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); { int off; char *dump; char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dump = palloc(IndexTupleSize(itup) * 3); memset(dump, 0, IndexTupleSize(itup) * 3); for (off = 0; off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); off++) { if (dump[0] == '\0') sprintf(dump, "%02x", *(ptr + off) & 0xff); else { char buf[4]; sprintf(buf, " %02x", *(ptr + off) & 0xff); strcat(dump, buf); } } values[j] = dump; } tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values); result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple); } uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { ReleaseBuffer(uargs->buffer); relation_close(uargs->rel, AccessShareLock); SRF_RETURN_DONE(fctx); } }
Datum retcomposite(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; int call_cntr; int max_calls; TupleDesc tupdesc; AttInMetadata *attinmeta; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* total number of tuples to be returned */ 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 = 1; attinmeta = funcctx->attinmeta; if (call_cntr < max_calls) /* do when there is more left to send */ { char **values; HeapTuple tuple; Datum result; /* * 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(3 * sizeof(char *)); values[0] = (char *) palloc(16 * sizeof(char)); values[1] = (char *) palloc(16 * sizeof(char)); values[2] = (char *) palloc(16 * sizeof(char)); snprintf(values[0], 16, "%d", 1* PG_GETARG_INT32(0)); snprintf(values[1], 16, "%d", 2* PG_GETARG_INT32(0)); snprintf(values[2], 16, "%d", 3* PG_GETARG_INT32(0)); /* 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 */ { SRF_RETURN_DONE(funcctx); } }
/* ------------------------------------------------ * bt_metap() * * Get a btree meta-page information * * Usage: SELECT * FROM bt_metap('t1_pkey') * ------------------------------------------------ */ Datum bt_metap(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); Buffer buffer; Relation rel; RangeVar *relrv; Datum result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "bt_metap() can be used only on b-tree index."); /* * 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 (isOtherTempNamespace(RelationGetNamespace(rel))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); buffer = ReadBuffer(rel, 0); { BTMetaPageData *metad; TupleDesc tupleDesc; int j; char *values[BTMETAP_NCOLUMNS]; HeapTuple tuple; Page page = BufferGetPage(buffer); metad = BTPageGetMeta(page); tupleDesc = RelationNameGetTupleDesc(BTMETAP_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 = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); } ReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_DATUM(result); }
/* ------------------------------------------------------ * pgstatindex() * * Usage: SELECT * FROM pgstatindex('t1_pkey'); * ------------------------------------------------------ */ Datum pgstatindex(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); Relation rel; RangeVar *relrv; Datum result; BlockNumber nblocks; BlockNumber blkno; BTIndexStat indexStat; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple 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"))); /* * Read metapage */ { Buffer buffer = ReadBuffer(rel, 0); 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.root_pages = 0; 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; /* Read and lock buffer */ buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); page = BufferGetPage(buffer); opaque = (BTPageOpaque) PageGetSpecialPointer(page); /* Determine page type, and update totals */ 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 if (P_ISDELETED(opaque)) indexStat.deleted_pages++; else if (P_IGNORE(opaque)) indexStat.empty_pages++; else if (P_ISROOT(opaque)) indexStat.root_pages++; 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] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.version); values[j] = palloc(32); snprintf(values[j++], 32, "%d", indexStat.level); values[j] = palloc(32); snprintf(values[j++], 32, INT64_FORMAT, (indexStat.root_pages + indexStat.leaf_pages + indexStat.internal_pages + indexStat.deleted_pages + indexStat.empty_pages) * BLCKSZ); values[j] = palloc(32); snprintf(values[j++], 32, "%u", indexStat.root_blkno); values[j] = palloc(32); snprintf(values[j++], 32, INT64_FORMAT, indexStat.internal_pages); values[j] = palloc(32); snprintf(values[j++], 32, INT64_FORMAT, indexStat.leaf_pages); values[j] = palloc(32); snprintf(values[j++], 32, INT64_FORMAT, indexStat.empty_pages); values[j] = palloc(32); snprintf(values[j++], 32, INT64_FORMAT, indexStat.deleted_pages); values[j] = palloc(32); if (indexStat.max_avail > 0) snprintf(values[j++], 32, "%.2f", 100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0); else snprintf(values[j++], 32, "NaN"); values[j] = palloc(32); if (indexStat.leaf_pages > 0) snprintf(values[j++], 32, "%.2f", (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0); else snprintf(values[j++], 32, "NaN"); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); } PG_RETURN_DATUM(result); }
/* ----------------------------------------------- * bt_page() * * Usage: SELECT * FROM bt_page('t1_pkey', 1); * ----------------------------------------------- */ Datum bt_page_stats(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(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); /* 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); /* 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 = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); ReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_DATUM(result); }
Datum pgp_armor_headers(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; pgp_armor_headers_state *state; char *utf8key; char *utf8val; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; if (SRF_IS_FIRSTCALL()) { text *data = PG_GETARG_TEXT_PP(0); int res; MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); /* we need the state allocated in the multi call context */ 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) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; state = (pgp_armor_headers_state *) palloc(sizeof(pgp_armor_headers_state)); res = pgp_extract_armor_headers((uint8 *) VARDATA_ANY(data), VARSIZE_ANY_EXHDR(data), &state->nheaders, &state->keys, &state->values); if (res < 0) ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("%s", px_strerror(res)))); MemoryContextSwitchTo(oldcontext); funcctx->user_fctx = state; } funcctx = SRF_PERCALL_SETUP(); state = (pgp_armor_headers_state *) funcctx->user_fctx; if (funcctx->call_cntr >= state->nheaders) SRF_RETURN_DONE(funcctx); else { char *values[2]; /* we assume that the keys (and values) are in UTF-8. */ utf8key = state->keys[funcctx->call_cntr]; utf8val = state->values[funcctx->call_cntr]; values[0] = pg_any_to_server(utf8key, strlen(utf8key), PG_UTF8); values[1] = pg_any_to_server(utf8val, strlen(utf8val), PG_UTF8); /* build a tuple */ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } }
Datum bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; char *values[6]; HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pageinspect functions")))); if (SRF_IS_FIRSTCALL()) { RangeVar *relrv; Relation rel; Buffer buffer; BTPageOpaque opaque; TupleDesc tupleDesc; fctx = SRF_FIRSTCALL_INIT(); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the * owning session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); if (blkno == 0) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); /* * 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); ReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); if (P_ISDELETED(opaque)) elog(NOTICE, "page is deleted"); fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); fctx->user_fctx = uargs; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); uargs = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { ItemId id; IndexTuple itup; int j; int off; int dlen; char *dump; char *ptr; id = PageGetItemId(uargs->page, uargs->offset); if (!ItemIdIsValid(id)) elog(ERROR, "invalid ItemId"); itup = (IndexTuple) PageGetItem(uargs->page, id); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); dump = palloc0(dlen * 3 + 1); values[j] = dump; for (off = 0; off < dlen; off++) { if (off > 0) *dump++ = ' '; sprintf(dump, "%02x", *(ptr + off) & 0xff); dump += 2; } tuple = BuildTupleFromCStrings(fctx->attinmeta, values); result = HeapTupleGetDatum(tuple); uargs->offset = uargs->offset + 1; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
/* 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); }
/* ------------------------------------------------ * 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_P(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); 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] = 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 = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); ReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_DATUM(result); }
Datum xpath_table(PG_FUNCTION_ARGS) { /* Function parameters */ char *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1)); char *relname = text_to_cstring(PG_GETARG_TEXT_PP(2)); char *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3)); char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4)); /* SPI (input tuple) support */ SPITupleTable *tuptable; HeapTuple spi_tuple; TupleDesc spi_tupdesc; /* Output tuple (tuplestore) support */ Tuplestorestate *tupstore = NULL; TupleDesc ret_tupdesc; HeapTuple ret_tuple; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; AttInMetadata *attinmeta; MemoryContext per_query_ctx; MemoryContext oldcontext; char **values; xmlChar **xpaths; char *pos; const char *pathsep = "|"; int numpaths; int ret; int proc; int i; int j; int rownr; /* For issuing multiple rows from one original * document */ bool had_values; /* To determine end of nodeset results */ StringInfoData query_buf; PgXmlErrorContext *xmlerrcxt; volatile xmlDocPtr doctree = NULL; /* We only have a valid tuple description in table function mode */ 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->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table must be called as a table function"))); /* * We want to materialise because it means that we don't have to carry * libxml2 parser state between invocations of this function */ if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table requires Materialize mode, but it is not " "allowed in this context"))); /* * The tuplestore must exist in a higher context than this function call * (per_query_ctx is used) */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* * Create the tuplestore - work_mem is the max in-memory size before a * file is created on disk to hold it. */ tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem); MemoryContextSwitchTo(oldcontext); /* get the requested return tuple description */ ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); /* must have at least one output column (for the pkey) */ if (ret_tupdesc->natts < 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table must have at least one output column"))); /* * At the moment we assume that the returned attributes make sense for the * XPath specififed (i.e. we trust the caller). It's not fatal if they get * it wrong - the input function for the column type will raise an error * if the path result can't be converted into the correct binary * representation. */ attinmeta = TupleDescGetAttInMetadata(ret_tupdesc); /* Set return mode and allocate value space. */ rsinfo->returnMode = SFRM_Materialize; rsinfo->setDesc = ret_tupdesc; values = (char **) palloc(ret_tupdesc->natts * sizeof(char *)); xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *)); /* * Split XPaths. xpathset is a writable CString. * * Note that we stop splitting once we've done all needed for tupdesc */ numpaths = 0; pos = xpathset; while (numpaths < (ret_tupdesc->natts - 1)) { xpaths[numpaths++] = (xmlChar *) pos; pos = strstr(pos, pathsep); if (pos != NULL) { *pos = '\0'; pos++; } else break; } /* Now build query */ initStringInfo(&query_buf); /* Build initial sql statement */ appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s", pkeyfield, xmlfield, relname, condition); if ((ret = SPI_connect()) < 0) elog(ERROR, "xpath_table: SPI_connect returned %d", ret); if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT) elog(ERROR, "xpath_table: SPI execution failed for query %s", query_buf.data); proc = SPI_processed; /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */ tuptable = SPI_tuptable; spi_tupdesc = tuptable->tupdesc; /* Switch out of SPI context */ MemoryContextSwitchTo(oldcontext); /* * Check that SPI returned correct result. If you put a comma into one of * the function parameters, this will catch it when the SPI query returns * e.g. 3 columns. */ if (spi_tupdesc->natts != 2) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("expression returning multiple columns is not valid in parameter list"), errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts))); } /* * Setup the parser. This should happen after we are done evaluating the * query, in case it calls functions that set up libxml differently. */ xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY); PG_TRY(); { /* For each row i.e. document returned from SPI */ for (i = 0; i < proc; i++) { char *pkey; char *xmldoc; xmlXPathContextPtr ctxt; xmlXPathObjectPtr res; xmlChar *resstr; xmlXPathCompExprPtr comppath; /* Extract the row data as C Strings */ spi_tuple = tuptable->vals[i]; pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1); xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2); /* * Clear the values array, so that not-well-formed documents * return NULL in all columns. Note that this also means that * spare columns will be NULL. */ for (j = 0; j < ret_tupdesc->natts; j++) values[j] = NULL; /* Insert primary key */ values[0] = pkey; /* Parse the document */ if (xmldoc) doctree = xmlParseMemory(xmldoc, strlen(xmldoc)); else /* treat NULL as not well-formed */ doctree = NULL; if (doctree == NULL) { /* not well-formed, so output all-NULL tuple */ ret_tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, ret_tuple); heap_freetuple(ret_tuple); } else { /* New loop here - we have to deal with nodeset results */ rownr = 0; do { /* Now evaluate the set of xpaths. */ had_values = false; for (j = 0; j < numpaths; j++) { ctxt = xmlXPathNewContext(doctree); ctxt->node = xmlDocGetRootElement(doctree); /* compile the path */ comppath = xmlXPathCompile(xpaths[j]); if (comppath == NULL) xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "XPath Syntax Error"); /* Now evaluate the path expression. */ res = xmlXPathCompiledEval(comppath, ctxt); xmlXPathFreeCompExpr(comppath); if (res != NULL) { switch (res->type) { case XPATH_NODESET: /* We see if this nodeset has enough nodes */ if (res->nodesetval != NULL && rownr < res->nodesetval->nodeNr) { resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]); had_values = true; } else resstr = NULL; break; case XPATH_STRING: resstr = xmlStrdup(res->stringval); break; default: elog(NOTICE, "unsupported XQuery result: %d", res->type); resstr = xmlStrdup((const xmlChar *) "<unsupported/>"); } /* * Insert this into the appropriate column in the * result tuple. */ values[j + 1] = (char *) resstr; } xmlXPathFreeContext(ctxt); } /* Now add the tuple to the output, if there is one. */ if (had_values) { ret_tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, ret_tuple); heap_freetuple(ret_tuple); } rownr++; } while (had_values); } if (doctree != NULL) xmlFreeDoc(doctree); doctree = NULL; if (pkey) pfree(pkey); if (xmldoc) pfree(xmldoc); } } PG_CATCH(); { if (doctree != NULL) xmlFreeDoc(doctree); pg_xml_done(xmlerrcxt, true); PG_RE_THROW(); } PG_END_TRY(); if (doctree != NULL) xmlFreeDoc(doctree); pg_xml_done(xmlerrcxt, false); tuplestore_donestoring(tupstore); SPI_finish(); rsinfo->setResult = tupstore; /* * 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; }
Datum tkd_query(PG_FUNCTION_ARGS){ char *command; int i, j; int ret, curdm; // return value, current dimetion int call_cntr; int max_calls; int *retarr; FuncCallContext *funcctx; // context switch variable AttInMetadata *attinmeta; // not known------------------------ TupleDesc tupdesc; // tuple descriptor, for getting data D = 0; /* * this section will be executed only for the first call of function * connect to postgres server and execute the first command and get data */ if(SRF_IS_FIRSTCALL()){ MemoryContext oldcontext; /* * create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* * switch to memory context appropriate for multiple function calls * */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* * get arguments, convert given text object to a C string that can be used by server * */ command = text_to_cstring(PG_GETARG_TEXT_P(0)); /* * arguments error * */ if(!PG_ARGISNULL(1)) K = PG_GETARG_INT32(1); else K = 1; if(!PG_ARGISNULL(2)) dominating_type = PG_GETARG_INT32(2); else dominating_type = 0; SPI_connect(); // open internal connection to database ret = SPI_exec(command, 0); // run the SQL command, 0 for no limit of returned row number N = SPI_processed; // save the number of rows dataset = (Dataset *)palloc(sizeof(Dataset)*(N+2)); candidateset = (int *)palloc(sizeof(int)*(N+2)); if(dataset == NULL){ exit(1); } /* * some rows are fetched * */ if(ret > 0 && SPI_tuptable != NULL){ TupleDesc tupdesc; SPITupleTable *tuptable; HeapTuple tuple; char *type_name; /* * get tuple description * */ tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; /* * for each colum, check type * */ for(i = 1; i <= tupdesc->natts; ++i){ type_name = SPI_gettype(tupdesc, i);// get type of data if(strcmp(type_name,"int4") == 0 || strcmp(type_name,"int2") ==0 )//add float4 or flat8 types if want (2) ++D; } /* * for each tuple * and palloc all memory needed * */ kesai = (int *)palloc(sizeof(int)*(N+2)); lbound = (int *)palloc(sizeof(int)*(N+2)); ubound = (int *)palloc(sizeof(int)*(N+2)); //ari = (int *)palloc(sizeof(int)*(N+2)); goods = (int *)palloc(sizeof(int)*(N+2)); goodv = (int *)palloc(sizeof(int)*(N+2)); Q = (int *)palloc(sizeof(int)*(N+2)); P = (int *)palloc(sizeof(int)*(N+2)); nonD = (int *)palloc(sizeof(int)*(N+2)); whichbin = (int *)palloc(sizeof(int)*(N+2)); incomparable = (int *)palloc(sizeof(int)*(N+2)); tagT = (int *)palloc(sizeof(int)*(N+2)); Pi = (int **)palloc(sizeof(int *)*(N+2)); Qi = (int **)palloc(sizeof(int *)*(N+2)); for(i = 0; i < N; ++i){ Pi[i] = (int *)palloc(sizeof(int)*(N+2)); Qi[i] = (int *)palloc(sizeof(int)*(N+2)); } arr = (int **)palloc(sizeof(int)*(D+2)); score = (int *)palloc(sizeof(int)*(N+2)); queue = (int *)palloc(sizeof(int)*(N+2)); missd = (int *)palloc(sizeof(int)*(D+2)); maxscore = (int *)palloc(sizeof(int)*(N+2)); for(i = 0; i < N; ++i){ dataset[i].missing = (int *)palloc(sizeof(int)*(D+2)); dataset[i].value = (int *)palloc(sizeof(int)*(D+2)); dataset[i].T = (int *)palloc(sizeof(int)*(D+2)); if(dataset[i].missing == NULL || dataset[i].value == NULL || dataset[i].T == NULL){ exit(1); } curdm = 0; tuple = tuptable->vals[i]; // get the ith tuple /* * for each dimention of a tuple * */ for(j = 1; j <= tupdesc->natts; ++j) { type_name = SPI_gettype(tupdesc, j); if(strcmp(type_name,"int4") == 0 || strcmp(type_name,"int2") == 0 ){ if(SPI_getvalue(tuple, tupdesc, j) == NULL) { // value is missing dataset[i].missing[curdm] = 1; /*if(dominating_type == 0) dataset[i].value[curdm] = T1MISS; else dataset[i].value[curdm] = T2MISS;*/ } else{ // value is not missing dataset[i].missing[curdm] = 0; dataset[i].value[curdm] = atof(SPI_getvalue(tuple, tupdesc, j)); } ++curdm; } } } } pfree(command); tkd_exec(); // call to execute tkd query funcctx->max_calls = K; /* * allocate local variable retstruct and store the result tuple init * */ retarr = (int *)palloc(sizeof(int)*(K+2)); if(retarr == NULL){ exit(1); } for(i = 0; i < K; ++i ) retarr[i] = candidateset[i+1]; funcctx->user_fctx = retarr; 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; /* MemoryContext switch to old context */ MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; attinmeta = funcctx->attinmeta; retarr = funcctx->user_fctx; if(call_cntr < max_calls){ char **values; HeapTuple tuple; HeapTuple ret_tuple; Datum result; TupleDesc tupdesc; SPITupleTable *tuptable; tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; /* * 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((tupdesc->natts+2) * sizeof(char *)); values = (char **)palloc((tupdesc->natts+2) * sizeof(char *)); if(values == NULL){ exit(1); } for(i = 0; i < tupdesc->natts; ++i ){ tuple = tuptable->vals[retarr[call_cntr]]; values[i] = (SPI_getvalue(tuple, tupdesc, i+1)); } ret_tuple = BuildTupleFromCStrings(attinmeta, values); // build a return tuple result = HeapTupleGetDatum(ret_tuple); // make the tuple into a datum SRF_RETURN_NEXT(funcctx,result); } else{ SPI_finish(); SRF_RETURN_DONE(funcctx); } }