/* * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum * as list of DefElem. */ List * GetForeignColumnOptions(Oid relid, AttrNumber attnum) { List *options; HeapTuple tp; Datum datum; bool isnull; tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, relid); datum = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attfdwoptions, &isnull); if (isnull) options = NIL; else options = untransformRelOptions(datum); ReleaseSysCache(tp); return options; }
/* * GetUserMapping - look up the user mapping. * * If no mapping is found for the supplied user, we also look for * PUBLIC mappings (userid == InvalidOid). */ UserMapping * GetUserMapping(Oid userid, Oid serverid) { Form_pg_user_mapping umform; Datum datum; HeapTuple tp; bool isnull; UserMapping *um; tp = SearchSysCache2(USERMAPPINGUSERSERVER, ObjectIdGetDatum(userid), ObjectIdGetDatum(serverid)); if (!HeapTupleIsValid(tp)) { /* Not found for the specific user -- try PUBLIC */ tp = SearchSysCache2(USERMAPPINGUSERSERVER, ObjectIdGetDatum(InvalidOid), ObjectIdGetDatum(serverid)); } if (!HeapTupleIsValid(tp)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("user mapping not found for \"%s\"", MappingUserName(userid)))); umform = (Form_pg_user_mapping) GETSTRUCT(tp); /* Extract the umoptions */ datum = SysCacheGetAttr(USERMAPPINGUSERSERVER, tp, Anum_pg_user_mapping_umoptions, &isnull); um = palloc(sizeof(UserMapping)); um->userid = userid; um->serverid = serverid; um->options = untransformRelOptions(datum); ReleaseSysCache(tp); return um; }
/* * SQL function to_json(anyvalue) */ Datum to_json(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); StringInfo result; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); result = makeStringInfo(); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); datum_to_json(val, false, result, tcategory, typoutput); PG_RETURN_TEXT_P(cstring_to_text(result->data)); }
/* * Get state of subscription table. * * Returns SUBREL_STATE_UNKNOWN when not found and missing_ok is true. */ char GetSubscriptionRelState(Oid subid, Oid relid, XLogRecPtr *sublsn, bool missing_ok) { Relation rel; HeapTuple tup; char substate; bool isnull; Datum d; rel = table_open(SubscriptionRelRelationId, AccessShareLock); /* Try finding the mapping. */ tup = SearchSysCache2(SUBSCRIPTIONRELMAP, ObjectIdGetDatum(relid), ObjectIdGetDatum(subid)); if (!HeapTupleIsValid(tup)) { if (missing_ok) { table_close(rel, AccessShareLock); *sublsn = InvalidXLogRecPtr; return SUBREL_STATE_UNKNOWN; } elog(ERROR, "subscription table %u in subscription %u does not exist", relid, subid); } /* Get the state. */ d = SysCacheGetAttr(SUBSCRIPTIONRELMAP, tup, Anum_pg_subscription_rel_srsubstate, &isnull); Assert(!isnull); substate = DatumGetChar(d); d = SysCacheGetAttr(SUBSCRIPTIONRELMAP, tup, Anum_pg_subscription_rel_srsublsn, &isnull); if (isnull) *sublsn = InvalidXLogRecPtr; else *sublsn = DatumGetLSN(d); /* Cleanup */ ReleaseSysCache(tup); table_close(rel, AccessShareLock); return substate; }
/* * SearchSysCacheAttNum * * This routine is equivalent to SearchSysCache on the ATTNUM cache, * except that it will return NULL if the found attribute is marked * attisdropped. This is convenient for callers that want to act as * though dropped attributes don't exist. */ HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum) { HeapTuple tuple; tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum)); if (!HeapTupleIsValid(tuple)) return NULL; if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped) { ReleaseSysCache(tuple); return NULL; } return tuple; }
/* * SearchSysCacheAttName * * This routine is equivalent to SearchSysCache on the ATTNAME cache, * except that it will return NULL if the found attribute is marked * attisdropped. This is convenient for callers that want to act as * though dropped attributes don't exist. */ HeapTuple SearchSysCacheAttName(Oid relid, const char *attname) { HeapTuple tuple; tuple = SearchSysCache2(ATTNAME, ObjectIdGetDatum(relid), CStringGetDatum(attname)); if (!HeapTupleIsValid(tuple)) return NULL; if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped) { ReleaseSysCache(tuple); return NULL; } return tuple; }
/* * fixup_whole_row_references * * When user reference a whole of row, it is equivalent to reference to * all the user columns (not system columns). So, we need to fix up the * given bitmapset, if it contains a whole of the row reference. */ static Bitmapset * fixup_whole_row_references(Oid relOid, Bitmapset *columns) { Bitmapset *result; HeapTuple tuple; AttrNumber natts; AttrNumber attno; int index; /* if no whole of row references, do not anything */ index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; if (!bms_is_member(index, columns)) return columns; /* obtain number of attributes */ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relOid); natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts; ReleaseSysCache(tuple); /* fix up the given columns */ result = bms_copy(columns); result = bms_del_member(result, index); for (attno = 1; attno <= natts; attno++) { tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relOid), Int16GetDatum(attno)); if (!HeapTupleIsValid(tuple)) continue; if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped) continue; index = attno - FirstLowInvalidHeapAttributeNumber; result = bms_add_member(result, index); ReleaseSysCache(tuple); } return result; }
/* Binary I/O support */ Datum enum_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); Oid enumtypoid = PG_GETARG_OID(1); Oid enumoid; HeapTuple tup; char *name; int nbytes; name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); /* must check length to prevent Assert failure within SearchSysCache */ if (strlen(name) >= NAMEDATALEN) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input value for enum %s: \"%s\"", format_type_be(enumtypoid), name))); tup = SearchSysCache2(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid), CStringGetDatum(name)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input value for enum %s: \"%s\"", format_type_be(enumtypoid), name))); /* check it's safe to use in SQL */ check_safe_enum_use(tup); enumoid = HeapTupleGetOid(tup); ReleaseSysCache(tup); pfree(name); PG_RETURN_OID(enumoid); }
Datum enum_in(PG_FUNCTION_ARGS) { char *name = PG_GETARG_CSTRING(0); Oid enumtypoid = PG_GETARG_OID(1); Oid enumoid; HeapTuple tup; /* must check length to prevent Assert failure within SearchSysCache */ if (strlen(name) >= NAMEDATALEN) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input value for enum %s: \"%s\"", format_type_be(enumtypoid), name))); tup = SearchSysCache2(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid), CStringGetDatum(name)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input value for enum %s: \"%s\"", format_type_be(enumtypoid), name))); /* check it's safe to use in SQL */ check_safe_enum_use(tup); /* * This comes from pg_enum.oid and stores system oids in user tables. This * oid must be preserved by binary upgrades. */ enumoid = HeapTupleGetOid(tup); ReleaseSysCache(tup); PG_RETURN_OID(enumoid); }
/* * Find rule oid. * * If missing_ok is false, throw an error if rule name not found. If * true, just return InvalidOid. */ Oid get_rewrite_oid(Oid relid, const char *rulename, bool missing_ok) { HeapTuple tuple; Oid ruleoid; /* Find the rule's pg_rewrite tuple, get its OID */ tuple = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(relid), PointerGetDatum(rulename)); if (!HeapTupleIsValid(tuple)) { if (missing_ok) return InvalidOid; ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", rulename, get_rel_name(relid)))); } Assert(relid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class); ruleoid = HeapTupleGetOid(tuple); ReleaseSysCache(tuple); return ruleoid; }
/* * InsertRule - * takes the arguments and inserts them as a row into the system * relation "pg_rewrite" */ static Oid InsertRule(char *rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, Node *event_qual, List *action, bool replace) { char *evqual = nodeToString(event_qual); char *actiontree = nodeToString((Node *) action); int i; Datum values[Natts_pg_rewrite]; bool nulls[Natts_pg_rewrite]; bool replaces[Natts_pg_rewrite]; NameData rname; Relation pg_rewrite_desc; HeapTuple tup, oldtup; Oid rewriteObjectId; ObjectAddress myself, referenced; bool is_update = false; /* * Set up *nulls and *values arrays */ MemSet(nulls, false, sizeof(nulls)); i = 0; namestrcpy(&rname, rulname); values[i++] = NameGetDatum(&rname); /* rulename */ values[i++] = ObjectIdGetDatum(eventrel_oid); /* ev_class */ values[i++] = Int16GetDatum(evslot_index); /* ev_attr */ values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN); /* ev_enabled */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ /* * Ready to store new pg_rewrite tuple */ pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); /* * Check to see if we are replacing an existing tuple */ oldtup = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(eventrel_oid), PointerGetDatum(rulname)); if (HeapTupleIsValid(oldtup)) { if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", rulname, get_rel_name(eventrel_oid)))); /* * When replacing, we don't need to replace every attribute */ MemSet(replaces, false, sizeof(replaces)); replaces[Anum_pg_rewrite_ev_attr - 1] = true; replaces[Anum_pg_rewrite_ev_type - 1] = true; replaces[Anum_pg_rewrite_is_instead - 1] = true; replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; tup = heap_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc), values, nulls, replaces); simple_heap_update(pg_rewrite_desc, &tup->t_self, tup); ReleaseSysCache(oldtup); rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls); rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup); } /* Need to update indexes in either case */ CatalogUpdateIndexes(pg_rewrite_desc, tup); heap_freetuple(tup); /* If replacing, get rid of old dependencies and make new ones */ if (is_update) deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false); /* * Install dependency on rule's relation to ensure it will go away on * relation deletion. If the rule is ON SELECT, make the dependency * implicit --- this prevents deleting a view's SELECT rule. Other kinds * of rules can be AUTO. */ myself.classId = RewriteRelationId; myself.objectId = rewriteObjectId; myself.objectSubId = 0; referenced.classId = RelationRelationId; referenced.objectId = eventrel_oid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); /* * Also install dependencies on objects referenced in action and qual. */ recordDependencyOnExpr(&myself, (Node *) action, NIL, DEPENDENCY_NORMAL); if (event_qual != NULL) { /* Find query containing OLD/NEW rtable entries */ Query *qry = (Query *) linitial(action); qry = getInsertSelectQuery(qry, NULL); recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } /* Post creation hook for new rule */ InvokeObjectAccessHook(OAT_POST_CREATE, RewriteRelationId, rewriteObjectId, 0); heap_close(pg_rewrite_desc, RowExclusiveLock); return rewriteObjectId; }
/* * get_attribute_options * Fetch attribute options for a specified table OID. */ AttributeOpts * get_attribute_options(Oid attrelid, int attnum) { AttoptCacheKey key; AttoptCacheEntry *attopt; AttributeOpts *result; HeapTuple tp; /* Find existing cache entry, if any. */ if (!AttoptCacheHash) InitializeAttoptCache(); memset(&key, 0, sizeof(key)); /* make sure any padding bits are * unset */ key.attrelid = attrelid; key.attnum = attnum; attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash, (void *) &key, HASH_FIND, NULL); /* Not found in Attopt cache. Construct new cache entry. */ if (!attopt) { AttributeOpts *opts; tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(attrelid), Int16GetDatum(attnum)); /* * If we don't find a valid HeapTuple, it must mean someone has * managed to request attribute details for a non-existent attribute. * We treat that case as if no options were specified. */ if (!HeapTupleIsValid(tp)) opts = NULL; else { Datum datum; bool isNull; datum = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attoptions, &isNull); if (isNull) opts = NULL; else { bytea *bytea_opts = attribute_reloptions(datum, false); opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts)); memcpy(opts, bytea_opts, VARSIZE(bytea_opts)); } ReleaseSysCache(tp); } /* * It's important to create the actual cache entry only after reading * pg_attribute, since the read could cause a cache flush. */ attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash, (void *) &key, HASH_ENTER, NULL); attopt->opts = opts; } /* Return results in caller's memory context. */ if (attopt->opts == NULL) return NULL; result = palloc(VARSIZE(attopt->opts)); memcpy(result, attopt->opts, VARSIZE(attopt->opts)); return result; }
/* * json_agg transition function */ Datum json_agg_transfn(PG_FUNCTION_ARGS) { Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); MemoryContext aggcontext, oldcontext; StringInfo state; Datum val; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "json_agg_transfn called in non-aggregate context"); } if (PG_ARGISNULL(0)) { /* * Make this StringInfo in a context where it will persist for the * duration off the aggregate call. It's only needed for this initial * piece, as the StringInfo routines make sure they use the right * context to enlarge the object if necessary. */ oldcontext = MemoryContextSwitchTo(aggcontext); state = makeStringInfo(); MemoryContextSwitchTo(oldcontext); appendStringInfoChar(state, '['); } else { state = (StringInfo) PG_GETARG_POINTER(0); appendStringInfoString(state, ", "); } /* fast path for NULLs */ if (PG_ARGISNULL(1)) { val = (Datum) 0; datum_to_json(val, true, state, 0, InvalidOid); PG_RETURN_POINTER(state); } val = PG_GETARG_DATUM(1); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); if (!PG_ARGISNULL(0) && (tcategory == TYPCATEGORY_ARRAY || tcategory == TYPCATEGORY_COMPOSITE)) { appendStringInfoString(state, "\n "); } datum_to_json(val, false, state, tcategory, typoutput); /* * The transition type for array_agg() is declared to be "internal", which * is a pass-by-value type the same size as a pointer. So we can safely * pass the ArrayBuildState pointer through nodeAgg.c's machinations. */ PG_RETURN_POINTER(state); }
/* * SQL function to_json(anyvalue) */ Datum to_json(PG_FUNCTION_ARGS) { Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); StringInfo result; Datum orig_val, val; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); result = makeStringInfo(); orig_val = PG_ARGISNULL(0) ? (Datum) 0 : PG_GETARG_DATUM(0); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena && orig_val != (Datum) 0) val = PointerGetDatum(PG_DETOAST_DATUM(orig_val)); else val = orig_val; datum_to_json(val, false, result, tcategory, typoutput); /* Clean up detoasted copy, if any */ if (val != orig_val) pfree(DatumGetPointer(val)); PG_RETURN_TEXT_P(cstring_to_text(result->data)); }
/* * Turn a composite / record into JSON. */ static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup, *tuple; int i; bool needsep = false; const char *sep; sep = use_line_feeds ? ",\n " : ","; td = DatumGetHeapTupleHeader(composite); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuple = &tmptup; appendStringInfoChar(result, '{'); for (i = 0; i < tupdesc->natts; i++) { Datum val; bool isnull; char *attname; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (tupdesc->attrs[i]->attisdropped) continue; if (needsep) appendStringInfoString(result, sep); needsep = true; attname = NameStr(tupdesc->attrs[i]->attname); escape_json(result, attname); appendStringInfoChar(result, ':'); val = heap_getattr(tuple, i + 1, tupdesc, &isnull); getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); if (tupdesc->attrs[i]->atttypid > FirstNormalObjectId) { HeapTuple cast_tuple; Form_pg_cast castForm; cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(tupdesc->attrs[i]->atttypid), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(cast_tuple)) { castForm = (Form_pg_cast) GETSTRUCT(cast_tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(cast_tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (tupdesc->attrs[i]->atttypid == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (tupdesc->attrs[i]->atttypid == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(tupdesc->attrs[i]->atttypid); datum_to_json(val, isnull, result, tcategory, typoutput); } appendStringInfoChar(result, '}'); ReleaseTupleDesc(tupdesc); }
/* * Turn an array into JSON. */ static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds) { ArrayType *v = DatumGetArrayTypeP(array); Oid element_type = ARR_ELEMTYPE(v); int *dim; int ndim; int nitems; int count = 0; Datum *elements; bool *nulls; int16 typlen; bool typbyval; char typalign, typdelim; Oid typioparam; Oid typoutputfunc; TYPCATEGORY tcategory; Oid castfunc = InvalidOid; ndim = ARR_NDIM(v); dim = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dim); if (nitems <= 0) { appendStringInfoString(result, "[]"); return; } get_type_io_data(element_type, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typoutputfunc); if (element_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(element_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutputfunc = castForm->castfunc; ReleaseSysCache(tuple); } } deconstruct_array(v, element_type, typlen, typbyval, typalign, &elements, &nulls, &nitems); if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (element_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (element_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(element_type); array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory, typoutputfunc, use_line_feeds); pfree(elements); pfree(nulls); }
/* * Test whether an object exists. */ static bool object_exists(ObjectAddress address) { int cache = -1; Oid indexoid = InvalidOid; Relation rel; ScanKeyData skey[1]; SysScanDesc sd; bool found; /* Sub-objects require special treatment. */ if (address.objectSubId != 0) { HeapTuple atttup; /* Currently, attributes are the only sub-objects. */ Assert(address.classId == RelationRelationId); atttup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(address.objectId), Int16GetDatum(address.objectSubId)); if (!HeapTupleIsValid(atttup)) found = false; else { found = ((Form_pg_attribute) GETSTRUCT(atttup))->attisdropped; ReleaseSysCache(atttup); } return found; } /* * For object types that have a relevant syscache, we use it; for * everything else, we'll have to do an index-scan. This switch * sets either the cache to be used for the syscache lookup, or the * index to be used for the index scan. */ switch (address.classId) { case RelationRelationId: cache = RELOID; break; case RewriteRelationId: indexoid = RewriteOidIndexId; break; case TriggerRelationId: indexoid = TriggerOidIndexId; break; case ConstraintRelationId: cache = CONSTROID; break; case DatabaseRelationId: cache = DATABASEOID; break; case TableSpaceRelationId: cache = TABLESPACEOID; break; case AuthIdRelationId: cache = AUTHOID; break; case NamespaceRelationId: cache = NAMESPACEOID; break; case LanguageRelationId: cache = LANGOID; break; case TypeRelationId: cache = TYPEOID; break; case ProcedureRelationId: cache = PROCOID; break; case OperatorRelationId: cache = OPEROID; break; case CollationRelationId: cache = COLLOID; break; case ConversionRelationId: cache = CONVOID; break; case OperatorClassRelationId: cache = CLAOID; break; case OperatorFamilyRelationId: cache = OPFAMILYOID; break; case LargeObjectRelationId: /* * Weird backward compatibility hack: ObjectAddress notation uses * LargeObjectRelationId for large objects, but since PostgreSQL * 9.0, the relevant catalog is actually * LargeObjectMetadataRelationId. */ address.classId = LargeObjectMetadataRelationId; indexoid = LargeObjectMetadataOidIndexId; break; case CastRelationId: indexoid = CastOidIndexId; break; case ForeignDataWrapperRelationId: cache = FOREIGNDATAWRAPPEROID; break; case ForeignServerRelationId: cache = FOREIGNSERVEROID; break; case TSParserRelationId: cache = TSPARSEROID; break; case TSDictionaryRelationId: cache = TSDICTOID; break; case TSTemplateRelationId: cache = TSTEMPLATEOID; break; case TSConfigRelationId: cache = TSCONFIGOID; break; case ExtensionRelationId: indexoid = ExtensionOidIndexId; break; default: elog(ERROR, "unrecognized classid: %u", address.classId); } /* Found a syscache? */ if (cache != -1) return SearchSysCacheExists1(cache, ObjectIdGetDatum(address.objectId)); /* No syscache, so examine the table directly. */ Assert(OidIsValid(indexoid)); ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(address.objectId)); rel = heap_open(address.classId, AccessShareLock); sd = systable_beginscan(rel, indexoid, true, SnapshotNow, 1, skey); found = HeapTupleIsValid(systable_getnext(sd)); systable_endscan(sd); heap_close(rel, AccessShareLock); return found; }