float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd) { float penalty = 0.0; if (giststate->penaltyFn[attno].fn_strict == FALSE || (isNullOrig == FALSE && isNullAdd == FALSE)) { FunctionCall3(&giststate->penaltyFn[attno], PointerGetDatum(orig), PointerGetDatum(add), PointerGetDatum(&penalty)); /* disallow negative or NaN penalty */ if (isnan(penalty) || penalty < 0.0) penalty = 0.0; } else if (isNullOrig && isNullAdd) penalty = 0.0; else penalty = 1e10; /* try to prevent mixing null and non-null * values */ return penalty; }
/* * index_beginscan_internal --- common code for index_beginscan variants */ static IndexScanDesc index_beginscan_internal(Relation indexRelation, int nkeys, int norderbys, Snapshot snapshot) { IndexScanDesc scan; FmgrInfo *procedure; RELATION_CHECKS; GET_REL_PROCEDURE(ambeginscan); if (!(indexRelation->rd_am->ampredlocks)) PredicateLockRelation(indexRelation, snapshot); /* * We hold a reference count to the relcache entry throughout the scan. */ RelationIncrementReferenceCount(indexRelation); /* * Tell the AM to open a scan. */ scan = (IndexScanDesc) DatumGetPointer(FunctionCall3(procedure, PointerGetDatum(indexRelation), Int32GetDatum(nkeys), Int32GetDatum(norderbys))); return scan; }
/* * index_beginscan_internal --- common code for index_beginscan variants */ static IndexScanDesc index_beginscan_internal(Relation indexRelation, int nkeys, ScanKey key) { IndexScanDesc scan; FmgrInfo *procedure; RELATION_CHECKS; GET_REL_PROCEDURE(ambeginscan); /* * We hold a reference count to the relcache entry throughout the scan. */ RelationIncrementReferenceCount(indexRelation); /* * Tell the AM to open a scan. */ scan = (IndexScanDesc) DatumGetPointer(FunctionCall3(procedure, PointerGetDatum(indexRelation), Int32GetDatum(nkeys), PointerGetDatum(key))); return scan; }
Datum lexize(PG_FUNCTION_ARGS) { text *in = PG_GETARG_TEXT_P(1); DictInfo *dict; TSLexeme *res, *ptr; Datum *da; ArrayType *a; SET_FUNCOID(); dict = finddict(PG_GETARG_OID(0)); ptr = res = (TSLexeme *) DatumGetPointer( FunctionCall3(&(dict->lexize_info), PointerGetDatum(dict->dictionary), PointerGetDatum(VARDATA(in)), Int32GetDatum(VARSIZE(in) - VARHDRSZ) ) ); PG_FREE_IF_COPY(in, 1); if (!res) { if (PG_NARGS() > 2) PG_RETURN_POINTER(NULL); else PG_RETURN_NULL(); } while (ptr->lexeme) ptr++; da = (Datum *) palloc(sizeof(Datum) * (ptr - res + 1)); ptr = res; while (ptr->lexeme) { da[ptr - res] = PointerGetDatum(char2text(ptr->lexeme)); ptr++; } a = construct_array( da, ptr - res, TEXTOID, -1, false, 'i' ); ptr = res; while (ptr->lexeme) { pfree(DatumGetPointer(da[ptr - res])); pfree(ptr->lexeme); ptr++; } pfree(res); pfree(da); PG_RETURN_POINTER(a); }
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) ); }
/* Read null-terminated string and convert to internal format */ Datum TupleFormerValue(TupleFormer *former, const char *str, int col) { return FunctionCall3(&former->typInput[col], CStringGetDatum(str), ObjectIdGetDatum(former->typIOParam[col]), Int32GetDatum(former->typMod[col])); }
bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b) { bool result; FunctionCall3(&giststate->equalFn[attno], a, b, PointerGetDatum(&result)); return result; }
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); }
jvalue _String_coerceDatum(Type self, Datum arg) { jvalue result; char* tmp = DatumGetCString(FunctionCall3( &((String)self)->textOutput, arg, ObjectIdGetDatum(((String)self)->elementType), Int32GetDatum(-1))); result.l = String_createJavaStringFromNTS(tmp); pfree(tmp); return result; }
static void gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *key1, bool isNull1, GISTENTRY *key2, bool isNull2, float *penalty) { if (giststate->penaltyFn[attno].fn_strict && (isNull1 || isNull2)) *penalty = 0.0; else FunctionCall3(&giststate->penaltyFn[attno], PointerGetDatum(key1), PointerGetDatum(key2), PointerGetDatum(penalty)); }
Datum ts_headline_byid_opt(PG_FUNCTION_ARGS) { Oid tsconfig = PG_GETARG_OID(0); text *in = PG_GETARG_TEXT_PP(1); TSQuery query = PG_GETARG_TSQUERY(2); text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_PP(3) : NULL; HeadlineParsedText prs; List *prsoptions; text *out; TSConfigCacheEntry *cfg; TSParserCacheEntry *prsobj; cfg = lookup_ts_config_cache(tsconfig); prsobj = lookup_ts_parser_cache(cfg->prsId); if (!OidIsValid(prsobj->headlineOid)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("text search parser does not support headline creation"))); memset(&prs, 0, sizeof(HeadlineParsedText)); prs.lenwords = 32; prs.words = (HeadlineWordEntry *) palloc(sizeof(HeadlineWordEntry) * prs.lenwords); hlparsetext(cfg->cfgId, &prs, query, VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in)); if (opt) prsoptions = deserialize_deflist(PointerGetDatum(opt)); else prsoptions = NIL; FunctionCall3(&(prsobj->prsheadline), PointerGetDatum(&prs), PointerGetDatum(prsoptions), PointerGetDatum(query)); out = generateHeadline(&prs); PG_FREE_IF_COPY(in, 1); PG_FREE_IF_COPY(query, 2); if (opt) PG_FREE_IF_COPY(opt, 3); pfree(prs.words); pfree(prs.startsel); pfree(prs.stopsel); PG_RETURN_POINTER(out); }
void newScanKey(IndexScanDesc scan) { ScanKey scankey = scan->keyData; GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; uint32 nkeys = 0; so->keys = (GinScanKey) palloc(scan->numberOfKeys * sizeof(GinScanKeyData)); if (scan->numberOfKeys < 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("GIN indexes do not support whole-index scans"))); for (i = 0; i < scan->numberOfKeys; i++) { Datum *entryValues; uint32 nEntryValues; if (scankey[i].sk_flags & SK_ISNULL) elog(ERROR, "Gin doesn't support NULL as scan key"); Assert(scankey[i].sk_attno == 1); entryValues = (Datum *) DatumGetPointer( FunctionCall3( &so->ginstate.extractQueryFn, scankey[i].sk_argument, PointerGetDatum(&nEntryValues), UInt16GetDatum(scankey[i].sk_strategy) ) ); if (entryValues == NULL || nEntryValues == 0) /* full scan... */ continue; fillScanKey(&so->ginstate, &(so->keys[nkeys]), scankey[i].sk_argument, entryValues, nEntryValues, scankey[i].sk_strategy); nkeys++; } so->nkeys = nkeys; if (so->nkeys == 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("GIN index does not support search with void query"))); pgstat_count_index_scan(&scan->xs_pgstat_info); }
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd) { float penalty = 0.0; if (giststate->penaltyFn[attno].fn_strict == FALSE || (isNullOrig == FALSE && isNullAdd == FALSE)) FunctionCall3(&giststate->penaltyFn[attno], PointerGetDatum(orig), PointerGetDatum(add), PointerGetDatum(&penalty)); else if (isNullOrig && isNullAdd) penalty = 0.0; else penalty = 1e10; /* try to prevent to mix null and non-null * value */ return penalty; }
/* * Return headline in text from, generated from a json(b) element */ static text * headline_json_value(void *_state, char *elem_value, int elem_len) { HeadlineJsonState *state = (HeadlineJsonState *) _state; HeadlineParsedText *prs = state->prs; TSConfigCacheEntry *cfg = state->cfg; TSParserCacheEntry *prsobj = state->prsobj; TSQuery query = state->query; List *prsoptions = state->prsoptions; prs->curwords = 0; hlparsetext(cfg->cfgId, prs, query, elem_value, elem_len); FunctionCall3(&(prsobj->prsheadline), PointerGetDatum(prs), PointerGetDatum(prsoptions), PointerGetDatum(query)); state->transformed = true; return generateHeadline(prs); }
Datum headline(PG_FUNCTION_ARGS) { text *in = PG_GETARG_TEXT_P(1); QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(2))); text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL; HLPRSTEXT prs; text *out; TSCfgInfo *cfg; WParserInfo *prsobj; SET_FUNCOID(); cfg = findcfg(PG_GETARG_OID(0)); prsobj = findprs(cfg->prs_id); memset(&prs, 0, sizeof(HLPRSTEXT)); prs.lenwords = 32; prs.words = (HLWORD *) palloc(sizeof(HLWORD) * prs.lenwords); hlparsetext(cfg, &prs, query, VARDATA(in), VARSIZE(in) - VARHDRSZ); FunctionCall3( &(prsobj->headline_info), PointerGetDatum(&prs), PointerGetDatum(opt), PointerGetDatum(query) ); out = genhl(&prs); PG_FREE_IF_COPY(in, 1); PG_FREE_IF_COPY(query, 2); if (opt) PG_FREE_IF_COPY(opt, 3); pfree(prs.words); pfree(prs.startsel); pfree(prs.stopsel); PG_RETURN_POINTER(out); }
Datum _String_coerceObject(Type self, jobject jstr) { char* tmp; Datum ret; if(jstr == 0) return 0; jstr = JNI_callObjectMethod(jstr, s_Object_toString); if(JNI_exceptionCheck()) return 0; tmp = String_createNTS(jstr); JNI_deleteLocalRef(jstr); ret = FunctionCall3( &((String)self)->textInput, CStringGetDatum(tmp), ObjectIdGetDatum(((String)self) -> Type_extension.typeId /* elementType */ ), Int32GetDatum(-1)); pfree(tmp); return ret; }
/* * Forms union of oldtup and addtup, if union == oldtup then return NULL */ static IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) { GistEntryVector *evec; Datum datum; int datumsize; bool result, neednew = false; char isnull[INDEX_MAX_KEYS], whatfree[INDEX_MAX_KEYS]; Datum attr[INDEX_MAX_KEYS]; GISTENTRY centry[INDEX_MAX_KEYS], oldatt[INDEX_MAX_KEYS], addatt[INDEX_MAX_KEYS], *ev0p, *ev1p; bool olddec[INDEX_MAX_KEYS], adddec[INDEX_MAX_KEYS]; bool oldisnull[INDEX_MAX_KEYS], addisnull[INDEX_MAX_KEYS]; IndexTuple newtup = NULL; int j; evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ); evec->n = 2; ev0p = &(evec->vector[0]); ev1p = &(evec->vector[1]); gistDeCompressAtt(giststate, r, oldtup, NULL, (OffsetNumber) 0, oldatt, olddec, oldisnull); gistDeCompressAtt(giststate, r, addtup, NULL, (OffsetNumber) 0, addatt, adddec, addisnull); for (j = 0; j < r->rd_att->natts; j++) { if (oldisnull[j] && addisnull[j]) { isnull[j] = 'n'; attr[j] = (Datum) 0; whatfree[j] = FALSE; } else { FILLEV( oldisnull[j], oldatt[j].key, oldatt[j].bytes, addisnull[j], addatt[j].key, addatt[j].bytes ); datum = FunctionCall2(&giststate->unionFn[j], PointerGetDatum(evec), PointerGetDatum(&datumsize)); if (oldisnull[j] || addisnull[j]) { if (oldisnull[j]) neednew = true; } else { FunctionCall3(&giststate->equalFn[j], ev0p->key, datum, PointerGetDatum(&result)); if (!result) neednew = true; } if (olddec[j]) pfree(DatumGetPointer(oldatt[j].key)); if (adddec[j]) pfree(DatumGetPointer(addatt[j].key)); gistcentryinit(giststate, j, ¢ry[j], datum, NULL, NULL, (OffsetNumber) 0, datumsize, FALSE, FALSE); isnull[j] = ' '; attr[j] = centry[j].key; if ((!isAttByVal(giststate, j))) { whatfree[j] = TRUE; if (centry[j].key != datum) pfree(DatumGetPointer(datum)); } else whatfree[j] = FALSE; } } pfree(evec); if (neednew) { /* need to update key */ newtup = (IndexTuple) index_formtuple(giststate->tupdesc, attr, isnull); newtup->t_tid = oldtup->t_tid; } for (j = 0; j < r->rd_att->natts; j++) if (whatfree[j]) pfree(DatumGetPointer(attr[j])); return newtup; }
static void prs_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, int prsid, text *txt) { TupleDesc tupdesc; MemoryContext oldcontext; PrsStorage *st; WParserInfo *prs = findprs(prsid); char *lex = NULL; int llen = 0, type = 0; 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); prs->prs = (void *) DatumGetPointer( FunctionCall2( &(prs->start_info), PointerGetDatum(VARDATA(txt)), Int32GetDatum(VARSIZE(txt) - VARHDRSZ) ) ); while ((type = DatumGetInt32(FunctionCall3( &(prs->getlexeme_info), PointerGetDatum(prs->prs), 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->end_info), PointerGetDatum(prs->prs) ); st->len = st->cur; st->cur = 0; funcctx->user_fctx = (void *) st; if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupdesc = CreateTupleDescCopy(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
/* * 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; }
/* * Sets key->curItem to new found heap item pointer for one scan key * returns isFinished! */ static bool keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey key) { uint32 i; GinScanEntry entry; bool res; MemoryContext oldCtx; if (key->isFinished) return TRUE; do { /* * move forward from previously value and set new curItem, which is * minimal from entries->curItems */ ItemPointerSetMax(&key->curItem); for (i = 0; i < key->nentries; i++) { entry = key->scanEntry + i; if (key->entryRes[i]) { if (entry->isFinished == FALSE && entryGetItem(index, entry) == FALSE) { if (compareItemPointers(&entry->curItem, &key->curItem) < 0) key->curItem = entry->curItem; } else key->entryRes[i] = FALSE; } else if (entry->isFinished == FALSE) { if (compareItemPointers(&entry->curItem, &key->curItem) < 0) key->curItem = entry->curItem; } } if (ItemPointerIsMax(&key->curItem)) { /* all entries are finished */ key->isFinished = TRUE; return TRUE; } if (key->nentries == 1) { /* we can do not call consistentFn !! */ key->entryRes[0] = TRUE; return FALSE; } /* setting up array for consistentFn */ for (i = 0; i < key->nentries; i++) { entry = key->scanEntry + i; if (entry->isFinished == FALSE && compareItemPointers(&entry->curItem, &key->curItem) == 0) key->entryRes[i] = TRUE; else key->entryRes[i] = FALSE; } oldCtx = MemoryContextSwitchTo(tempCtx); res = DatumGetBool(FunctionCall3( &ginstate->consistentFn, PointerGetDatum(key->entryRes), UInt16GetDatum(key->strategy), key->query )); MemoryContextSwitchTo(oldCtx); MemoryContextReset(tempCtx); } while (!res); return FALSE; }
/* * Extract the index key values from an indexable item * * The resulting key values are sorted, and any duplicates are removed. * This avoids generating redundant index entries. */ Datum * ginExtractEntries(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories) { Datum *entries; bool *nullFlags; int32 i; /* * We don't call the extractValueFn on a null item. Instead generate a * placeholder. */ if (isNull) { *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); entries[0] = (Datum) 0; *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory)); (*categories)[0] = GIN_CAT_NULL_ITEM; return entries; } /* OK, call the opclass's extractValueFn */ nullFlags = NULL; /* in case extractValue doesn't set it */ entries = (Datum *) DatumGetPointer(FunctionCall3(&ginstate->extractValueFn[attnum - 1], value, PointerGetDatum(nentries), PointerGetDatum(&nullFlags))); /* * Generate a placeholder if the item contained no keys. */ if (entries == NULL || *nentries <= 0) { *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); entries[0] = (Datum) 0; *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory)); (*categories)[0] = GIN_CAT_EMPTY_ITEM; return entries; } /* * If the extractValueFn didn't create a nullFlags array, create one, * assuming that everything's non-null. Otherwise, run through the * array and make sure each value is exactly 0 or 1; this ensures * binary compatibility with the GinNullCategory representation. */ if (nullFlags == NULL) nullFlags = (bool *) palloc0(*nentries * sizeof(bool)); else { for (i = 0; i < *nentries; i++) nullFlags[i] = (nullFlags[i] ? true : false); } /* now we can use the nullFlags as category codes */ *categories = (GinNullCategory *) nullFlags; /* * If there's more than one key, sort and unique-ify. * * XXX Using qsort here is notationally painful, and the overhead is * pretty bad too. For small numbers of keys it'd likely be better to * use a simple insertion sort. */ if (*nentries > 1) { keyEntryData *keydata; cmpEntriesArg arg; keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData)); for (i = 0; i < *nentries; i++) { keydata[i].datum = entries[i]; keydata[i].isnull = nullFlags[i]; } arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1]; arg.haveDups = false; qsort_arg(keydata, *nentries, sizeof(keyEntryData), cmpEntries, (void *) &arg); if (arg.haveDups) { /* there are duplicates, must get rid of 'em */ int32 j; entries[0] = keydata[0].datum; nullFlags[0] = keydata[0].isnull; j = 1; for (i = 1; i < *nentries; i++) { if (cmpEntries(&keydata[i-1], &keydata[i], &arg) != 0) { entries[j] = keydata[i].datum; nullFlags[j] = keydata[i].isnull; j++; } } *nentries = j; } else { /* easy, no duplicates */ for (i = 0; i < *nentries; i++) { entries[i] = keydata[i].datum; nullFlags[i] = keydata[i].isnull; } } pfree(keydata); } return entries; }
/* * 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)); }
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)); }
void parsetext_v2(TSCfgInfo * cfg, PRSTEXT * prs, char *buf, int4 buflen) { int type, lenlemm; char *lemm = NULL; WParserInfo *prsobj = findprs(cfg->prs_id); LexizeData ldata; TSLexeme *norms; 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); 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 = (TSWORD *) repalloc((void *) prs->words, prs->lenwords * sizeof(TSWORD)); } 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].alen = 0; prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos); ptr++; prs->curwords++; } pfree(norms); } } while (type > 0); FunctionCall1( &(prsobj->end_info), PointerGetDatum(prsobj->prs) ); }
static HeapTuple plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup) { SV **svp; HV *hvNew; HeapTuple rtup; SV *val; char *key; I32 klen; int slotsused; int *modattrs; Datum *modvalues; char *modnulls; TupleDesc tupdesc; tupdesc = tdata->tg_relation->rd_att; svp = hv_fetch(hvTD, "new", 3, FALSE); if (!svp) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("$_TD->{new} does not exist"))); if (!SvOK(*svp) || SvTYPE(*svp) != SVt_RV || SvTYPE(SvRV(*svp)) != SVt_PVHV) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("$_TD->{new} is not a hash reference"))); hvNew = (HV *) SvRV(*svp); modattrs = palloc(tupdesc->natts * sizeof(int)); modvalues = palloc(tupdesc->natts * sizeof(Datum)); modnulls = palloc(tupdesc->natts * sizeof(char)); slotsused = 0; hv_iterinit(hvNew); while ((val = hv_iternextsv(hvNew, &key, &klen))) { int attn = SPI_fnumber(tupdesc, key); if (attn <= 0 || tupdesc->attrs[attn - 1]->attisdropped) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("Perl hash contains nonexistent column \"%s\"", key))); if (SvOK(val) && SvTYPE(val) != SVt_NULL) { Oid typinput; Oid typioparam; FmgrInfo finfo; /* XXX would be better to cache these lookups */ getTypeInputInfo(tupdesc->attrs[attn - 1]->atttypid, &typinput, &typioparam); fmgr_info(typinput, &finfo); modvalues[slotsused] = FunctionCall3(&finfo, CStringGetDatum(SvPV(val, PL_na)), ObjectIdGetDatum(typioparam), Int32GetDatum(tupdesc->attrs[attn - 1]->atttypmod)); modnulls[slotsused] = ' '; } else { modvalues[slotsused] = (Datum) 0; modnulls[slotsused] = 'n'; } modattrs[slotsused] = attn; slotsused++; } hv_iterinit(hvNew); rtup = SPI_modifytuple(tdata->tg_relation, otup, slotsused, modattrs, modvalues, modnulls); pfree(modattrs); pfree(modvalues); pfree(modnulls); if (rtup == NULL) elog(ERROR, "SPI_modifytuple failed: %s", SPI_result_code_string(SPI_result)); return rtup; }
/* * Note: plperl_return_next is called both in Postgres and Perl contexts. * We report any errors in Postgres fashion (via ereport). If called in * Perl context, it is SPI.xs's responsibility to catch the error and * convert to a Perl error. We assume (perhaps without adequate justification) * that we need not abort the current transaction if the Perl code traps the * error. */ void plperl_return_next(SV *sv) { plperl_proc_desc *prodesc = plperl_current_prodesc; FunctionCallInfo fcinfo = plperl_current_caller_info; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext cxt; HeapTuple tuple; TupleDesc tupdesc; if (!sv) return; if (!prodesc->fn_retisset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use return_next in a non-SETOF function"))); if (prodesc->fn_retistuple && !(SvOK(sv) && SvTYPE(sv) == SVt_RV && SvTYPE(SvRV(sv)) == SVt_PVHV)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("setof-composite-returning Perl function " "must call return_next with reference to hash"))); cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); if (!plperl_current_tuple_store) plperl_current_tuple_store = tuplestore_begin_heap(true, false, work_mem); if (prodesc->fn_retistuple) { TypeFuncClass rettype; AttInMetadata *attinmeta; rettype = get_call_result_type(fcinfo, NULL, &tupdesc); tupdesc = CreateTupleDescCopy(tupdesc); attinmeta = TupleDescGetAttInMetadata(tupdesc); tuple = plperl_build_tuple_result((HV *) SvRV(sv), attinmeta); } else { Datum ret; bool isNull; tupdesc = CreateTupleDescCopy(rsi->expectedDesc); if (SvOK(sv) && SvTYPE(sv) != SVt_NULL) { char *val = SvPV(sv, PL_na); ret = FunctionCall3(&prodesc->result_in_func, PointerGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), Int32GetDatum(-1)); isNull = false; } else { ret = (Datum) 0; isNull = true; } tuple = heap_form_tuple(tupdesc, &ret, &isNull); } if (!plperl_current_tuple_desc) plperl_current_tuple_desc = tupdesc; tuplestore_puttuple(plperl_current_tuple_store, tuple); heap_freetuple(tuple); MemoryContextSwitchTo(cxt); }
/* * find group in vector with equial value */ static int gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) { int i, j, len; int curid = 1; bool result; /* * first key is always not null (see gistinsert), so we may not check * for nulls */ for (i = 0; i < spl->spl_nleft; i++) { if (spl->spl_idgrp[spl->spl_left[i]]) continue; len = 0; /* find all equal value in right part */ for (j = 0; j < spl->spl_nright; j++) { if (spl->spl_idgrp[spl->spl_right[j]]) continue; FunctionCall3(&giststate->equalFn[0], valvec[spl->spl_left[i]].key, valvec[spl->spl_right[j]].key, PointerGetDatum(&result)); if (result) { spl->spl_idgrp[spl->spl_right[j]] = curid; len++; } } /* find all other equal value in left part */ if (len) { /* add current val to list of equial values */ spl->spl_idgrp[spl->spl_left[i]] = curid; /* searching .. */ for (j = i + 1; j < spl->spl_nleft; j++) { if (spl->spl_idgrp[spl->spl_left[j]]) continue; FunctionCall3(&giststate->equalFn[0], valvec[spl->spl_left[i]].key, valvec[spl->spl_left[j]].key, PointerGetDatum(&result)); if (result) { spl->spl_idgrp[spl->spl_left[j]] = curid; len++; } } spl->spl_ngrp[curid] = len + 1; curid++; } } return curid; }
static Datum plperl_func_handler(PG_FUNCTION_ARGS) { plperl_proc_desc *prodesc; SV *perlret; Datum retval; ReturnSetInfo *rsi; SV *array_ret = NULL; if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false); plperl_current_prodesc = prodesc; plperl_current_caller_info = fcinfo; plperl_current_tuple_store = 0; plperl_current_tuple_desc = 0; rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (prodesc->fn_retisset) { /* Check context before allowing the call to go through */ if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that " "cannot accept a set"))); } perlret = plperl_call_perl_func(prodesc, fcinfo); /************************************************************ * Disconnect from SPI manager and then create the return * values datum (if the input function does a palloc for it * this must not be allocated in the SPI memory context * because SPI_finish would free it). ************************************************************/ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish() failed"); if (prodesc->fn_retisset) { /* * If the Perl function returned an arrayref, we pretend that it * called return_next() for each element of the array, to handle old * SRFs that didn't know about return_next(). Any other sort of return * value is an error. */ if (SvTYPE(perlret) == SVt_RV && SvTYPE(SvRV(perlret)) == SVt_PVAV) { int i = 0; SV **svp = 0; AV *rav = (AV *) SvRV(perlret); while ((svp = av_fetch(rav, i, FALSE)) != NULL) { plperl_return_next(*svp); i++; } } else if (SvTYPE(perlret) != SVt_NULL) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("set-returning Perl function must return " "reference to array or use return_next"))); } rsi->returnMode = SFRM_Materialize; if (plperl_current_tuple_store) { rsi->setResult = plperl_current_tuple_store; rsi->setDesc = plperl_current_tuple_desc; } retval = (Datum) 0; } else if (SvTYPE(perlret) == SVt_NULL) { /* Return NULL if Perl code returned undef */ if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = ExprEndResult; fcinfo->isnull = true; retval = (Datum) 0; } else if (prodesc->fn_retistuple) { /* Return a perl hash converted to a Datum */ TupleDesc td; AttInMetadata *attinmeta; HeapTuple tup; if (!SvOK(perlret) || SvTYPE(perlret) != SVt_RV || SvTYPE(SvRV(perlret)) != SVt_PVHV) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("composite-returning Perl function " "must return reference to hash"))); } /* XXX should cache the attinmeta data instead of recomputing */ if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } attinmeta = TupleDescGetAttInMetadata(td); tup = plperl_build_tuple_result((HV *) SvRV(perlret), attinmeta); retval = HeapTupleGetDatum(tup); } else { /* Return a perl string converted to a Datum */ char *val; if (prodesc->fn_retisarray && SvROK(perlret) && SvTYPE(SvRV(perlret)) == SVt_PVAV) { array_ret = plperl_convert_to_pg_array(perlret); SvREFCNT_dec(perlret); perlret = array_ret; } val = SvPV(perlret, PL_na); retval = FunctionCall3(&prodesc->result_in_func, CStringGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), Int32GetDatum(-1)); } if (array_ret == NULL) SvREFCNT_dec(perlret); return retval; }
static text* plvsubst_string(text *template_in, ArrayType *vals_in, text *c_subst, FunctionCallInfo fcinfo) { ArrayType *v = vals_in; int nitems, *dims, ndims; char *p; int16 typlen; bool typbyval; char typalign; char typdelim; Oid typelem; Oid typiofunc; FmgrInfo proc; int i = 0, items = 0; StringInfo sinfo; const char *template_str; int template_len; char *sizes; int *positions; int subst_mb_len; int subst_len; const bits8 *bitmap; int bitmask; if (v != NULL && (ndims = ARR_NDIM(v)) > 0) { if (ndims != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid parameter"), errdetail("Array of arguments has wrong dimension: %d", ndims))); p = ARR_DATA_PTR(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndims, dims); bitmap = ARR_NULLBITMAP(v); get_type_io_data(ARR_ELEMTYPE(v), IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typelem, &typiofunc); fmgr_info_cxt(typiofunc, &proc, fcinfo->flinfo->fn_mcxt); } else { nitems = 0; p = NULL; bitmap = NULL; } template_str = VARDATA(template_in); template_len = ora_mb_strlen(template_in, &sizes, &positions); subst_mb_len = ora_mb_strlen1(c_subst); subst_len = VARSIZE_ANY_EXHDR(c_subst); sinfo = makeStringInfo(); bitmask = 1; for (i = 0; i < template_len; i++) { if (strncmp(&template_str[positions[i]], VARDATA(c_subst), subst_len) == 0) { Datum itemvalue; char *value; if (items++ < nitems) { if (bitmap && (*bitmap & bitmask) == 0) value = pstrdup("NULL"); else { itemvalue = fetch_att(p, typbyval, typlen); value = DatumGetCString(FunctionCall3(&proc, itemvalue, ObjectIdGetDatum(typelem), Int32GetDatum(-1))); p = att_addlength_pointer(p, typlen, p); p = (char *) att_align_nominal(p, typalign); } appendStringInfoString(sinfo, value); pfree(value); if (bitmap) { bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too few parameters specified for template string"))); i += subst_mb_len - 1; } else appendBinaryStringInfo(sinfo, &template_str[positions[i]], sizes[i]); } return cstring_to_text(sinfo->data); }