static void formatTuple(StringInfo buf, HeapTuple tup, TupleDesc tupdesc, Oid *outputFunArray) { int i; for (i = 0; i < tupdesc->natts; i++) { bool isnull; Datum d = heap_getattr(tup, i+1, tupdesc, &isnull); if (d && !isnull) { Datum ds = OidFunctionCall1(outputFunArray[i], d); char *s = DatumGetCString(ds); char *name = NameStr(tupdesc->attrs[i]->attname); if (name && *name) appendStringInfo(buf, " %s=\"%.30s\"", name, s); else appendStringInfo(buf, " \"%.30s\"", s); pfree(s); } } appendStringInfoChar(buf, '\n'); }
/* ---------------- * index_endscan - end a scan * ---------------- */ void index_endscan(IndexScanDesc scan) { RegProcedure procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(endscan, 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 */ OidFunctionCall1(procedure, PointerGetDatum(scan)); /* Release index lock and refcount acquired by index_beginscan */ UnlockRelation(scan->indexRelation, AccessShareLock); RelationDecrementReferenceCount(scan->indexRelation); /* Release the scan data structure itself */ IndexScanEnd(scan); }
/* * Look up and call sortsupport function to setup SortSupport comparator; * or if no such function exists or it declines to set up the appropriate * state, prepare a suitable shim. */ static void FinishSortSupportFunction(Oid opfamily, Oid opcintype, SortSupport ssup) { Oid sortSupportFunction; /* Look for a sort support function */ sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype, BTSORTSUPPORT_PROC); if (OidIsValid(sortSupportFunction)) { /* * The sort support function can provide a comparator, but it can also * choose not to so (e.g. based on the selected collation). */ OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup)); } if (ssup->comparator == NULL) { Oid sortFunction; sortFunction = get_opfamily_proc(opfamily, opcintype, opcintype, BTORDER_PROC); if (!OidIsValid(sortFunction)) elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", BTORDER_PROC, opcintype, opcintype, opfamily); /* We'll use a shim to call the old-style btree comparator */ PrepareSortSupportComparisonShim(sortFunction, ssup); } }
static void tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) { TupleDesc tupdesc; MemoryContext oldcontext; TSTokenTypeStorage *st; TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid); if (!OidIsValid(prs->lextypeOid)) elog(ERROR, "method lextype isn't defined for text search parser %u", prsid); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (TSTokenTypeStorage *) palloc(sizeof(TSTokenTypeStorage)); st->cur = 0; /* lextype takes one dummy argument */ st->list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid, (Datum) 0)); funcctx->user_fctx = (void *) st; tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
/* * Add typmod decoration to the basic type name */ static char * printTypmod(const char *typname, int32 typmod, Oid typmodout) { char *res; /* Shouldn't be called if typmod is -1 */ Assert(typmod >= 0); if (typmodout == InvalidOid) { /* Default behavior: just print the integer typmod with parens */ res = psnprintf(strlen(typname) + MAX_INT32_LEN + 3, "%s(%d)", typname, (int) typmod); } else { /* Use the type-specific typmodout procedure */ char *tmstr; tmstr = DatumGetCString(OidFunctionCall1(typmodout, Int32GetDatum(typmod))); res = psnprintf(strlen(typname) + strlen(tmstr) + 1, "%s%s", typname, tmstr); } return res; }
char * svec_out_internal(SvecType *svec) { char *ix_string,*vals_string,*result; int ixlen,vslen; SparseData sdata=sdata_from_svec(svec); int64 *array_ix =sdata_index_to_int64arr(sdata); ArrayType *pgarray_ix,*pgarray_vals; pgarray_ix = construct_array((Datum *)array_ix, sdata->unique_value_count,INT8OID, sizeof(int64),true,'d'); ix_string = DatumGetPointer(OidFunctionCall1(F_ARRAY_OUT, PointerGetDatum(pgarray_ix))); ixlen = strlen(ix_string); pgarray_vals = construct_array((Datum *)sdata->vals->data, sdata->unique_value_count,FLOAT8OID, sizeof(float8),true,'d'); vals_string = DatumGetPointer(OidFunctionCall1(F_ARRAY_OUT, PointerGetDatum(pgarray_vals))); vslen = strlen(vals_string); result = (char *)palloc(sizeof(char)*(vslen+ixlen+1+1)); /* NULLs are represented as NaN internally; see svec_in(); * Here we print each NaN as an NVP. */ for (int i=0; i!=vslen; i++) if (vals_string[i] == 'N') { vals_string[i+1] = 'V'; vals_string[i+2] = 'P'; i = i+2; } sprintf(result,"%s:%s",ix_string,vals_string); pfree(ix_string); pfree(vals_string); pfree(array_ix); return(result); }
/* ---------------- * index_markpos - mark a scan position * ---------------- */ void index_markpos(IndexScanDesc scan) { RegProcedure procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(markpos, ammarkpos); scan->unique_tuple_mark = scan->unique_tuple_pos; OidFunctionCall1(procedure, PointerGetDatum(scan)); }
static SV * plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) { HV *hv; int i; hv = newHV(); for (i = 0; i < tupdesc->natts; i++) { Datum attr; bool isnull; char *attname; char *outputstr; Oid typoutput; bool typisvarlena; int namelen; SV *sv; if (tupdesc->attrs[i]->attisdropped) continue; attname = NameStr(tupdesc->attrs[i]->attname); namelen = strlen(attname); attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (isnull) { /* Store (attname => undef) and move on. */ hv_store(hv, attname, namelen, newSV(0), 0); continue; } /* XXX should have a way to cache these lookups */ getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); outputstr = DatumGetCString(OidFunctionCall1(typoutput, attr)); sv = newSVpv(outputstr, 0); #if PERL_BCDVERSION >= 0x5006000L if (GetDatabaseEncoding() == PG_UTF8) SvUTF8_on(sv); #endif hv_store(hv, attname, namelen, sv, 0); pfree(outputstr); } return newRV_noinc((SV *) hv); }
void init_dict(Oid id, DictInfo * dict) { Oid arg[1]; bool isnull; Datum pars[1]; int stat; void *plan; char buf[1024]; char *nsp = get_namespace(TSNSP_FunctionOid); arg[0] = OIDOID; pars[0] = ObjectIdGetDatum(id); memset(dict, 0, sizeof(DictInfo)); SPI_connect(); sprintf(buf, "select dict_init, dict_initoption, dict_lexize from %s.pg_ts_dict where oid = $1", nsp); pfree(nsp); plan = SPI_prepare(buf, 1, arg); if (!plan) ts_error(ERROR, "SPI_prepare() failed"); stat = SPI_execp(plan, pars, " ", 1); if (stat < 0) ts_error(ERROR, "SPI_execp return %d", stat); if (SPI_processed > 0) { Datum opt; Oid oid = InvalidOid; oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); if (!(isnull || oid == InvalidOid)) { opt = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &isnull); dict->dictionary = (void *) DatumGetPointer(OidFunctionCall1(oid, opt)); } oid = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &isnull)); if (isnull || oid == InvalidOid) ts_error(ERROR, "Null dict_lexize for dictonary %d", id); fmgr_info_cxt(oid, &(dict->lexize_info), TopMemoryContext); dict->dict_id = id; } else ts_error(ERROR, "No dictionary with id %d", id); SPI_freeplan(plan); SPI_finish(); }
/* * _hash_datum2hashkey_type -- given a Datum of a specified type, * hash it in a fashion compatible with this index * * This is much more expensive than _hash_datum2hashkey, so use it only in * cross-type situations. */ uint32 _hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype) { RegProcedure hash_proc; /* XXX assumes index has only one attribute */ hash_proc = get_opfamily_proc(rel->rd_opfamily[0], keytype, keytype, HASHPROC); if (!RegProcedureIsValid(hash_proc)) elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"", HASHPROC, keytype, keytype, RelationGetRelationName(rel)); return DatumGetUInt32(OidFunctionCall1(hash_proc, key)); }
/* ---------------- * index_restrpos - restore a scan position * ---------------- */ void index_restrpos(IndexScanDesc scan) { RegProcedure procedure; SCAN_CHECKS; GET_SCAN_PROCEDURE(restrpos, amrestrpos); scan->kill_prior_tuple = false; /* for safety */ /* * We do not reset got_tuple; so if the scan is actually being * short-circuited by index_getnext, the effective position * restoration is done by restoring unique_tuple_pos. */ scan->unique_tuple_pos = scan->unique_tuple_mark; OidFunctionCall1(procedure, PointerGetDatum(scan)); }
static void setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, Oid prsid) { TupleDesc tupdesc; MemoryContext oldcontext; TypeStorage *st; WParserInfo *prs = findprs(prsid); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); st = (TypeStorage *) palloc(sizeof(TypeStorage)); st->cur = 0; st->list = (LexDescr *) DatumGetPointer( OidFunctionCall1(prs->lextype, PointerGetDatum(prs->prs)) ); funcctx->user_fctx = (void *) st; if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupdesc = CreateTupleDescCopy(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
/* * Turn a Datum into jsonb, adding it to the result JsonbInState. * * tcategory and outfuncoid are from a previous call to json_categorize_type, * except that if is_null is true then they can be invalid. * * If key_scalar is true, the value is stored as a key, so insist * it's of an acceptable type, and force it to be a jbvString. */ static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar) { char *outputstr; bool numeric_error; JsonbValue jb; bool scalar_jsonb = false; check_stack_depth(); /* Convert val to a JsonbValue in jb (in most cases) */ if (is_null) { Assert(!key_scalar); jb.type = jbvNull; } else if (key_scalar && (tcategory == JSONBTYPE_ARRAY || tcategory == JSONBTYPE_COMPOSITE || tcategory == JSONBTYPE_JSON || tcategory == JSONBTYPE_JSONB || tcategory == JSONBTYPE_JSONCAST)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("key value must be scalar, not array, composite, or json"))); } else { if (tcategory == JSONBTYPE_JSONCAST) val = OidFunctionCall1(outfuncoid, val); switch (tcategory) { case JSONBTYPE_ARRAY: array_to_jsonb_internal(val, result); break; case JSONBTYPE_COMPOSITE: composite_to_jsonb(val, result); break; case JSONBTYPE_BOOL: if (key_scalar) { outputstr = DatumGetBool(val) ? "true" : "false"; jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { jb.type = jbvBool; jb.val.boolean = DatumGetBool(val); } break; case JSONBTYPE_NUMERIC: outputstr = OidOutputFunctionCall(outfuncoid, val); if (key_scalar) { /* always quote keys */ jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { /* * Make it numeric if it's a valid JSON number, otherwise * a string. Invalid numeric output will always have an * 'N' or 'n' in it (I think). */ numeric_error = (strchr(outputstr, 'N') != NULL || strchr(outputstr, 'n') != NULL); if (!numeric_error) { jb.type = jbvNumeric; jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1)); pfree(outputstr); } else { jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } } break; case JSONBTYPE_DATE: { DateADT date; struct pg_tm tm; char buf[MAXDATELEN + 1]; date = DatumGetDateADT(val); /* Same as date_out(), but forcing DateStyle */ if (DATE_NOT_FINITE(date)) EncodeSpecialDate(date, buf); else { j2date(date + POSTGRES_EPOCH_JDATE, &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday)); EncodeDateOnly(&tm, USE_XSD_DATES, buf); } jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMP: { Timestamp timestamp; struct pg_tm tm; fsec_t fsec; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(val); /* Same as timestamp_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0) EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMPTZ: { TimestampTz timestamp; struct pg_tm tm; int tz; fsec_t fsec; const char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestampTz(val); /* Same as timestamptz_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_JSONCAST: case JSONBTYPE_JSON: { /* parse the json right into the existing result object */ JsonLexContext *lex; JsonSemAction sem; text *json = DatumGetTextP(val); lex = makeJsonLexContext(json, true); memset(&sem, 0, sizeof(sem)); sem.semstate = (void *) result; sem.object_start = jsonb_in_object_start; sem.array_start = jsonb_in_array_start; sem.object_end = jsonb_in_object_end; sem.array_end = jsonb_in_array_end; sem.scalar = jsonb_in_scalar; sem.object_field_start = jsonb_in_object_field_start; pg_parse_json(lex, &sem); } break; case JSONBTYPE_JSONB: { Jsonb *jsonb = DatumGetJsonb(val); JsonbIterator *it; it = JsonbIteratorInit(&jsonb->root); if (JB_ROOT_IS_SCALAR(jsonb)) { (void) JsonbIteratorNext(&it, &jb, true); Assert(jb.type == jbvArray); (void) JsonbIteratorNext(&it, &jb, true); scalar_jsonb = true; } else { JsonbIteratorToken type; while ((type = JsonbIteratorNext(&it, &jb, false)) != WJB_DONE) { if (type == WJB_END_ARRAY || type == WJB_END_OBJECT || type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT) result->res = pushJsonbValue(&result->parseState, type, NULL); else result->res = pushJsonbValue(&result->parseState, type, &jb); } } } break; default: outputstr = OidOutputFunctionCall(outfuncoid, val); jb.type = jbvString; jb.val.string.len = checkStringLen(strlen(outputstr)); jb.val.string.val = outputstr; break; } } /* Now insert jb into result, unless we did it recursively */ if (!is_null && !scalar_jsonb && tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST) { /* work has been done recursively */ return; } else if (result->parseState == NULL) { /* single root scalar */ JsonbValue va; va.type = jbvArray; va.val.array.rawScalar = true; va.val.array.nElems = 1; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va); result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL); } else { JsonbValue *o = &result->parseState->contVal; switch (o->type) { case jbvArray: result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); break; case jbvObject: result->res = pushJsonbValue(&result->parseState, key_scalar ? WJB_KEY : WJB_VALUE, &jb); break; default: elog(ERROR, "unexpected parent of nested structure"); } } }
/* * pgpool_switch_log is the same as pg_switch_xlog except that * it wait till archiving is completed. * We call xlog functions with the oid to avoid a compile error * at old PostgreSQL. */ Datum pgpool_switch_xlog(PG_FUNCTION_ARGS) { char *archive_dir; char *filename; char path[MAXPGPATH]; struct stat fst; Datum location; text *filename_t; text *result; Oid switch_xlog_oid; Oid xlogfile_name_oid; #if !defined(PG_VERSION_NUM) || (PG_VERSION_NUM < 90400) char *pg_xlogfile_name_arg_type = "text"; #else /* * The argument data type of PG's pg_xlogfile_name() function has been * changed from text to pg_lsn since PostgreSQL 9.4 */ char *pg_xlogfile_name_arg_type = "pg_lsn"; #endif archive_dir = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); if (stat(archive_dir, &fst) < 0) #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", archive_dir))); #else elog(ERROR, "could not stat file \"%s\"", archive_dir); #endif switch_xlog_oid = get_function_oid("pg_switch_xlog", NULL, "pg_catalog"); xlogfile_name_oid = get_function_oid("pg_xlogfile_name", pg_xlogfile_name_arg_type, "pg_catalog"); if (!switch_xlog_oid || !xlogfile_name_oid) { /* probably PostgreSQL is 10 or greater */ switch_xlog_oid = get_function_oid("pg_switch_wal", NULL, "pg_catalog"); xlogfile_name_oid = get_function_oid("pg_walfile_name", pg_xlogfile_name_arg_type, "pg_catalog"); if (!switch_xlog_oid || !xlogfile_name_oid) elog(ERROR, "cannot find xlog functions"); } location = OidFunctionCall1(switch_xlog_oid, PointerGetDatum(NULL)); filename_t = DatumGetTextP(OidFunctionCall1(xlogfile_name_oid, location)); filename = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(filename_t))); snprintf(path, MAXPGPATH, "%s/%s", archive_dir, filename); elog(LOG, "pgpool_switch_xlog: waiting for \"%s\"", path); while (stat(path, &fst) != 0 || fst.st_size == 0 || fst.st_size % (1024 * 1024) != 0) { CHECK_FOR_INTERRUPTS(); sleep(1); } result = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(path))); PG_RETURN_TEXT_P(result); }
/* * Trigger */ Datum tsearch2(PG_FUNCTION_ARGS) { TriggerData *trigdata; Trigger *trigger; Relation rel; HeapTuple rettuple = NULL; TSCfgInfo *cfg = findcfg(get_currcfg()); int numidxattr, i; PRSTEXT prs; Datum datum = (Datum) 0; Oid funcoid = InvalidOid; if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ elog(ERROR, "TSearch: Not fired by trigger manager"); trigdata = (TriggerData *) fcinfo->context; if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) /* internal error */ elog(ERROR, "TSearch: Can't process STATEMENT events"); if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) /* internal error */ elog(ERROR, "TSearch: Must be fired BEFORE event"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) rettuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) rettuple = trigdata->tg_newtuple; else /* internal error */ elog(ERROR, "TSearch: Unknown event"); trigger = trigdata->tg_trigger; rel = trigdata->tg_relation; if (trigger->tgnargs < 2) /* internal error */ elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)"); numidxattr = SPI_fnumber(rel->rd_att, trigger->tgargs[0]); if (numidxattr == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("tsvector column \"%s\" does not exist", trigger->tgargs[0]))); prs.lenwords = 32; prs.curwords = 0; prs.pos = 0; prs.words = (WORD *) palloc(sizeof(WORD) * prs.lenwords); /* find all words in indexable column */ for (i = 1; i < trigger->tgnargs; i++) { int numattr; Oid oidtype; Datum txt_toasted; bool isnull; text *txt; numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]); if (numattr == SPI_ERROR_NOATTRIBUTE) { funcoid = findFunc(trigger->tgargs[i]); if (funcoid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("could not find function or field \"%s\"", trigger->tgargs[i]))); continue; } oidtype = SPI_gettypeid(rel->rd_att, numattr); /* We assume char() and varchar() are binary-equivalent to text */ if (!(oidtype == TEXTOID || oidtype == VARCHAROID || oidtype == BPCHAROID)) { elog(WARNING, "TSearch: '%s' is not of character type", trigger->tgargs[i]); continue; } txt_toasted = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull); if (isnull) continue; if (funcoid != InvalidOid) { text *txttmp = (text *) DatumGetPointer(OidFunctionCall1( funcoid, PointerGetDatum(txt_toasted) )); txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txttmp))); if (txt == txttmp) txt_toasted = PointerGetDatum(txt); } else txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txt_toasted))); parsetext_v2(cfg, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ); if (txt != (text *) DatumGetPointer(txt_toasted)) pfree(txt); } /* make tsvector value */ if (prs.curwords) { datum = PointerGetDatum(makevalue(&prs)); rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr, &datum, NULL); pfree(DatumGetPointer(datum)); } else { tsvector *out = palloc(CALCDATASIZE(0, 0)); out->len = CALCDATASIZE(0, 0); out->size = 0; datum = PointerGetDatum(out); pfree(prs.words); rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr, &datum, NULL); } if (rettuple == NULL) /* internal error */ elog(ERROR, "TSearch: %d returned by SPI_modifytuple", SPI_result); return PointerGetDatum(rettuple); }
/* LocalTableSize returns the size on disk of the given table. */ static uint64 LocalTableSize(Oid relationId) { uint64 tableSize = 0; char relationType = 0; Datum relationIdDatum = ObjectIdGetDatum(relationId); relationType = get_rel_relkind(relationId); if (relationType == RELKIND_RELATION) { Datum tableSizeDatum = DirectFunctionCall1(pg_table_size, relationIdDatum); tableSize = DatumGetInt64(tableSizeDatum); } else if (relationType == RELKIND_FOREIGN_TABLE) { bool cstoreTable = CStoreTable(relationId); if (cstoreTable) { /* extract schema name of cstore */ Oid cstoreId = get_extension_oid(CSTORE_FDW_NAME, false); Oid cstoreSchemaOid = get_extension_schema(cstoreId); const char *cstoreSchemaName = get_namespace_name(cstoreSchemaOid); const int tableSizeArgumentCount = 1; Oid tableSizeFunctionOid = FunctionOid(cstoreSchemaName, CSTORE_TABLE_SIZE_FUNCTION_NAME, tableSizeArgumentCount); Datum tableSizeDatum = OidFunctionCall1(tableSizeFunctionOid, relationIdDatum); tableSize = DatumGetInt64(tableSizeDatum); } else { char *relationName = get_rel_name(relationId); struct stat fileStat; int statOK = 0; StringInfo localFilePath = makeStringInfo(); appendStringInfo(localFilePath, FOREIGN_CACHED_FILE_PATH, relationName); /* extract the file size using stat, analogous to pg_stat_file */ statOK = stat(localFilePath->data, &fileStat); if (statOK < 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", localFilePath->data))); } tableSize = (uint64) fileStat.st_size; } } else { char *relationName = get_rel_name(relationId); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot get size for table \"%s\"", relationName), errdetail("Only regular and foreign tables are supported."))); } return tableSize; }
/* * XXX: If type methods ever come along, hopefully this will be implemented * using a more generalized function. */ static PyObj obj_absolute(PyObj self) { MemoryContext former; Oid typoid; volatile PyObj rob = NULL; if (DB_IS_NOT_READY() || PyPgObjectType_Require(Py_TYPE(self))) return(NULL); typoid = PyPgType_GetOid(Py_TYPE(self)); former = CurrentMemoryContext; PG_TRY(); { HeapTuple procTuple; Datum rd = 0; Oid procoid, roid; List *qnl; qnl = stringToQualifiedNameList("abs"); procoid = LookupFuncName(qnl, 1, &(typoid), true); list_free(qnl); if (procoid == InvalidOid) { PyErr_Format(PyExc_LookupError, "no such function named 'abs' for type %u", typoid); return(NULL); } procTuple = SearchSysCache(PROCOID, procoid, 0, 0, 0); if (procTuple == NULL) { PyErr_Format(PyExc_LookupError, "no procedure with Oid %u", procoid); return(NULL); } roid = ((Form_pg_proc) GETSTRUCT(procTuple))->prorettype; ReleaseSysCache(procTuple); rd = OidFunctionCall1(procoid, PyPgObject_GetDatum(self)); rob = PyPgObject_FromTypeOidAndDatum(roid, rd); if (PyPgType_ShouldFree(Py_TYPE(rob))) { /* * That's our datum... */ if (PyPgObject_GetDatum(self) != rd) pfree(DatumGetPointer(rd)); } } PG_CATCH(); { Py_XDECREF(rob); PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); return(rob); }
/* * Turn a scalar Datum into JSON, appending the string to "result". * * Hand off a non-scalar datum to composite_to_json or array_to_json_internal * as appropriate. */ static void datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory, Oid typoutputfunc) { char *outputstr; text *jsontext; if (is_null) { appendStringInfoString(result, "null"); return; } switch (tcategory) { case TYPCATEGORY_ARRAY: array_to_json_internal(val, result, false); break; case TYPCATEGORY_COMPOSITE: composite_to_json(val, result, false); break; case TYPCATEGORY_BOOLEAN: if (DatumGetBool(val)) appendStringInfoString(result, "true"); else appendStringInfoString(result, "false"); break; case TYPCATEGORY_NUMERIC: outputstr = OidOutputFunctionCall(typoutputfunc, val); /* * Don't call escape_json here if it's a valid JSON number. * Numeric output should usually be a valid JSON number and JSON * numbers shouldn't be quoted. Quote cases like "Nan" and * "Infinity", however. */ if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL) appendStringInfoString(result, outputstr); else escape_json(result, outputstr); pfree(outputstr); break; case TYPCATEGORY_JSON: /* JSON will already be escaped */ outputstr = OidOutputFunctionCall(typoutputfunc, val); appendStringInfoString(result, outputstr); pfree(outputstr); break; case TYPCATEGORY_JSON_CAST: jsontext = DatumGetTextP(OidFunctionCall1(typoutputfunc, val)); outputstr = text_to_cstring(jsontext); appendStringInfoString(result, outputstr); pfree(outputstr); pfree(jsontext); break; default: outputstr = OidOutputFunctionCall(typoutputfunc, val); escape_json(result, outputstr); pfree(outputstr); break; } }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames are either arrays * of the proper types or NULL. We declare them Datum, not "ArrayType *", * to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */ Oid ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, bool isAgg, bool security_definer, bool isStrict, char volatility, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; bool genericInParam = false; bool genericOutParam = false; bool internalInParam = false; bool internalOutParam = false; Relation rel; HeapTuple tup; HeapTuple oldtup; char nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; char replaces[Natts_pg_proc]; Oid relid; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; /* * sanity checks */ Assert(PointerIsValid(prosrc)); Assert(PointerIsValid(probin)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("functions cannot have more than %d arguments", FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ allParamCount = ARR_DIMS(DatumGetPointer(allParameterTypes))[0]; if (ARR_NDIM(DatumGetPointer(allParameterTypes)) != 1 || allParamCount <= 0 || ARR_ELEMTYPE(DatumGetPointer(allParameterTypes)) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(DatumGetPointer(allParameterTypes)); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } /* * Do not allow return type ANYARRAY or ANYELEMENT unless at least one * input argument is ANYARRAY or ANYELEMENT. Also, do not allow return * type INTERNAL unless at least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { /* * We don't bother to distinguish input and output params here, so * if there is, say, just an input INTERNAL param then we will * still set internalOutParam. This is OK since we don't really * care. */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OidIsValid(parameterTypes->values[0]) && (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid && get_attnum(relid, procedureName) != InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = ' '; values[i] = (Datum) 0; replaces[i] = 'r'; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = 'n'; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = 'n'; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = 'n'; values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(prosrc)); values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin, CStringGetDatum(probin)); /* start out with empty permissions */ nulls[Anum_pg_proc_proacl - 1] = 'n'; rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace), 0); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION first."))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION first."))); } /* Can't change aggregate status, either */ if (oldproc->proisagg != isAgg) { if (oldproc->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is an aggregate", procedureName))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is not an aggregate", procedureName))); } /* do not change existing ownership or permissions, either */ replaces[Anum_pg_proc_proowner - 1] = ' '; replaces[Anum_pg_proc_proacl - 1] = ' '; /* Okay, do it... */ tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); is_update = true; } else { /* Creating a new procedure */ tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; } /* Need to update indexes for either the insert or update case */ CatalogUpdateIndexes(rel, tup); retval = HeapTupleGetOid(tup); /* * Create dependencies for the new function. If we are updating an * existing function, first delete any existing pg_depend entries. */ if (is_update) { deleteDependencyRecordsFor(ProcedureRelationId, retval); deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); } myself.classId = ProcedureRelationId; myself.objectId = retval; myself.objectSubId = 0; /* dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = procNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on implementation language */ referenced.classId = LanguageRelationId; referenced.objectId = languageObjectId; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on return type */ referenced.classId = TypeRelationId; referenced.objectId = returnType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on parameter types */ for (i = 0; i < allParamCount; i++) { referenced.classId = TypeRelationId; referenced.objectId = allParams[i]; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId()); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); /* Verify function body */ if (OidIsValid(languageValidator)) { /* Advance command counter so new tuple can be seen by validator */ CommandCounterIncrement(); OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval)); } return retval; }
/* * Fetch dictionary cache entry */ TSDictionaryCacheEntry * lookup_ts_dictionary_cache(Oid dictId) { TSDictionaryCacheEntry *entry; if (TSDictionaryCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TSDictionaryCacheEntry); TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8, &ctl, HASH_ELEM | HASH_BLOBS); /* Flush cache on pg_ts_dict and pg_ts_template changes */ CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) CreateCacheMemoryContext(); } /* Check single-entry cache */ if (lastUsedDictionary && lastUsedDictionary->dictId == dictId && lastUsedDictionary->isvalid) return lastUsedDictionary; /* Try to look up an existing entry */ entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash, (void *) &dictId, HASH_FIND, NULL); if (entry == NULL || !entry->isvalid) { /* * If we didn't find one, we want to make one. But first look up the * object to be sure the OID is real. */ HeapTuple tpdict, tptmpl; Form_pg_ts_dict dict; Form_pg_ts_template ctemplate; MemoryContext saveCtx; tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId)); if (!HeapTupleIsValid(tpdict)) elog(ERROR, "cache lookup failed for text search dictionary %u", dictId); dict = (Form_pg_ts_dict) GETSTRUCT(tpdict); /* * Sanity checks */ if (!OidIsValid(dict->dicttemplate)) elog(ERROR, "text search dictionary %u has no ctemplate", dictId); /* * Retrieve dictionary's ctemplate */ tptmpl = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(dict->dicttemplate)); if (!HeapTupleIsValid(tptmpl)) elog(ERROR, "cache lookup failed for text search ctemplate %u", dict->dicttemplate); ctemplate = (Form_pg_ts_template) GETSTRUCT(tptmpl); /* * Sanity checks */ if (!OidIsValid(ctemplate->tmpllexize)) elog(ERROR, "text search ctemplate %u has no lexize method", ctemplate->tmpllexize); if (entry == NULL) { bool found; /* Now make the cache entry */ entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash, (void *) &dictId, HASH_ENTER, &found); Assert(!found); /* it wasn't there a moment ago */ /* Create private___ memory context the first time through */ saveCtx = AllocSetContextCreate(CacheMemoryContext, NameStr(dict->dictname), ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); } else { /* Clear the existing entry's private___ context */ saveCtx = entry->dictCtx; MemoryContextResetAndDeleteChildren(saveCtx); } MemSet(entry, 0, sizeof(TSDictionaryCacheEntry)); entry->dictId = dictId; entry->dictCtx = saveCtx; entry->lexizeOid = ctemplate->tmpllexize; if (OidIsValid(ctemplate->tmplinit)) { List *dictoptions; Datum opt; bool isnull; MemoryContext oldcontext; /* * Init method runs in dictionary's private___ memory context, and we * make sure the options are stored there too */ oldcontext = MemoryContextSwitchTo(entry->dictCtx); opt = SysCacheGetAttr(TSDICTOID, tpdict, Anum_pg_ts_dict_dictinitoption, &isnull); if (isnull) dictoptions = NIL; else dictoptions = deserialize_deflist(opt); entry->dictData = DatumGetPointer(OidFunctionCall1(ctemplate->tmplinit, PointerGetDatum(dictoptions))); MemoryContextSwitchTo(oldcontext); } ReleaseSysCache(tptmpl); ReleaseSysCache(tpdict); fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx); entry->isvalid = true; } lastUsedDictionary = entry; return entry; }