char *ConvertToText( Datum value, Oid column_type, MemoryContext fn_mcxt, char** pbuf ) { bool typIsVarlena; Oid typiofunc; FmgrInfo proc; char* result; // FILE* log; // log = fopen("/var/lib/postgresql/serializer.log", "a"); getTypeOutputInfo(column_type, &typiofunc, &typIsVarlena); fmgr_info_cxt( typiofunc, &proc, fn_mcxt ); // fprintf(log, "Oid of function: %i\n", proc.fn_oid); result = OutputFunctionCall( &proc, value ); if((column_type != INT8OID) && (column_type != BOOLOID) && (column_type != INT4OID) && (column_type != FLOAT8OID) && (column_type != INT2OID) && (column_type != FLOAT4OID)) { // fprintf(log, "WELL WELL\n"); result = json_escape_str(pbuf, result); // fprintf(log, "result: %s\n", result); // fclose(log); return result; } // fclose(log); return result; }
/* * Print a value into the StringInfo provided by caller. */ static void print_value(StringInfo s, Datum origval, Oid typid, bool isnull) { Oid typoutput; bool typisvarlena; /* Query output function */ getTypeOutputInfo(typid, &typoutput, &typisvarlena); /* Print value */ if (isnull) appendStringInfoString(s, "null"); else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) appendStringInfoString(s, "unchanged-toast-datum"); else if (!typisvarlena) print_literal(s, typid, OidOutputFunctionCall(typoutput, origval)); else { /* Definitely detoasted Datum */ Datum val; val = PointerGetDatum(PG_DETOAST_DATUM(origval)); print_literal(s, typid, OidOutputFunctionCall(typoutput, val)); } }
/* ---------------- * debugtup - print one tuple for an interactive backend * ---------------- */ bool debugtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; int natts = typeinfo->natts; int i; Datum attr; char *value; bool isnull; Oid typoutput; bool typisvarlena; for (i = 0; i < natts; ++i) { attr = slot_getattr(slot, i + 1, &isnull); if (isnull) continue; getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &typoutput, &typisvarlena); value = OidOutputFunctionCall(typoutput, attr); printatt((unsigned) i + 1, typeinfo->attrs[i], value); } printf("\t----\n"); return true; }
/* check whether the content in the given bytea is safe for mfvtransval */ void check_mfvtransval(bytea *storage) { size_t left_len = VARSIZE(storage); size_t cur_size = 0; size_t cur_capacity = 0; Oid outFuncOid; bool typIsVarLen; mfvtransval *mfv = NULL; if (left_len < VARHDRSZ + sizeof(mfvtransval)) { elog(ERROR, "invalid transition state for mfvsketch"); } mfv = (mfvtransval*)VARDATA(storage); left_len -= VARHDRSZ + sizeof(mfvtransval); if (mfv->next_mfv > mfv->max_mfvs) { elog(ERROR, "invalid transition state for mfvsketch"); } if (mfv->next_offset + VARHDRSZ > VARSIZE(storage)) { elog(ERROR, "invalid transition state for mfvsketch"); } if (InvalidOid == mfv->typOid) { elog(ERROR, "invalid transition state for mfvsketch"); } getTypeOutputInfo(mfv->typOid, &outFuncOid, &typIsVarLen); if (mfv->outFuncOid != outFuncOid || mfv->typLen != get_typlen(mfv->typOid) || mfv->typByVal != get_typbyval(mfv->typOid)) { elog(ERROR, "invalid transition state for mfvsketch"); } if (left_len < sizeof(offsetcnt)*mfv->max_mfvs) { elog(ERROR, "invalid transition state for mfvsketch"); } /* offset is relative to mfvtransval */ left_len = VARSIZE(storage) - VARHDRSZ; /* * the following checking may be inefficiency, but by doing this centrally, * we can avoid spreading the checking code everywhere. */ for (unsigned i = 0; i < mfv->next_mfv; i ++) { cur_capacity = left_len - mfv->mfvs[i].offset; if (mfv->mfvs[i].offset > left_len) { elog(ERROR, "invalid transition state for mfvsketch"); } cur_size = ExtractDatumLen(PointerGetDatum(MFV_DATA(mfv) + mfv->mfvs[i].offset), mfv->typLen, mfv->typByVal, cur_capacity); if (cur_size > cur_capacity) { elog(ERROR, "invalid transition state for mfvsketch"); } } }
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { Datum origval, val, result; bool isnull; Oid typoid, foutoid, typioparam; int32 typmod; bool typisvarlena; SPI_result = 0; if (fnumber > tuple->t_data->t_natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); if (isnull) return NULL; if (fnumber > 0) { typoid = tupdesc->attrs[fnumber - 1]->atttypid; typmod = tupdesc->attrs[fnumber - 1]->atttypmod; } else { typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; typmod = -1; } getTypeOutputInfo(typoid, &foutoid, &typioparam, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (typisvarlena) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; result = OidFunctionCall3(foutoid, val, ObjectIdGetDatum(typioparam), Int32GetDatum(typmod)); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); return DatumGetCString(result); }
/* * SQL function to_json(anyvalue) */ Datum to_json(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); StringInfo result; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); result = makeStringInfo(); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); datum_to_json(val, false, result, tcategory, typoutput); PG_RETURN_TEXT_P(cstring_to_text(result->data)); }
/* * CreateBufferPrinter * * Create a new BufferPrinterState to use for printing tuples to a buffer */ BufferPrinterState * CreateBufferPrinter(TupleDesc attrinfo, List *targetlist, int16 *formats) { BufferPrinterState *self = (BufferPrinterState *) palloc0(sizeof(BufferPrinterState)); int i; int numAttrs; self->attrinfo = attrinfo; self->typeinfo = NULL; self->targetlist = targetlist; self->formats = formats; /* get rid of any old data */ if (self->typeinfo) pfree(self->typeinfo); self->typeinfo = NULL; numAttrs = self->attrinfo->natts; if (numAttrs <= 0) return NULL; self->typeinfo = (BufferPrinterAttrInfo *) palloc0(numAttrs * sizeof(BufferPrinterAttrInfo)); for (i = 0; i < numAttrs; i++) { BufferPrinterAttrInfo *thisState = self->typeinfo + i; int16 format = (formats ? formats[i] : 0); thisState->format = format; if (format == 0) { getTypeOutputInfo(attrinfo->attrs[i]->atttypid, &thisState->typoutput, &thisState->typisvarlena); fmgr_info(thisState->typoutput, &thisState->finfo); } else if (format == 1) { getTypeBinaryOutputInfo(attrinfo->attrs[i]->atttypid, &thisState->typsend, &thisState->typisvarlena); fmgr_info(thisState->typsend, &thisState->finfo); } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", format))); } return self; }
static SV * plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) { HV *hv; int i; hv = newHV(); for (i = 0; i < tupdesc->natts; i++) { Datum attr; bool isnull; char *attname; char *outputstr; Oid typoutput; bool typisvarlena; int namelen; SV *sv; if (tupdesc->attrs[i]->attisdropped) continue; attname = NameStr(tupdesc->attrs[i]->attname); namelen = strlen(attname); attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (isnull) { /* Store (attname => undef) and move on. */ hv_store(hv, attname, namelen, newSV(0), 0); continue; } /* XXX should have a way to cache these lookups */ getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); outputstr = DatumGetCString(OidFunctionCall1(typoutput, attr)); sv = newSVpv(outputstr, 0); #if PERL_BCDVERSION >= 0x5006000L if (GetDatabaseEncoding() == PG_UTF8) SvUTF8_on(sv); #endif hv_store(hv, attname, namelen, sv, 0); pfree(outputstr); } return newRV_noinc((SV *) hv); }
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { char *result; Datum origval, val; bool isnull; Oid typoid, foutoid; bool typisvarlena; SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); if (isnull) return NULL; if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; getTypeOutputInfo(typoid, &foutoid, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; result = OidOutputFunctionCall(foutoid, val); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); return result; }
/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * for building unique-constraint and exclusion-constraint error messages. * * The passed-in values/nulls arrays are the "raw" input to the index AM, * e.g. results of FormIndexDatum --- this is not necessarily what is stored * in the index, but it's what the user perceives to be stored. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { StringInfoData buf; int natts = indexRelation->rd_rel->relnatts; int i; initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(RelationGetRelid(indexRelation), true)); for (i = 0; i < natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; /* * The provided data is not necessarily of the type stored in the * index; rather it is of the index opclass's input type. So look * at rd_opcintype not the index tupdesc. * * Note: this is a bit shaky for opclasses that have pseudotype * input types such as ANYARRAY or RECORD. Currently, the * typoutput functions associated with the pseudotypes will work * okay, but we might have to try harder in future. */ getTypeOutputInfo(indexRelation->rd_opcintype[i], &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }
bytea *cmsketch_init_transval(Oid typOid) { bool typIsVarlena; cmtransval *transval; /* allocate and zero out a transval via palloc0 */ bytea * transblob = (bytea *)palloc0(CM_TRANSVAL_SZ); SET_VARSIZE(transblob, CM_TRANSVAL_SZ); transval = (cmtransval *)VARDATA(transblob); transval->typOid = typOid; getTypeOutputInfo(transval->typOid, &(transval->outFuncOid), &typIsVarlena); return(transblob); }
/* * Deprecated function. * Use "pg_trgm.similarity_threshold" GUC variable instead of this function. */ Datum set_limit(PG_FUNCTION_ARGS) { float4 nlimit = PG_GETARG_FLOAT4(0); char *nlimit_str; Oid func_out_oid; bool is_varlena; getTypeOutputInfo(FLOAT4OID, &func_out_oid, &is_varlena); nlimit_str = OidOutputFunctionCall(func_out_oid, Float4GetDatum(nlimit)); SetConfigOption("pg_trgm.similarity_threshold", nlimit_str, PGC_USERSET, PGC_S_SESSION); PG_RETURN_FLOAT4(similarity_threshold); }
/* * Get the lookup info that printtup() needs */ static void printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) { int16 *formats = myState->portal->formats; int i; /* get rid of any old data */ if (myState->myinfo) pfree(myState->myinfo); myState->myinfo = NULL; myState->attrinfo = typeinfo; myState->nattrs = numAttrs; if (numAttrs <= 0) return; myState->myinfo = (PrinttupAttrInfo *) palloc0(numAttrs * sizeof(PrinttupAttrInfo)); for (i = 0; i < numAttrs; i++) { PrinttupAttrInfo *thisState = myState->myinfo + i; int16 format = (formats ? formats[i] : 0); Form_pg_attribute attr = TupleDescAttr(typeinfo, i); thisState->format = format; if (format == 0) { getTypeOutputInfo(attr->atttypid, &thisState->typoutput, &thisState->typisvarlena); fmgr_info(thisState->typoutput, &thisState->finfo); } else if (format == 1) { getTypeBinaryOutputInfo(attr->atttypid, &thisState->typsend, &thisState->typisvarlena); fmgr_info(thisState->typsend, &thisState->finfo); } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", format))); } }
/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * only for building unique-constraint error messages, but we don't want * to hardwire the spelling of the messages here. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { /* * XXX for the moment we use the index's tupdesc as a guide to the * datatypes of the values. This is okay for btree indexes but is in * fact the wrong thing in general. This will have to be fixed if we * are ever to support non-btree unique indexes. */ TupleDesc tupdesc = RelationGetDescr(indexRelation); StringInfoData buf; int i; initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(RelationGetRelid(indexRelation), true)); for (i = 0; i < tupdesc->natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }
static void print_value(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, int i) { bool typisvarlena; bool isnull; Oid typoutput; Form_pg_attribute attr = tupdesc->attrs[i]; Datum origval = fastgetattr(tuple, i + 1, tupdesc, &isnull); Oid typid = attr->atttypid; getTypeOutputInfo(typid, &typoutput, &typisvarlena); if (isnull) { appendStringInfoString(s, "null"); } else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) { appendStringInfoString(s, "\"???unchanged-toast-datum???\""); } else if (!typisvarlena) { print_literal(s, typid, OidOutputFunctionCall(typoutput, origval)); } else { Datum val = PointerGetDatum(PG_DETOAST_DATUM(origval)); print_literal(s, typid, OidOutputFunctionCall(typoutput, val)); } }
/* ---------------- * debugtup - print one tuple for an interactive backend * ---------------- */ void debugtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; int natts = typeinfo->natts; int i; Datum origattr, attr; char *value; bool isnull; Oid typoutput; bool typisvarlena; for (i = 0; i < natts; ++i) { origattr = slot_getattr(slot, i + 1, &isnull); if (isnull) continue; getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &typoutput, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (typisvarlena) attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); else attr = origattr; value = OidOutputFunctionCall(typoutput, attr); printatt((unsigned) i + 1, typeinfo->attrs[i], value); pfree(value); /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } printf("\t----\n"); }
/*! * Initialize an mfv sketch * \param max_mfvs the number of "bins" in the histogram * \param typOid the type ID for the column */ bytea *mfv_init_transval(int max_mfvs, Oid typOid) { int initial_size; bool typIsVarLen; bytea * transblob; mfvtransval *transval; /* * initialize mfvtransval, using palloc0 to zero it out. * if typlen is positive (fixed), size chosen accurately. * Else we'll do a conservative estimate of 16 bytes, and repalloc as needed. */ if ((initial_size = get_typlen(typOid)) > 0) initial_size *= max_mfvs*get_typlen(typOid); else /* guess */ initial_size = max_mfvs*16; transblob = (bytea *)palloc0(MFV_TRANSVAL_SZ(max_mfvs) + initial_size); SET_VARSIZE(transblob, MFV_TRANSVAL_SZ(max_mfvs) + initial_size); transval = (mfvtransval *)VARDATA(transblob); transval->max_mfvs = max_mfvs; transval->next_mfv = 0; transval->next_offset = MFV_TRANSVAL_SZ(max_mfvs)-VARHDRSZ; transval->typOid = typOid; getTypeOutputInfo(transval->typOid, &(transval->outFuncOid), &(typIsVarLen)); transval->typLen = get_typlen(transval->typOid); transval->typByVal = get_typbyval(transval->typOid); if (!transval->outFuncOid) { /* no outFunc for this type! */ elog(ERROR, "no outFunc for type %d", transval->typOid); } return(transblob); }
/* check whether the content in the given bytea is safe for mfvtransval */ void check_mfvtransval(bytea *storage) { size_t left_len = VARSIZE(storage); Oid outFuncOid; bool typIsVarLen; mfvtransval *mfv = NULL; if (left_len < VARHDRSZ + sizeof(mfvtransval)) { elog(ERROR, "invalid transition state for mfvsketch"); } mfv = (mfvtransval*)VARDATA(storage); left_len -= VARHDRSZ + sizeof(mfvtransval); if (mfv->next_mfv > mfv->max_mfvs) { elog(ERROR, "invalid transition state for mfvsketch"); } if (mfv->next_offset + VARHDRSZ > VARSIZE(storage)) { elog(ERROR, "invalid transition state for mfvsketch"); } if (InvalidOid == mfv->typOid) { elog(ERROR, "invalid transition state for mfvsketch"); } getTypeOutputInfo(mfv->typOid, &outFuncOid, &typIsVarLen); if (mfv->outFuncOid != outFuncOid || mfv->typLen != get_typlen(mfv->typOid) || mfv->typByVal != get_typbyval(mfv->typOid)) { elog(ERROR, "invalid transition state for mfvsketch"); } if (left_len < sizeof(offsetcnt)*mfv->max_mfvs) { elog(ERROR, "invalid transition state for mfvsketch"); } }
/* * Turn a composite / record into JSON. */ static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup, *tuple; int i; bool needsep = false; const char *sep; sep = use_line_feeds ? ",\n " : ","; td = DatumGetHeapTupleHeader(composite); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuple = &tmptup; appendStringInfoChar(result, '{'); for (i = 0; i < tupdesc->natts; i++) { Datum val; bool isnull; char *attname; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (tupdesc->attrs[i]->attisdropped) continue; if (needsep) appendStringInfoString(result, sep); needsep = true; attname = NameStr(tupdesc->attrs[i]->attname); escape_json(result, attname); appendStringInfoChar(result, ':'); val = heap_getattr(tuple, i + 1, tupdesc, &isnull); getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); if (tupdesc->attrs[i]->atttypid > FirstNormalObjectId) { HeapTuple cast_tuple; Form_pg_cast castForm; cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(cast_tuple)) { castForm = (Form_pg_cast) GETSTRUCT(cast_tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(cast_tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (tupdesc->attrs[i]->atttypid == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (tupdesc->attrs[i]->atttypid == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(tupdesc->attrs[i]->atttypid); datum_to_json(val, isnull, result, tcategory, typoutput); } appendStringInfoChar(result, '}'); ReleaseTupleDesc(tupdesc); }
/* * record_out - output routine for any composite type. */ Datum record_out(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; RecordIOData *my_extra; bool needComma = false; int ncolumns; int i; Datum *values; bool *nulls; StringInfoData buf; /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* And build the result string */ initStringInfo(&buf); appendStringInfoChar(&buf, '('); for (i = 0; i < ncolumns; i++) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; char *tmp; bool nq; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; if (needComma) appendStringInfoChar(&buf, ','); needComma = true; if (nulls[i]) { /* emit nothing... */ continue; } /* * Convert the column value to text */ if (column_info->column_type != column_type) { bool typIsVarlena; getTypeOutputInfo(column_type, &column_info->typiofunc, &typIsVarlena); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } value = OutputFunctionCall(&column_info->proc, values[i]); /* Detect whether we need double quotes for this value */ nq = (value[0] == '\0'); /* force quotes for empty string */ for (tmp = value; *tmp; tmp++) { char ch = *tmp; if (ch == '"' || ch == '\\' || ch == '(' || ch == ')' || ch == ',' || isspace((unsigned char) ch)) { nq = true; break; } } /* And emit the string */ if (nq) appendStringInfoChar(&buf, '"'); for (tmp = value; *tmp; tmp++) { char ch = *tmp; if (ch == '"' || ch == '\\') appendStringInfoChar(&buf, ch); appendStringInfoChar(&buf, ch); } if (nq) appendStringInfoChar(&buf, '"'); } appendStringInfoChar(&buf, ')'); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); PG_RETURN_CSTRING(buf.data); }
Datum hstore_from_record(PG_FUNCTION_ARGS) { HeapTupleHeader rec; int4 buflen; HStore *out; Pairs *pairs; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; RecordIOData *my_extra; int ncolumns; int i, j; Datum *values; bool *nulls; if (PG_ARGISNULL(0)) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); /* * have no tuple to look at, so the only source of type info is the * argtype. The lookup_rowtype_tupdesc call below will error out if we * don't have a known composite type oid here. */ tupType = argtype; tupTypmod = -1; rec = NULL; } else { rec = PG_GETARG_HEAPTUPLEHEADER(0); /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); } tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } pairs = palloc(ncolumns * sizeof(Pairs)); if (rec) { /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); //tuple.t_tableOid = InvalidOid; tuple.t_data = rec; values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); } else { values = NULL; nulls = NULL; } for (i = 0, j = 0; i < ncolumns; ++i) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; pairs[j].key = NameStr(tupdesc->attrs[i]->attname); pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname))); if (!nulls || nulls[i]) { pairs[j].val = NULL; pairs[j].vallen = 4; pairs[j].isnull = true; pairs[j].needfree = false; ++j; continue; } /* * Convert the column value to text */ if (column_info->column_type != column_type) { bool typIsVarlena; getTypeOutputInfo(column_type, &column_info->typiofunc, &typIsVarlena); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } value = OutputFunctionCall(&column_info->proc, values[i]); pairs[j].val = value; pairs[j].vallen = hstoreCheckValLen(strlen(value)); pairs[j].isnull = false; pairs[j].needfree = false; ++j; } ncolumns = hstoreUniquePairs(pairs, j, &buflen); out = hstorePairs(pairs, ncolumns, buflen); ReleaseTupleDesc(tupdesc); PG_RETURN_POINTER(out); }
char * tuple_to_cstring(TupleDesc tupdesc, HeapTuple tuple) { bool needComma = false; int ncolumns; int i; Datum *values; bool *nulls; StringInfoData buf; ncolumns = tupdesc->natts; values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(tuple, tupdesc, values, nulls); /* And build the result string */ initStringInfo(&buf); for (i = 0; i < ncolumns; i++) { char *value; char *tmp; bool nq; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; if (needComma) appendStringInfoChar(&buf, ','); needComma = true; if (nulls[i]) { /* emit nothing... */ continue; } else { Oid foutoid; bool typisvarlena; getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &foutoid, &typisvarlena); value = OidOutputFunctionCall(foutoid, values[i]); } /* Detect whether we need double quotes for this value */ nq = (value[0] == '\0'); /* force quotes for empty string */ for (tmp = value; *tmp; tmp++) { char ch = *tmp; if (ch == '"' || ch == '\\' || ch == '(' || ch == ')' || ch == ',' || isspace((unsigned char) ch)) { nq = true; break; } } /* And emit the string */ if (nq) appendStringInfoChar(&buf, '"'); for (tmp = value; *tmp; tmp++) { char ch = *tmp; if (ch == '"' || ch == '\\') appendStringInfoChar(&buf, ch); appendStringInfoChar(&buf, ch); } if (nq) appendStringInfoChar(&buf, '"'); } pfree(values); pfree(nulls); return buf.data; }
/* * Extract all item values from a BRIN index page * * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass); */ Datum brin_page_items(PG_FUNCTION_ARGS) { brin_page_state *state; FuncCallContext *fctx; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); if (SRF_IS_FIRSTCALL()) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Oid indexRelid = PG_GETARG_OID(1); Page page; TupleDesc tupdesc; MemoryContext mctx; Relation indexRel; AttrNumber attno; /* minimally verify the page we got */ page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular"); /* create a function context for cross-call persistence */ fctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ mctx = MemoryContextSwitchTo(fctx->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"); indexRel = index_open(indexRelid, AccessShareLock); state = palloc(offsetof(brin_page_state, columns) + sizeof(brin_column_state) * RelationGetDescr(indexRel)->natts); state->bdesc = brin_build_desc(indexRel); state->page = page; state->offset = FirstOffsetNumber; state->unusedItem = false; state->done = false; state->dtup = NULL; /* * Initialize output functions for all indexed datatypes; simplifies * calling them later. */ for (attno = 1; attno <= state->bdesc->bd_tupdesc->natts; attno++) { Oid output; bool isVarlena; BrinOpcInfo *opcinfo; int i; brin_column_state *column; opcinfo = state->bdesc->bd_info[attno - 1]; column = palloc(offsetof(brin_column_state, outputFn) + sizeof(FmgrInfo) * opcinfo->oi_nstored); column->nstored = opcinfo->oi_nstored; for (i = 0; i < opcinfo->oi_nstored; i++) { getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena); fmgr_info(output, &column->outputFn[i]); } state->columns[attno - 1] = column; } index_close(indexRel, AccessShareLock); fctx->user_fctx = state; fctx->tuple_desc = BlessTupleDesc(tupdesc); MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); state = fctx->user_fctx; if (!state->done) { HeapTuple result; Datum values[7]; bool nulls[7]; /* * This loop is called once for every attribute of every tuple in the * page. At the start of a tuple, we get a NULL dtup; that's our * signal for obtaining and decoding the next one. If that's not the * case, we output the next attribute. */ if (state->dtup == NULL) { BrinTuple *tup; MemoryContext mctx; ItemId itemId; /* deformed tuple must live across calls */ mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); /* verify item status: if there's no data, we can't decode */ itemId = PageGetItemId(state->page, state->offset); if (ItemIdIsUsed(itemId)) { tup = (BrinTuple *) PageGetItem(state->page, PageGetItemId(state->page, state->offset)); state->dtup = brin_deform_tuple(state->bdesc, tup); state->attno = 1; state->unusedItem = false; } else state->unusedItem = true; MemoryContextSwitchTo(mctx); } else state->attno++; MemSet(nulls, 0, sizeof(nulls)); if (state->unusedItem) { values[0] = UInt16GetDatum(state->offset); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; } else { int att = state->attno - 1; values[0] = UInt16GetDatum(state->offset); values[1] = UInt32GetDatum(state->dtup->bt_blkno); values[2] = UInt16GetDatum(state->attno); values[3] = BoolGetDatum(state->dtup->bt_columns[att].bv_allnulls); values[4] = BoolGetDatum(state->dtup->bt_columns[att].bv_hasnulls); values[5] = BoolGetDatum(state->dtup->bt_placeholder); if (!state->dtup->bt_columns[att].bv_allnulls) { BrinValues *bvalues = &state->dtup->bt_columns[att]; StringInfoData s; bool first; int i; initStringInfo(&s); appendStringInfoChar(&s, '{'); first = true; for (i = 0; i < state->columns[att]->nstored; i++) { char *val; if (!first) appendStringInfoString(&s, " .. "); first = false; val = OutputFunctionCall(&state->columns[att]->outputFn[i], bvalues->bv_values[i]); appendStringInfoString(&s, val); pfree(val); } appendStringInfoChar(&s, '}'); values[6] = CStringGetTextDatum(s.data); pfree(s.data); } else { nulls[6] = true; } } result = heap_form_tuple(fctx->tuple_desc, values, nulls); /* * If the item was unused, jump straight to the next one; otherwise, * the only cleanup needed here is to set our signal to go to the next * tuple in the following iteration, by freeing the current one. */ if (state->unusedItem) state->offset = OffsetNumberNext(state->offset); else if (state->attno >= state->bdesc->bd_tupdesc->natts) { pfree(state->dtup); state->dtup = NULL; state->offset = OffsetNumberNext(state->offset); } /* * If we're beyond the end of the page, set flag to end the function * in the following iteration. */ if (state->offset > PageGetMaxOffsetNumber(state->page)) state->done = true; SRF_RETURN_NEXT(fctx, HeapTupleGetDatum(result)); } brin_free_desc(state->bdesc); SRF_RETURN_DONE(fctx); }
/* * json_agg transition function */ Datum json_agg_transfn(PG_FUNCTION_ARGS) { Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); MemoryContext aggcontext, oldcontext; StringInfo state; Datum val; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "json_agg_transfn called in non-aggregate context"); } if (PG_ARGISNULL(0)) { /* * Make this StringInfo in a context where it will persist for the * duration off the aggregate call. It's only needed for this initial * piece, as the StringInfo routines make sure they use the right * context to enlarge the object if necessary. */ oldcontext = MemoryContextSwitchTo(aggcontext); state = makeStringInfo(); MemoryContextSwitchTo(oldcontext); appendStringInfoChar(state, '['); } else { state = (StringInfo) PG_GETARG_POINTER(0); appendStringInfoString(state, ", "); } /* fast path for NULLs */ if (PG_ARGISNULL(1)) { val = (Datum) 0; datum_to_json(val, true, state, 0, InvalidOid); PG_RETURN_POINTER(state); } val = PG_GETARG_DATUM(1); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); if (!PG_ARGISNULL(0) && (tcategory == TYPCATEGORY_ARRAY || tcategory == TYPCATEGORY_COMPOSITE)) { appendStringInfoString(state, "\n "); } datum_to_json(val, false, state, tcategory, typoutput); /* * The transition type for array_agg() is declared to be "internal", which * is a pass-by-value type the same size as a pointer. So we can safely * pass the ArrayBuildState pointer through nodeAgg.c's machinations. */ PG_RETURN_POINTER(state); }
/* * SQL function to_json(anyvalue) */ Datum to_json(PG_FUNCTION_ARGS) { Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); StringInfo result; Datum orig_val, val; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); result = makeStringInfo(); orig_val = PG_ARGISNULL(0) ? (Datum) 0 : PG_GETARG_DATUM(0); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena && orig_val != (Datum) 0) val = PointerGetDatum(PG_DETOAST_DATUM(orig_val)); else val = orig_val; datum_to_json(val, false, result, tcategory, typoutput); /* Clean up detoasted copy, if any */ if (val != orig_val) pfree(DatumGetPointer(val)); PG_RETURN_TEXT_P(cstring_to_text(result->data)); }
/* * Turn a composite / record into JSON. */ static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup, *tuple; int i; bool needsep = false; char *sep; sep = use_line_feeds ? ",\n " : ","; td = DatumGetHeapTupleHeader(composite); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuple = &tmptup; appendStringInfoChar(result,'{'); for (i = 0; i < tupdesc->natts; i++) { Datum val, origval; bool isnull; char *attname; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; if (tupdesc->attrs[i]->attisdropped) continue; if (needsep) appendStringInfoString(result,sep); needsep = true; attname = NameStr(tupdesc->attrs[i]->attname); escape_json(result,attname); appendStringInfoChar(result,':'); origval = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (tupdesc->attrs[i]->atttypid == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (tupdesc->attrs[i]->atttypid == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(tupdesc->attrs[i]->atttypid); getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena && ! isnull) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; datum_to_json(val, result, tcategory, typoutput); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); } appendStringInfoChar(result,'}'); ReleaseTupleDesc(tupdesc); }
/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * for building unique-constraint and exclusion-constraint error messages. * * Note that if the user does not have permissions to view all of the * columns involved then a NULL is returned. Returning a partial key seems * unlikely to be useful and we have no way to know which of the columns the * user provided (unlike in ExecBuildSlotValueDescription). * * The passed-in values/nulls arrays are the "raw" input to the index AM, * e.g. results of FormIndexDatum --- this is not necessarily what is stored * in the index, but it's what the user perceives to be stored. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { StringInfoData buf; Form_pg_index idxrec; HeapTuple ht_idx; int natts = indexRelation->rd_rel->relnatts; int i; int keyno; Oid indexrelid = RelationGetRelid(indexRelation); Oid indrelid; AclResult aclresult; /* * Check permissions- if the user does not have access to view all of the * key columns then return NULL to avoid leaking data. * * First check if RLS is enabled for the relation. If so, return NULL to * avoid leaking data. * * Next we need to check table-level SELECT access and then, if there is * no access there, check column-level permissions. */ /* * Fetch the pg_index tuple by the Oid of the index */ ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid)); if (!HeapTupleIsValid(ht_idx)) elog(ERROR, "cache lookup failed for index %u", indexrelid); idxrec = (Form_pg_index) GETSTRUCT(ht_idx); indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); /* RLS check- if RLS is enabled then we don't return anything. */ if (check_enable_rls(indrelid, GetUserId(), true) == RLS_ENABLED) { ReleaseSysCache(ht_idx); return NULL; } /* Table-level SELECT is enough, if the user has it */ aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) { /* * No table-level access, so step through the columns in the index and * make sure the user has SELECT rights on all of them. */ for (keyno = 0; keyno < idxrec->indnatts; keyno++) { AttrNumber attnum = idxrec->indkey.values[keyno]; /* * Note that if attnum == InvalidAttrNumber, then this is an index * based on an expression and we return no detail rather than try * to figure out what column(s) the expression includes and if the * user has SELECT rights on them. */ if (attnum == InvalidAttrNumber || pg_attribute_aclcheck(indrelid, attnum, GetUserId(), ACL_SELECT) != ACLCHECK_OK) { /* No access, so clean up and return */ ReleaseSysCache(ht_idx); return NULL; } } } ReleaseSysCache(ht_idx); initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(indexrelid, true)); for (i = 0; i < natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; /* * The provided data is not necessarily of the type stored in the * index; rather it is of the index opclass's input type. So look * at rd_opcintype not the index tupdesc. * * Note: this is a bit shaky for opclasses that have pseudotype * input types such as ANYARRAY or RECORD. Currently, the * typoutput functions associated with the pseudotypes will work * okay, but we might have to try harder in future. */ getTypeOutputInfo(indexRelation->rd_opcintype[i], &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }
/* * CopyOps_BuildOneRowTo * Build one row message to be sent to remote nodes through COPY protocol */ char * CopyOps_BuildOneRowTo(TupleDesc tupdesc, Datum *values, bool *nulls, int *len) { bool need_delim = false; char *res; int i; FmgrInfo *out_functions; Form_pg_attribute *attr = tupdesc->attrs; StringInfo buf; /* Get info about the columns we need to process. */ out_functions = (FmgrInfo *) palloc(tupdesc->natts * sizeof(FmgrInfo)); for (i = 0; i < tupdesc->natts; i++) { Oid out_func_oid; bool isvarlena; /* Do not need any information for dropped attributes */ if (attr[i]->attisdropped) continue; getTypeOutputInfo(attr[i]->atttypid, &out_func_oid, &isvarlena); fmgr_info(out_func_oid, &out_functions[i]); } /* Initialize output buffer */ buf = makeStringInfo(); for (i = 0; i < tupdesc->natts; i++) { Datum value = values[i]; bool isnull = nulls[i]; /* Do not need any information for dropped attributes */ if (attr[i]->attisdropped) continue; if (need_delim) appendStringInfoCharMacro(buf, COPYOPS_DELIMITER); need_delim = true; if (isnull) { /* Null print value to client */ appendBinaryStringInfo(buf, "\\N", strlen("\\N")); } else { char *string; string = OutputFunctionCall(&out_functions[i], value); attribute_out_text(buf, string); pfree(string); } } /* Record length of message */ *len = buf->len; res = pstrdup(buf->data); pfree(out_functions); pfree(buf->data); pfree(buf); return res; }
/* * Extract all item values from a BRIN index page * * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass); */ Datum brin_page_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Oid indexRelid = PG_GETARG_OID(1); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; MemoryContext oldcontext; Tuplestorestate *tupstore; Relation indexRel; brin_column_state **columns; BrinDesc *bdesc; BrinMemTuple *dtup; Page page; OffsetNumber offset; AttrNumber attno; bool unusedItem; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize) || rsinfo->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); /* 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"); /* Build tuplestore to hold the result rows */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); indexRel = index_open(indexRelid, AccessShareLock); bdesc = brin_build_desc(indexRel); /* minimally verify the page we got */ page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular"); /* * Initialize output functions for all indexed datatypes; simplifies * calling them later. */ columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts); for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++) { Oid output; bool isVarlena; BrinOpcInfo *opcinfo; int i; brin_column_state *column; opcinfo = bdesc->bd_info[attno - 1]; column = palloc(offsetof(brin_column_state, outputFn) + sizeof(FmgrInfo) * opcinfo->oi_nstored); column->nstored = opcinfo->oi_nstored; for (i = 0; i < opcinfo->oi_nstored; i++) { getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena); fmgr_info(output, &column->outputFn[i]); } columns[attno - 1] = column; } offset = FirstOffsetNumber; unusedItem = false; dtup = NULL; for (;;) { Datum values[7]; bool nulls[7]; /* * This loop is called once for every attribute of every tuple in the * page. At the start of a tuple, we get a NULL dtup; that's our * signal for obtaining and decoding the next one. If that's not the * case, we output the next attribute. */ if (dtup == NULL) { ItemId itemId; /* verify item status: if there's no data, we can't decode */ itemId = PageGetItemId(page, offset); if (ItemIdIsUsed(itemId)) { dtup = brin_deform_tuple(bdesc, (BrinTuple *) PageGetItem(page, itemId)); attno = 1; unusedItem = false; } else unusedItem = true; } else attno++; MemSet(nulls, 0, sizeof(nulls)); if (unusedItem) { values[0] = UInt16GetDatum(offset); nulls[1] = true; nulls[2] = true; nulls[3] = true; nulls[4] = true; nulls[5] = true; nulls[6] = true; } else { int att = attno - 1; values[0] = UInt16GetDatum(offset); values[1] = UInt32GetDatum(dtup->bt_blkno); values[2] = UInt16GetDatum(attno); values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls); values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls); values[5] = BoolGetDatum(dtup->bt_placeholder); if (!dtup->bt_columns[att].bv_allnulls) { BrinValues *bvalues = &dtup->bt_columns[att]; StringInfoData s; bool first; int i; initStringInfo(&s); appendStringInfoChar(&s, '{'); first = true; for (i = 0; i < columns[att]->nstored; i++) { char *val; if (!first) appendStringInfoString(&s, " .. "); first = false; val = OutputFunctionCall(&columns[att]->outputFn[i], bvalues->bv_values[i]); appendStringInfoString(&s, val); pfree(val); } appendStringInfoChar(&s, '}'); values[6] = CStringGetTextDatum(s.data); pfree(s.data); } else { nulls[6] = true; } } tuplestore_putvalues(tupstore, tupdesc, values, nulls); /* * If the item was unused, jump straight to the next one; otherwise, * the only cleanup needed here is to set our signal to go to the next * tuple in the following iteration, by freeing the current one. */ if (unusedItem) offset = OffsetNumberNext(offset); else if (attno >= bdesc->bd_tupdesc->natts) { pfree(dtup); dtup = NULL; offset = OffsetNumberNext(offset); } /* * If we're beyond the end of the page, we're done. */ if (offset > PageGetMaxOffsetNumber(page)) break; } /* clean up and return the tuplestore */ brin_free_desc(bdesc); tuplestore_donestoring(tupstore); index_close(indexRel, AccessShareLock); return (Datum) 0; }
/*! * scalar function taking an mfv sketch, returning a histogram of * its most frequent values */ Datum __mfvsketch_final(PG_FUNCTION_ARGS) { bytea * transblob = PG_GETARG_BYTEA_P(0); mfvtransval *transval = NULL; ArrayType * retval; uint32 i; int dims[2], lbs[2]; /* Oid typInput, typIOParam; */ Oid outFuncOid; bool typIsVarlena; int16 typlen; bool typbyval; char typalign; char typdelim; Oid typioparam; Oid typiofunc; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); if (VARSIZE(transblob) < MFV_TRANSVAL_SZ(0)) PG_RETURN_NULL(); check_mfvtransval(transblob); transval = (mfvtransval *)VARDATA(transblob); /* * We only declare the variable-length array histo here after some sanity * checking. We risk a stack overflow otherwise. In particular, we need to * make sure that transval->max_mfvs is initialized. It might not be if the * (strict) transition function is never called. (MADLIB-254) */ Datum histo[transval->max_mfvs][2]; qsort(transval->mfvs, transval->next_mfv, sizeof(offsetcnt), cnt_cmp_desc); getTypeOutputInfo(INT8OID, &outFuncOid, &typIsVarlena); for (i = 0; i < transval->next_mfv; i++) { void *tmpp = mfv_transval_getval(transblob,i); Datum curval = PointerExtractDatum(tmpp, transval->typByVal); char *countbuf = OidOutputFunctionCall(outFuncOid, Int64GetDatum(transval->mfvs[i].cnt)); char *valbuf = OidOutputFunctionCall(transval->outFuncOid, curval); histo[i][0] = PointerGetDatum(cstring_to_text(valbuf)); histo[i][1] = PointerGetDatum(cstring_to_text(countbuf)); pfree(countbuf); pfree(valbuf); } /* * Get info about element type */ get_type_io_data(TEXTOID, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typiofunc); dims[0] = i; dims[1] = 2; lbs[0] = lbs[1] = 0; retval = construct_md_array((Datum *)histo, NULL, 2, dims, lbs, TEXTOID, -1, 0, 'i'); PG_RETURN_ARRAYTYPE_P(retval); }