static uint32 build_hash_key(const void *key, Size keysize __attribute__((unused))) { Assert(key); BMBuildHashKey *keyData = (BMBuildHashKey*)key; Datum *k = keyData->attributeValueArr; bool *isNull = keyData->isNullArr; int i; uint32 hashkey = 0; for(i = 0; i < cur_bmbuild->natts; i++) { /* rotate hashkey left 1 bit at each step */ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); if ( isNull[i] && cur_bmbuild->hash_func_is_strict[i]) { /* leave hashkey unmodified, equivalent to hashcode 0 */ } else { hashkey ^= DatumGetUInt32(FunctionCall1(&cur_bmbuild->hash_funcs[i], k[i])); } } return hashkey; }
/* * Add bits of given value to the signature. */ void signValue(BloomState *state, SignType *sign, Datum value, int attno) { uint32 hashVal; int nBit, j; /* * init generator with "column's" number to get "hashed" seed for new * value. We don't want to map the same numbers from different columns * into the same bits! */ mySrand(attno); /* * Init hash sequence to map our value into bits. the same values in * different columns will be mapped into different bits because of step * above */ hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value)); mySrand(hashVal ^ myRand()); for (j = 0; j < state->opts.bitSize[attno]; j++) { /* prevent mutiple evaluation */ nBit = myRand() % (state->opts.bloomLength * BITSIGNTYPE); SETBIT(sign, nBit); } }
/* * worker_hash returns the hashed value of the given value. */ Datum worker_hash(PG_FUNCTION_ARGS) { Datum valueDatum = PG_GETARG_DATUM(0); Datum hashedValueDatum = 0; TypeCacheEntry *typeEntry = NULL; FmgrInfo *hashFunction = NULL; Oid valueDataType = InvalidOid; /* figure out hash function from the data type */ valueDataType = get_fn_expr_argtype(fcinfo->flinfo, 0); typeEntry = lookup_type_cache(valueDataType, TYPECACHE_HASH_PROC_FINFO); if (typeEntry->hash_proc_finfo.fn_oid == InvalidOid) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot find a hash function for the input type"), errhint("Cast input to a data type with a hash function."))); } hashFunction = palloc0(sizeof(FmgrInfo)); fmgr_info_copy(hashFunction, &(typeEntry->hash_proc_finfo), CurrentMemoryContext); /* calculate hash value */ hashedValueDatum = FunctionCall1(hashFunction, valueDatum); PG_RETURN_INT32(hashedValueDatum); }
void hlparsetext(TSCfgInfo * cfg, HLPRSTEXT * prs, QUERYTYPE * query, char *buf, int4 buflen) { int type, lenlemm; char *lemm = NULL; WParserInfo *prsobj = findprs(cfg->prs_id); LexizeData ldata; TSLexeme *norms; ParsedLex *lexs; prsobj->prs = (void *) DatumGetPointer( FunctionCall2( &(prsobj->start_info), PointerGetDatum(buf), Int32GetDatum(buflen) ) ); LexizeInit(&ldata, cfg); do { type = DatumGetInt32(FunctionCall3( &(prsobj->getlexeme_info), PointerGetDatum(prsobj->prs), PointerGetDatum(&lemm), PointerGetDatum(&lenlemm))); if (type > 0 && lenlemm >= MAXSTRLEN) { #ifdef IGNORE_LONGLEXEME ereport(NOTICE, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("A word you are indexing is too long. It will be ignored."))); continue; #else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("A word you are indexing is too long"))); #endif } LexizeAddLemm(&ldata, type, lemm, lenlemm); do { if ((norms = LexizeExec(&ldata, &lexs)) != NULL) addHLParsedLex(prs, query, lexs, norms); else addHLParsedLex(prs, query, lexs, NULL); } while (norms); } while (type > 0); FunctionCall1( &(prsobj->end_info), PointerGetDatum(prsobj->prs) ); }
/* * Compute the hash value for a tuple * * The passed-in key is a pointer to TupleHashEntryData. In an actual hash * table entry, the firstTuple field points to a tuple (in MinimalTuple * format). LookupTupleHashEntry sets up a dummy TupleHashEntryData with a * NULL firstTuple field --- that cues us to look at the inputslot instead. * This convention avoids the need to materialize virtual input tuples unless * they actually need to get copied into the table. * * Also, the caller must select an appropriate memory context for running * the hash functions. (dynahash.c doesn't change CurrentMemoryContext.) */ static uint32 TupleHashTableHash(struct tuplehash_hash *tb, const MinimalTuple tuple) { TupleHashTable hashtable = (TupleHashTable) tb->private_data; int numCols = hashtable->numCols; AttrNumber *keyColIdx = hashtable->keyColIdx; uint32 hashkey = hashtable->hash_iv; TupleTableSlot *slot; FmgrInfo *hashfunctions; int i; if (tuple == NULL) { /* Process the current input tuple for the table */ slot = hashtable->inputslot; hashfunctions = hashtable->in_hash_funcs; } else { /* * Process a tuple already stored in the table. * * (this case never actually occurs due to the way simplehash.h is * used, as the hash-value is stored in the entries) */ slot = hashtable->tableslot; ExecStoreMinimalTuple(tuple, slot, false); hashfunctions = hashtable->tab_hash_funcs; } for (i = 0; i < numCols; i++) { AttrNumber att = keyColIdx[i]; Datum attr; bool isNull; /* rotate hashkey left 1 bit at each step */ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); attr = slot_getattr(slot, att, &isNull); if (!isNull) /* treat nulls as having hash key 0 */ { uint32 hkey; hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr)); hashkey ^= hkey; } } /* * The way hashes are combined above, among each other and with the IV, * doesn't lead to good bit perturbation. As the IV's goal is to lead to * achieve that, perform a round of hashing of the combined hash - * resulting in near perfect perturbation. */ return murmurhash32(hashkey); }
/* * Convert using a from-SQL transform function. */ static PyObject * PLyObject_FromTransform(PLyDatumToOb *arg, Datum d) { Datum t; t = FunctionCall1(&arg->u.transform.typtransform, d); return (PyObject *) DatumGetPointer(t); }
/* * Build a BrinDesc used to create or scan a BRIN index */ BrinDesc * brin_build_desc(Relation rel) { BrinOpcInfo **opcinfo; BrinDesc *bdesc; TupleDesc tupdesc; int totalstored = 0; int keyno; long totalsize; MemoryContext cxt; MemoryContext oldcxt; cxt = AllocSetContextCreate(CurrentMemoryContext, "brin desc cxt", ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_MAXSIZE); oldcxt = MemoryContextSwitchTo(cxt); tupdesc = RelationGetDescr(rel); /* * Obtain BrinOpcInfo for each indexed column. While at it, accumulate * the number of columns stored, since the number is opclass-defined. */ opcinfo = (BrinOpcInfo **) palloc(sizeof(BrinOpcInfo *) * tupdesc->natts); for (keyno = 0; keyno < tupdesc->natts; keyno++) { FmgrInfo *opcInfoFn; opcInfoFn = index_getprocinfo(rel, keyno + 1, BRIN_PROCNUM_OPCINFO); opcinfo[keyno] = (BrinOpcInfo *) DatumGetPointer(FunctionCall1(opcInfoFn, tupdesc->attrs[keyno]->atttypid)); totalstored += opcinfo[keyno]->oi_nstored; } /* Allocate our result struct and fill it in */ totalsize = offsetof(BrinDesc, bd_info) + sizeof(BrinOpcInfo *) * tupdesc->natts; bdesc = palloc(totalsize); bdesc->bd_context = cxt; bdesc->bd_index = rel; bdesc->bd_tupdesc = tupdesc; bdesc->bd_disktdesc = NULL; /* generated lazily */ bdesc->bd_totalstored = totalstored; for (keyno = 0; keyno < tupdesc->natts; keyno++) bdesc->bd_info[keyno] = opcinfo[keyno]; pfree(opcinfo); MemoryContextSwitchTo(oldcxt); return bdesc; }
/* ---------------- * index_markpos - mark a scan position * ---------------- */ void index_markpos(IndexScanDesc scan) { FmgrInfo *procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(ammarkpos); FunctionCall1(procedure, PointerGetDatum(scan)); }
/* * _hash_datum2hashkey -- given a Datum, call the index's hash procedure * * The Datum is assumed to be of the index's column type, so we can use the * "primary" hash procedure that's tracked for us by the generic index code. */ uint32 _hash_datum2hashkey(Relation rel, Datum key) { FmgrInfo *procinfo; /* XXX assumes index has only one attribute */ procinfo = index_getprocinfo(rel, 1, HASHPROC); return DatumGetUInt32(FunctionCall1(procinfo, key)); }
static void prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) { TupleDesc tupdesc; MemoryContext oldcontext; PrsStorage *st; TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid); char *lex = NULL; int llen = 0, type = 0; void *prsdata; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (PrsStorage *) palloc(sizeof(PrsStorage)); st->cur = 0; st->len = 16; st->list = (LexemeEntry *) palloc(sizeof(LexemeEntry) * st->len); prsdata = (void *) DatumGetPointer(FunctionCall2(&prs->prsstart, PointerGetDatum(VARDATA(txt)), Int32GetDatum(VARSIZE(txt) - VARHDRSZ))); while ((type = DatumGetInt32(FunctionCall3(&prs->prstoken, PointerGetDatum(prsdata), PointerGetDatum(&lex), PointerGetDatum(&llen)))) != 0) { if (st->cur >= st->len) { st->len = 2 * st->len; st->list = (LexemeEntry *) repalloc(st->list, sizeof(LexemeEntry) * st->len); } st->list[st->cur].lexeme = palloc(llen + 1); memcpy(st->list[st->cur].lexeme, lex, llen); st->list[st->cur].lexeme[llen] = '\0'; st->list[st->cur].type = type; st->cur++; } FunctionCall1(&prs->prsend, PointerGetDatum(prsdata)); st->len = st->cur; st->cur = 0; funcctx->user_fctx = (void *) st; tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
/* ---------------- * index_restrpos - restore a scan position * * NOTE: this only restores the internal scan state of the index AM. * The current result tuple (scan->xs_ctup) doesn't change. See comments * for ExecRestrPos(). * ---------------- */ void index_restrpos(IndexScanDesc scan) { FmgrInfo *procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(amrestrpos); scan->kill_prior_tuple = false; /* for safety */ FunctionCall1(procedure, PointerGetDatum(scan)); }
/* * Compute the hash value for a tuple * * The passed-in key is a pointer to TupleHashEntryData. In an actual hash * table entry, the firstTuple field points to a tuple (in MinimalTuple * format). LookupTupleHashEntry sets up a dummy TupleHashEntryData with a * NULL firstTuple field --- that cues us to look at the inputslot instead. * This convention avoids the need to materialize virtual input tuples unless * they actually need to get copied into the table. * * Also, the caller must select an appropriate memory context for running * the hash functions. (dynahash.c doesn't change CurrentMemoryContext.) */ static uint32 TupleHashTableHash(struct tuplehash_hash *tb, const MinimalTuple tuple) { TupleHashTable hashtable = (TupleHashTable) tb->private_data; int numCols = hashtable->numCols; AttrNumber *keyColIdx = hashtable->keyColIdx; uint32 hashkey = hashtable->hash_iv; TupleTableSlot *slot; FmgrInfo *hashfunctions; int i; if (tuple == NULL) { /* Process the current input tuple for the table */ slot = hashtable->inputslot; hashfunctions = hashtable->in_hash_funcs; } else { /* * Process a tuple already stored in the table. * * (this case never actually occurs due to the way simplehash.h is * used, as the hash-value is stored in the entries) */ slot = hashtable->tableslot; ExecStoreMinimalTuple(tuple, slot, false); hashfunctions = hashtable->tab_hash_funcs; } for (i = 0; i < numCols; i++) { AttrNumber att = keyColIdx[i]; Datum attr; bool isNull; /* rotate hashkey left 1 bit at each step */ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); attr = slot_getattr(slot, att, &isNull); if (!isNull) /* treat nulls as having hash key 0 */ { uint32 hkey; hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr)); hashkey ^= hkey; } } return hashkey; }
/* * Convert using a to-SQL transform function. */ static Datum PLyObject_ToTransform(PLyObToDatum *arg, PyObject *plrv, bool *isnull, bool inarray) { if (plrv == Py_None) { *isnull = true; return (Datum) 0; } *isnull = false; return FunctionCall1(&arg->u.transform.typtransform, PointerGetDatum(plrv)); }
/* * Compute the hash value for a tuple * * The passed-in key is a pointer to TupleHashEntryData. In an actual hash * table entry, the firstTuple field points to a tuple (in MinimalTuple * format). LookupTupleHashEntry sets up a dummy TupleHashEntryData with a * NULL firstTuple field --- that cues us to look at the inputslot instead. * This convention avoids the need to materialize virtual input tuples unless * they actually need to get copied into the table. * * CurTupleHashTable must be set before calling this, since dynahash.c * doesn't provide any API that would let us get at the hashtable otherwise. * * Also, the caller must select an appropriate memory context for running * the hash functions. (dynahash.c doesn't change CurrentMemoryContext.) */ static uint32 TupleHashTableHash(const void *key, Size keysize) { MinimalTuple tuple = ((const TupleHashEntryData *) key)->firstTuple; TupleTableSlot *slot; TupleHashTable hashtable = CurTupleHashTable; int numCols = hashtable->numCols; AttrNumber *keyColIdx = hashtable->keyColIdx; FmgrInfo *hashfunctions; uint32 hashkey = 0; int i; if (tuple == NULL) { /* Process the current input tuple for the table */ slot = hashtable->inputslot; hashfunctions = hashtable->in_hash_funcs; } else { /* Process a tuple already stored in the table */ /* (this case never actually occurs in current dynahash.c code) */ slot = hashtable->tableslot; ExecStoreMinimalTuple(tuple, slot, false); hashfunctions = hashtable->tab_hash_funcs; } for (i = 0; i < numCols; i++) { AttrNumber att = keyColIdx[i]; Datum attr; bool isNull; /* rotate hashkey left 1 bit at each step */ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); attr = slot_getattr(slot, att, &isNull); if (!isNull) /* treat nulls as having hash key 0 */ { uint32 hkey; hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr)); hashkey ^= hkey; } } return hashkey; }
/* * FindShardInterval finds a single shard interval in the cache for the * given partition column value. */ ShardInterval * FindShardInterval(Datum partitionColumnValue, ShardInterval **shardIntervalCache, int shardCount, char partitionMethod, FmgrInfo *compareFunction, FmgrInfo *hashFunction, bool useBinarySearch) { ShardInterval *shardInterval = NULL; if (partitionMethod == DISTRIBUTE_BY_HASH) { int hashedValue = DatumGetInt32(FunctionCall1(hashFunction, partitionColumnValue)); if (useBinarySearch) { Assert(compareFunction != NULL); shardInterval = SearchCachedShardInterval(Int32GetDatum(hashedValue), shardIntervalCache, shardCount, compareFunction); } else { uint64 hashTokenIncrement = HASH_TOKEN_COUNT / shardCount; int shardIndex = (uint32) (hashedValue - INT32_MIN) / hashTokenIncrement; Assert(shardIndex <= shardCount); /* * If the shard count is not power of 2, the range of the last * shard becomes larger than others. For that extra piece of range, * we still need to use the last shard. */ if (shardIndex == shardCount) { shardIndex = shardCount - 1; } shardInterval = shardIntervalCache[shardIndex]; } } else { Assert(compareFunction != NULL); shardInterval = SearchCachedShardInterval(partitionColumnValue, shardIntervalCache, shardCount, compareFunction); } return shardInterval; }
/* ---------------- * index_can_return - does index support index-only scans? * ---------------- */ bool index_can_return(Relation indexRelation) { FmgrInfo *procedure; RELATION_CHECKS; /* amcanreturn is optional; assume FALSE if not provided by AM */ if (!RegProcedureIsValid(indexRelation->rd_am->amcanreturn)) return false; GET_REL_PROCEDURE(amcanreturn); return DatumGetBool(FunctionCall1(procedure, PointerGetDatum(indexRelation))); }
/* ---------------- * index_restrpos - restore a scan position * * NOTE: this only restores the internal scan state of the index AM. * The current result tuple (scan->xs_ctup) doesn't change. See comments * for ExecRestrPos(). * * NOTE: in the presence of HOT chains, mark/restore only works correctly * if the scan's snapshot is MVCC-safe; that ensures that there's at most one * returnable tuple in each HOT chain, and so restoring the prior state at the * granularity of the index AM is sufficient. Since the only current user * of mark/restore functionality is nodeMergejoin.c, this effectively means * that merge-join plans only work for MVCC snapshots. This could be fixed * if necessary, but for now it seems unimportant. * ---------------- */ void index_restrpos(IndexScanDesc scan) { FmgrInfo *procedure; Assert(IsMVCCSnapshot(scan->xs_snapshot)); SCAN_CHECKS; GET_SCAN_PROCEDURE(amrestrpos); scan->xs_continue_hot = false; scan->kill_prior_tuple = false; /* for safety */ FunctionCall1(procedure, PointerGetDatum(scan)); }
void initForecastModel(ModelInfo *modelInfo, MemoryContext memoryContext) { MemoryContext oldContext = NULL; if (memoryContext != CurrentMemoryContext) oldContext = MemoryContextSwitchTo(memoryContext); FunctionCall1(&(modelInfo->algInfo->algInitModel), PointerGetDatum(modelInfo)); modelInfo->disAggKeyDenominator=0.0; modelInfo->disaggkey=1; if (oldContext != NULL) MemoryContextSwitchTo(oldContext); }
Datum json_agg_common_transfn( PG_FUNCTION_ARGS, bool top_object ) { StringInfo state; char *serialized_value; FmgrInfo flinfo; state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); /* Append the value unless null. */ if (!PG_ARGISNULL(1)) { /* On the first time through, we ignore the delimiter. */ if (state == NULL) { state = makeJsonAggState(fcinfo); if( top_object ) appendStringInfoChar(state, '{'); /* begin top-level json object */ if(!PG_ARGISNULL(2)) /* output array json-name */ { appendStringInfoQuotedString(state, PG_TEXT_DATUM_GET_CSTR( PG_GETARG_DATUM(2) )); appendStringInfoChar(state, ':'); /* array name delimiter */ } appendStringInfoChar(state, '['); /* array begin */ } else appendStringInfoChar(state, ','); /* delimiter */ //call to serialize_record( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_record; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; serialized_value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, PG_GETARG_DATUM(1) ) ); appendStringInfoString(state, serialized_value); /* append value */ } /* * The transition type for json_agg() is declared to be "internal", * which is a pass-by-value type the same size as a pointer. */ PG_RETURN_POINTER(state); }
/* * initialize a GiST entry with a compressed version of key */ void gistcentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull) { if (!isNull) { GISTENTRY *cep; gistentryinit(*e, k, r, pg, o, l); cep = (GISTENTRY *) DatumGetPointer(FunctionCall1(&giststate->compressFn[nkey], PointerGetDatum(e))); /* compressFn may just return the given pointer */ if (cep != e) gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset, cep->leafkey); } else gistentryinit(*e, (Datum) 0, r, pg, o, l); }
/* * finalizeForecastModel * * no more input tuples to read */ void finalizeForecastModel(ModelInfo *model) { int length = 0; FunctionCall1(&(model->algInfo->algFinalizeForecastModel),PointerGetDatum(model->model)); length = model->model->trainingTupleCount; //MEASURE-CHANGE // length=20; if (model->upperBound==0) { if(((int)(length*0.1))<1) model->lowerBound = 1; else model->lowerBound = (((int)(length*0.1))); //XXX: CHANGE FOR MESURING if(!model->errorArray){ if(((int)(length*0.1))<2) model->errorArray = palloc0(2*sizeof(double)); else model->errorArray = palloc0(((int)(length*0.1))*sizeof(double)); } else if(model->upperBound < ((int)(length*0.1))){ model->errorArray = repalloc(model->errorArray, ((int)(length*0.1))*sizeof(double)); model->errorArray[((int)(length*0.1))-1] = 0.0; } if(((int)(length*0.1))<2) model->upperBound = 2; else model->upperBound = ((int)(length*0.1)); } }
/* ** initialize a GiST entry with a decompressed version of key */ void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, int b, bool l, bool isNull) { if (b && !isNull) { GISTENTRY *dep; gistentryinit(*e, k, r, pg, o, b, l); dep = (GISTENTRY *) DatumGetPointer(FunctionCall1(&giststate->decompressFn[nkey], PointerGetDatum(e))); /* decompressFn may just return the given pointer */ if (dep != e) { gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset, dep->bytes, dep->leafkey); pfree(dep); } } else gistentryinit(*e, (Datum) 0, r, pg, o, 0, l); }
/* ---------------- * index_endscan - end a scan * ---------------- */ void index_endscan(IndexScanDesc scan) { FmgrInfo *procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(amendscan); /* Release any held pin on a heap page */ if (BufferIsValid(scan->xs_cbuf)) { ReleaseBuffer(scan->xs_cbuf); scan->xs_cbuf = InvalidBuffer; } /* End the AM's scan */ FunctionCall1(procedure, PointerGetDatum(scan)); /* Release index refcount acquired by index_beginscan */ RelationDecrementReferenceCount(scan->indexRelation); /* Release the scan data structure itself */ IndexScanEnd(scan); }
static SV * plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo) { dSP; SV *retval; int i; int count; SV *sv; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(&PL_sv_undef); /* no trigger data */ for (i = 0; i < desc->nargs; i++) { if (fcinfo->argnull[i]) XPUSHs(&PL_sv_undef); else if (desc->arg_is_rowtype[i]) { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup; SV *hashref; td = DatumGetHeapTupleHeader(fcinfo->arg[i]); /* 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; hashref = plperl_hash_from_tuple(&tmptup, tupdesc); XPUSHs(sv_2mortal(hashref)); } else { char *tmp; tmp = DatumGetCString(FunctionCall1(&(desc->arg_out_func[i]), fcinfo->arg[i])); sv = newSVpv(tmp, 0); #if PERL_BCDVERSION >= 0x5006000L if (GetDatabaseEncoding() == PG_UTF8) SvUTF8_on(sv); #endif XPUSHs(sv_2mortal(sv)); pfree(tmp); } } PUTBACK; /* Do NOT use G_KEEPERR here */ count = perl_call_sv(desc->reference, G_SCALAR | G_EVAL); SPAGAIN; if (count != 1) { PUTBACK; FREETMPS; LEAVE; elog(ERROR, "didn't get a return item from function"); } if (SvTRUE(ERRSV)) { (void) POPs; PUTBACK; FREETMPS; LEAVE; /* XXX need to find a way to assign an errcode here */ ereport(ERROR, (errmsg("error from Perl function: %s", strip_trailing_ws(SvPV(ERRSV, PL_na))))); } retval = newSVsv(POPs); PUTBACK; FREETMPS; LEAVE; return retval; }
/* * Parse string and lexize words. * * prs will be filled in. */ void parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen) { int type, lenlemm; char *lemm = NULL; LexizeData ldata; TSLexeme *norms; TSConfigCacheEntry *cfg; TSParserCacheEntry *prsobj; void *prsdata; cfg = lookup_ts_config_cache(cfgId); prsobj = lookup_ts_parser_cache(cfg->prsId); prsdata = (void *) DatumGetPointer(FunctionCall2(&prsobj->prsstart, PointerGetDatum(buf), Int32GetDatum(buflen))); LexizeInit(&ldata, cfg); do { type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken), PointerGetDatum(prsdata), PointerGetDatum(&lemm), PointerGetDatum(&lenlemm))); if (type > 0 && lenlemm >= MAXSTRLEN) { #ifdef IGNORE_LONGLEXEME ereport(NOTICE, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("word is too long to be indexed"), errdetail("Words longer than %d characters are ignored.", MAXSTRLEN))); continue; #else ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("word is too long to be indexed"), errdetail("Words longer than %d characters are ignored.", MAXSTRLEN))); #endif } LexizeAddLemm(&ldata, type, lemm, lenlemm); while ((norms = LexizeExec(&ldata, NULL)) != NULL) { TSLexeme *ptr = norms; prs->pos++; /* set pos */ while (ptr->lexeme) { if (prs->curwords == prs->lenwords) { prs->lenwords *= 2; prs->words = (ParsedWord *) repalloc((void *) prs->words, prs->lenwords * sizeof(ParsedWord)); } if (ptr->flags & TSL_ADDPOS) prs->pos++; prs->words[prs->curwords].len = strlen(ptr->lexeme); prs->words[prs->curwords].word = ptr->lexeme; prs->words[prs->curwords].nvariant = ptr->nvariant; prs->words[prs->curwords].flags = ptr->flags & TSL_PREFIX; prs->words[prs->curwords].alen = 0; prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos); ptr++; prs->curwords++; } pfree(norms); } } while (type > 0); FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata)); }
/* * Deserialize a HeapTuple's data from a byte-array. * * This code is based on the binary input handling functions in copy.c. */ HeapTuple DeserializeTuple(SerTupInfo * pSerInfo, StringInfo serialTup) { MemoryContext oldCtxt; TupleDesc tupdesc; HeapTuple htup; int natts; SerAttrInfo *attrInfo; uint32 attr_size; int i; StringInfoData attr_data; bool fHandled; AssertArg(pSerInfo != NULL); AssertArg(serialTup != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* * Flip to our tuple-serialization memory-context, to speed up memory * reclamation operations. */ AssertState(s_tupSerMemCtxt != NULL); oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Receive nulls character-array. */ pq_copymsgbytes(serialTup, pSerInfo->nulls, natts); skipPadding(serialTup); /* Deserialize the non-NULL attributes of this tuple */ initStringInfo(&attr_data); for (i = 0; i < natts; ++i) { attrInfo = pSerInfo->myinfo + i; if (pSerInfo->nulls[i]) /* NULL field. */ { pSerInfo->values[i] = (Datum) 0; continue; } /* * Assume that the data's output will be handled by the special IO * code, and if not then we can handle it the slow way. */ fHandled = true; switch (attrInfo->atttypid) { case INT4OID: pSerInfo->values[i] = Int32GetDatum(stringInfoGetInt32(serialTup)); break; case CHAROID: pSerInfo->values[i] = CharGetDatum(pq_getmsgbyte(serialTup)); skipPadding(serialTup); break; case BPCHAROID: case VARCHAROID: case INT2VECTOROID: /* postgres serialization logic broken, use our own */ case OIDVECTOROID: /* postgres serialization logic broken, use our own */ case ANYARRAYOID: { text *pText; int textSize; textSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (textSize + VARHDRSZ <= attrInfo->varlen_scratch_size) pText = (text *) attrInfo->pv_varlen_scratch; else pText = (text *) palloc(textSize + VARHDRSZ); #else pText = (text *) palloc(textSize + VARHDRSZ); #endif SET_VARSIZE(pText, textSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(pText), textSize); skipPadding(serialTup); pSerInfo->values[i] = PointerGetDatum(pText); break; } case DATEOID: { /* * TODO: I would LIKE to do something more efficient, but * DateADT is not strictly limited to 4 bytes by its * definition. */ DateADT date; pq_copymsgbytes(serialTup, (char *) &date, sizeof(DateADT)); skipPadding(serialTup); pSerInfo->values[i] = DateADTGetDatum(date); break; } case NUMERICOID: { /* * Treat the numeric as a varlena variable, and just push * the whole shebang to the output-buffer. We don't care * about the guts of the numeric. */ Numeric num; int numSize; numSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (numSize + VARHDRSZ <= attrInfo->varlen_scratch_size) num = (Numeric) attrInfo->pv_varlen_scratch; else num = (Numeric) palloc(numSize + VARHDRSZ); #else num = (Numeric) palloc(numSize + VARHDRSZ); #endif SET_VARSIZE(num, numSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(num), numSize); skipPadding(serialTup); pSerInfo->values[i] = NumericGetDatum(num); break; } case ACLITEMOID: { int aclSize, k, cnt; char *inputstring, *starsfree; aclSize = stringInfoGetInt32(serialTup); inputstring = (char*) palloc(aclSize + 1); starsfree = (char*) palloc(aclSize + 1); cnt = 0; pq_copymsgbytes(serialTup, inputstring, aclSize); skipPadding(serialTup); inputstring[aclSize] = '\0'; for(k=0; k<aclSize; k++) { if( inputstring[k] != '*') { starsfree[cnt] = inputstring[k]; cnt++; } } starsfree[cnt] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(aclitemin, CStringGetDatum(starsfree)); pfree(inputstring); break; } case 210: { int strsize; char *smgrstr; strsize = stringInfoGetInt32(serialTup); smgrstr = (char*) palloc(strsize + 1); pq_copymsgbytes(serialTup, smgrstr, strsize); skipPadding(serialTup); smgrstr[strsize] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(smgrin, CStringGetDatum(smgrstr)); break; } default: fHandled = false; } if (fHandled) continue; attr_size = stringInfoGetInt32(serialTup); /* reset attr_data to empty, and load raw data into it */ attr_data.len = 0; attr_data.data[0] = '\0'; attr_data.cursor = 0; appendBinaryStringInfo(&attr_data, pq_getmsgbytes(serialTup, attr_size), attr_size); skipPadding(serialTup); /* Call the attribute type's binary input converter. */ if (attrInfo->recv_finfo.fn_nargs == 1) pSerInfo->values[i] = FunctionCall1(&attrInfo->recv_finfo, PointerGetDatum(&attr_data)); else if (attrInfo->recv_finfo.fn_nargs == 2) pSerInfo->values[i] = FunctionCall2(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param)); else if (attrInfo->recv_finfo.fn_nargs == 3) pSerInfo->values[i] = FunctionCall3(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param), Int32GetDatum(tupdesc->attrs[i]->atttypmod) ); else { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("Conversion function takes %d args",attrInfo->recv_finfo.fn_nargs))); } /* Trouble if it didn't eat the whole buffer */ if (attr_data.cursor != attr_data.len) { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format"))); } } /* * Construct the tuple from the Datums and nulls values. NOTE: Switch * out of our temporary context before we form the tuple! */ MemoryContextSwitchTo(oldCtxt); htup = heap_form_tuple(tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextReset(s_tupSerMemCtxt); /* All done. Return the result. */ return htup; }
/* * Convert a HeapTuple into a byte-sequence, and store it directly * into a chunklist for transmission. * * This code is based on the printtup_internal_20() function in printtup.c. */ void SerializeTupleIntoChunks(HeapTuple tuple, SerTupInfo * pSerInfo, TupleChunkList tcList) { TupleChunkListItem tcItem = NULL; MemoryContext oldCtxt; TupleDesc tupdesc; int i, natts; bool fHandled; AssertArg(tcList != NULL); AssertArg(tuple != NULL); AssertArg(pSerInfo != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* get ready to go */ tcList->p_first = NULL; tcList->p_last = NULL; tcList->num_chunks = 0; tcList->serialized_data_length = 0; tcList->max_chunk_length = Gp_max_tuple_chunk_size; if (natts == 0) { tcItem = getChunkFromCache(&pSerInfo->chunkCache); if (tcItem == NULL) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Could not allocate space for first chunk item in new chunk list."))); } /* TC_EMTPY is just one chunk */ SetChunkType(tcItem->chunk_data, TC_EMPTY); tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE; appendChunkToTCList(tcList, tcItem); return; } tcItem = getChunkFromCache(&pSerInfo->chunkCache); if (tcItem == NULL) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Could not allocate space for first chunk item in new chunk list."))); } /* assume that we'll take a single chunk */ SetChunkType(tcItem->chunk_data, TC_WHOLE); tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE; appendChunkToTCList(tcList, tcItem); AssertState(s_tupSerMemCtxt != NULL); if (is_heaptuple_memtuple(tuple)) { addByteStringToChunkList(tcList, (char *)tuple, memtuple_get_size((MemTuple)tuple, NULL), &pSerInfo->chunkCache); addPadding(tcList, &pSerInfo->chunkCache, memtuple_get_size((MemTuple)tuple, NULL)); } else { TupSerHeader tsh; unsigned int datalen; unsigned int nullslen; HeapTupleHeader t_data = tuple->t_data; datalen = tuple->t_len - t_data->t_hoff; if (HeapTupleHasNulls(tuple)) nullslen = BITMAPLEN(HeapTupleHeaderGetNatts(t_data)); else nullslen = 0; tsh.tuplen = sizeof(TupSerHeader) + TYPEALIGN(TUPLE_CHUNK_ALIGN,nullslen) + datalen; tsh.natts = HeapTupleHeaderGetNatts(t_data); tsh.infomask = t_data->t_infomask; addByteStringToChunkList(tcList, (char *)&tsh, sizeof(TupSerHeader), &pSerInfo->chunkCache); /* If we don't have any attributes which have been toasted, we * can be very very simple: just send the raw data. */ if ((tsh.infomask & HEAP_HASEXTERNAL) == 0) { if (nullslen) { addByteStringToChunkList(tcList, (char *)t_data->t_bits, nullslen, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,nullslen); } addByteStringToChunkList(tcList, (char *)t_data + t_data->t_hoff, datalen, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,datalen); } else { /* We have to be more careful when we have tuples that * have been toasted. Ideally we'd like to send the * untoasted attributes in as "raw" a format as possible * but that makes rebuilding the tuple harder . */ oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* deconstruct the tuple (faster than a heap_getattr loop) */ heap_deform_tuple(tuple, tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextSwitchTo(oldCtxt); /* Send the nulls character-array. */ addByteStringToChunkList(tcList, pSerInfo->nulls, natts, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,natts); /* * send the attributes of this tuple: NOTE anything which allocates * temporary space (e.g. could result in a PG_DETOAST_DATUM) should be * executed with the memory context set to s_tupSerMemCtxt */ for (i = 0; i < natts; ++i) { SerAttrInfo *attrInfo = pSerInfo->myinfo + i; Datum origattr = pSerInfo->values[i], attr; bytea *outputbytes=0; /* skip null attributes (already taken care of above) */ if (pSerInfo->nulls[i]) continue; /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage: we want to force the detoast allocation(s) to * happen in our reset-able serialization context. */ if (attrInfo->typisvarlena) { oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* we want to detoast but leave compressed, if * possible, but we have to handle varlena * attributes (and others ?) differently than we * currently do (first step is to use * heap_tuple_fetch_attr() instead of * PG_DETOAST_DATUM()). */ attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); MemoryContextSwitchTo(oldCtxt); } else attr = origattr; /* * Assume that the data's output will be handled by the special IO * code, and if not then we can handle it the slow way. */ fHandled = true; switch (attrInfo->atttypid) { case INT4OID: addInt32ToChunkList(tcList, DatumGetInt32(attr), &pSerInfo->chunkCache); break; case CHAROID: addCharToChunkList(tcList, DatumGetChar(attr), &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,1); break; case BPCHAROID: case VARCHAROID: case INT2VECTOROID: /* postgres serialization logic broken, use our own */ case OIDVECTOROID: /* postgres serialization logic broken, use our own */ case ANYARRAYOID: { text *pText = DatumGetTextP(attr); int32 textSize = VARSIZE(pText) - VARHDRSZ; addInt32ToChunkList(tcList, textSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, (char *) VARDATA(pText), textSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,textSize); break; } case DATEOID: { DateADT date = DatumGetDateADT(attr); addByteStringToChunkList(tcList, (char *) &date, sizeof(DateADT), &pSerInfo->chunkCache); break; } case NUMERICOID: { /* * Treat the numeric as a varlena variable, and just push * the whole shebang to the output-buffer. We don't care * about the guts of the numeric. */ Numeric num = DatumGetNumeric(attr); int32 numSize = VARSIZE(num) - VARHDRSZ; addInt32ToChunkList(tcList, numSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, (char *) VARDATA(num), numSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,numSize); break; } case ACLITEMOID: { AclItem *aip = DatumGetAclItemP(attr); char *outputstring; int32 aclSize ; outputstring = DatumGetCString(DirectFunctionCall1(aclitemout, PointerGetDatum(aip))); aclSize = strlen(outputstring); addInt32ToChunkList(tcList, aclSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, outputstring,aclSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,aclSize); break; } case 210: /* storage manager */ { char *smgrstr; int32 strsize; smgrstr = DatumGetCString(DirectFunctionCall1(smgrout, 0)); strsize = strlen(smgrstr); addInt32ToChunkList(tcList, strsize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, smgrstr, strsize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,strsize); break; } default: fHandled = false; } if (fHandled) continue; /* * the FunctionCall2 call into the send function may result in some * allocations which we'd like to have contained by our reset-able * context */ oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Call the attribute type's binary input converter. */ if (attrInfo->send_finfo.fn_nargs == 1) outputbytes = DatumGetByteaP(FunctionCall1(&attrInfo->send_finfo, attr)); else if (attrInfo->send_finfo.fn_nargs == 2) outputbytes = DatumGetByteaP(FunctionCall2(&attrInfo->send_finfo, attr, ObjectIdGetDatum(attrInfo->send_typio_param))); else if (attrInfo->send_finfo.fn_nargs == 3) outputbytes = DatumGetByteaP(FunctionCall3(&attrInfo->send_finfo, attr, ObjectIdGetDatum(attrInfo->send_typio_param), Int32GetDatum(tupdesc->attrs[i]->atttypmod))); else { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("Conversion function takes %d args",attrInfo->recv_finfo.fn_nargs))); } MemoryContextSwitchTo(oldCtxt); /* We assume the result will not have been toasted */ addInt32ToChunkList(tcList, VARSIZE(outputbytes) - VARHDRSZ, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,VARSIZE(outputbytes) - VARHDRSZ); /* * this was allocated in our reset-able context, but we *are* done * with it; and for tuples with several large columns it'd be nice to * free the memory back to the context */ pfree(outputbytes); } MemoryContextReset(s_tupSerMemCtxt); } } /* * if we have more than 1 chunk we have to set the chunk types on our * first chunk and last chunk */ if (tcList->num_chunks > 1) { TupleChunkListItem first, last; first = tcList->p_first; last = tcList->p_last; Assert(first != NULL); Assert(first != last); Assert(last != NULL); SetChunkType(first->chunk_data, TC_PARTIAL_START); SetChunkType(last->chunk_data, TC_PARTIAL_END); /* * any intervening chunks are already set to TC_PARTIAL_MID when * allocated */ } return; }
void hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen) { int type, lenlemm; char *lemm = NULL; LexizeData ldata; TSLexeme *norms; ParsedLex *lexs; TSConfigCacheEntry *cfg; TSParserCacheEntry *prsobj; void *prsdata; cfg = lookup_ts_config_cache(cfgId); prsobj = lookup_ts_parser_cache(cfg->prsId); prsdata = (void *) DatumGetPointer(FunctionCall2(&(prsobj->prsstart), PointerGetDatum(buf), Int32GetDatum(buflen))); LexizeInit(&ldata, cfg); do { type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken), PointerGetDatum(prsdata), PointerGetDatum(&lemm), PointerGetDatum(&lenlemm))); if (type > 0 && lenlemm >= MAXSTRLEN) { #ifdef IGNORE_LONGLEXEME ereport(NOTICE, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("word is too long to be indexed"), errdetail("Words longer than %d characters are ignored.", MAXSTRLEN))); continue; #else ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("word is too long to be indexed"), errdetail("Words longer than %d characters are ignored.", MAXSTRLEN))); #endif } LexizeAddLemm(&ldata, type, lemm, lenlemm); do { if ((norms = LexizeExec(&ldata, &lexs)) != NULL) addHLParsedLex(prs, query, lexs, norms); else addHLParsedLex(prs, query, lexs, NULL); } while (norms); } while (type > 0); FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata)); }
Datum serialize_record( PG_FUNCTION_ARGS ) { // FILE* log; // log = fopen("/var/lib/postgresql/serializer.log", "a"); HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); HeapTupleData tuple; bool needComma = false; int i; Datum *values; bool *nulls; StringInfoData buf; char *conversion_buf; /* Extract type info from the tuple itself */ Oid tupType = HeapTupleHeaderGetTypeId(rec); int32 tupTypmod = HeapTupleHeaderGetTypMod(rec); TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); int 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; // fprintf(log, "Doing serialize_record\n"); // fflush(log); 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++) { Oid column_type = tupdesc->attrs[ i ]->atttypid; char *value; char *column_name; char type_category; HeapTuple type_tuple; FmgrInfo flinfo; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; if (nulls[i]) { /* emit nothing... */ continue; } if (needComma) appendStringInfoChar(&buf, ','); needComma = true; /* obtain column name */ column_name = SPI_fname( tupdesc, i + 1 ); /* obtain type information from pg_catalog */ type_tuple = SearchSysCache1( TYPEOID, ObjectIdGetDatum(column_type) ); if (!HeapTupleIsValid( type_tuple )) elog(ERROR, "cache lookup failed for relation %u", column_type); type_category = ((Form_pg_type) GETSTRUCT( type_tuple ))->typcategory; ReleaseSysCache( type_tuple ); /* append column name */ appendStringInfoChar(&buf, '"'); appendStringInfoString(&buf, column_name); appendStringInfoString(&buf, "\":"); switch( type_category ) { // http://www.postgresql.org/docs/current/static/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE case 'A': //array //call to serialize_array( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_array; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, values[ i ] ) ); appendStringInfoString(&buf, value); break; case 'C': //composite //recursive call to serialize_record( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_record; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, values[ i ] ) ); appendStringInfoString(&buf, value); break; case 'N': //numeric conversion_buf = NULL; // get column text value // fprintf(log, "Calling ConvertToText\n"); // fflush(log); value = ConvertToText( values[ i ], column_type, fcinfo->flinfo->fn_mcxt, &conversion_buf ); // fprintf(log, "ConvertToText succeded\n"); // fflush(log); appendStringInfoString(&buf, value); // fprintf(log, "append.... succeded\n"); // fflush(log); if(conversion_buf != NULL) { pfree(conversion_buf); conversion_buf = NULL; } break; case 'B': //boolean appendStringInfoString(&buf, // get column boolean value DatumGetBool( values[ i ] ) ? "true" : "false" ); break; default: //another conversion_buf = NULL; // get column text value // fprintf(log, "Calling ConvertToText\n"); // fflush(log); value = ConvertToText( values[ i ], column_type, fcinfo->flinfo->fn_mcxt, &conversion_buf ); // fprintf(log, "ConvertToText succeded\n"); // fflush(log); appendStringInfoQuotedString(&buf, value); // fprintf(log, "append.... succeded\n"); // fflush(log); if(conversion_buf != NULL) { pfree(conversion_buf); conversion_buf = NULL; } } } appendStringInfoChar(&buf, '}'); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); // fclose(log); PG_RETURN_TEXT_P( PG_CSTR_GET_TEXT( buf.data ) ); }
Datum serialize_array(PG_FUNCTION_ARGS) { ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); Oid element_type = ARR_ELEMTYPE(v); int16 typlen; bool typbyval; char typalign; char typdelim; char *p, *value; char type_category; bool needComma = false; bits8 *bitmap; int bitmask; int nitems, i; int ndim, *dims; Oid typioparam, typiofunc; HeapTuple type_tuple; FmgrInfo proc, flinfo; StringInfoData buf; // FILE* log; // log = fopen("/var/lib/postgresql/serializer.log", "a"); // fprintf(log, "Doing serialize_array\n"); // fflush(log); /* * Get info about element type, including its output conversion proc */ get_type_io_data(element_type, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typiofunc); fmgr_info_cxt( typiofunc, &proc, fcinfo->flinfo->fn_mcxt ); ndim = ARR_NDIM(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dims); if( ndim > 1 ) elog( ERROR, "multidimensional arrays doesn't supported" ); p = ARR_DATA_PTR(v); bitmap = ARR_NULLBITMAP(v); bitmask = 1; /* obtain type information from pg_catalog */ type_tuple = SearchSysCache1( TYPEOID, ObjectIdGetDatum(element_type) ); if (!HeapTupleIsValid( type_tuple )) elog(ERROR, "cache lookup failed for relation %u", element_type); type_category = ((Form_pg_type) GETSTRUCT( type_tuple ))->typcategory; ReleaseSysCache( type_tuple ); /* Build the result string */ initStringInfo(&buf); appendStringInfoChar(&buf, '['); for (i = 0; i < nitems; i++) { if (needComma) appendStringInfoChar(&buf, ','); needComma = true; /* Get source element, checking for NULL */ if (bitmap && (*bitmap & bitmask) == 0) { // append null appendStringInfoString(&buf, "null"); } else { /* get item value and advance array data pointer */ Datum itemvalue = fetch_att(p, typbyval, typlen); p = att_addlength_pointer(p, typlen, p); p = (char *) att_align_nominal(p, typalign); //------------------------ switch( type_category ) { // http://www.postgresql.org/docs/current/static/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE case 'A': //array - impossible case break; case 'C': //composite //call to serialize_record( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_record; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, itemvalue ) ); appendStringInfoString(&buf, value); break; case 'N': //numeric // get column text value value = OutputFunctionCall( &proc, itemvalue ); appendStringInfoString(&buf, value); break; default: //another // get column text value value = OutputFunctionCall( &proc, itemvalue ); appendStringInfoQuotedString(&buf, value); } } /* advance bitmap pointer if any */ if (bitmap) { bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } appendStringInfoChar(&buf, ']'); // fclose(log); //PG_RETURN_CSTRING(retval); PG_RETURN_TEXT_P( PG_CSTR_GET_TEXT( buf.data ) ); }