/* ---------------------------------------------------------------- * UpdateRelationRelation * ---------------------------------------------------------------- */ static void UpdateRelationRelation(Relation indexRelation) { Relation pg_class; HeapTuple tuple; pg_class = heap_openr(RelationRelationName, RowExclusiveLock); /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */ tuple = heap_addheader(Natts_pg_class_fixed, true, CLASS_TUPLE_SIZE, (void *) indexRelation->rd_rel); /* * the new tuple must have the oid already chosen for the index. sure * would be embarrassing to do this sort of thing in polite company. */ HeapTupleSetOid(tuple, RelationGetRelid(indexRelation)); simple_heap_insert(pg_class, tuple); /* update the system catalog indexes */ CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); }
/* * intorel_receive --- receive one tuple */ static void intorel_receive(TupleTableSlot *slot, DestReceiver *self) { DR_intorel *myState = (DR_intorel *) self; HeapTuple tuple; /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ tuple = ExecMaterializeSlot(slot); /* * force assignment of new OID (see comments in ExecInsert) */ if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); heap_insert(myState->rel, tuple, myState->output_cid, myState->hi_options, myState->bistate); /* We know this is a newly created relation, so there are no indexes */ }
/* * InsertAgLabelTuple - register the new label in ag_label * * See InsertPgClassTuple() */ static void InsertAgLabelTuple(Relation ag_label_desc, Oid laboid, RangeVar *label, Oid relid, char labkind) { Oid graphid = get_graphname_oid(label->schemaname); char *labname = label->relname; int32 labid; Datum values[Natts_ag_label]; bool nulls[Natts_ag_label]; HeapTuple tup; AssertArg(labkind == LABEL_KIND_VERTEX || labkind == LABEL_KIND_EDGE); labid = (int32) GetNewLabelId(label->schemaname, graphid); values[Anum_ag_label_labname - 1] = CStringGetDatum(labname); values[Anum_ag_label_graphid - 1] = CStringGetDatum(graphid); values[Anum_ag_label_labid - 1] = Int32GetDatum(labid); values[Anum_ag_label_relid - 1] = ObjectIdGetDatum(relid); values[Anum_ag_label_labkind - 1] = CharGetDatum(labkind); memset(nulls, false, sizeof(nulls)); tup = heap_form_tuple(RelationGetDescr(ag_label_desc), values, nulls); HeapTupleSetOid(tup, laboid); simple_heap_insert(ag_label_desc, tup); CatalogUpdateIndexes(ag_label_desc, tup); heap_freetuple(tup); }
/* * heap_modify_tuple * form a new tuple from an old tuple and a set of replacement values. * * The replValues, replIsnull, and doReplace arrays must be of the length * indicated by tupleDesc->natts. The new tuple is constructed using the data * from replValues/replIsnull at columns where doReplace is true, and using * the data from the old tuple at columns where doReplace is false. * * The result is allocated in the current memory context. */ HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace) { int numberOfAttributes = tupleDesc->natts; int attoff; Datum *values; bool *isnull; HeapTuple newTuple; Assert(!is_heaptuple_memtuple(tuple)); /* * allocate and fill values and isnull arrays from either the tuple or the * repl information, as appropriate. * * NOTE: it's debatable whether to use heap_deform_tuple() here or just * heap_getattr() only the non-replaced colums. The latter could win if * there are many replaced columns and few non-replaced ones. However, * heap_deform_tuple costs only O(N) while the heap_getattr way would cost * O(N^2) if there are many non-replaced columns, so it seems better to * err on the side of linear cost. */ values = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); isnull = (bool *) palloc(numberOfAttributes * sizeof(bool)); heap_deform_tuple(tuple, tupleDesc, values, isnull); for (attoff = 0; attoff < numberOfAttributes; attoff++) { if (doReplace[attoff]) { values[attoff] = replValues[attoff]; isnull[attoff] = replIsnull[attoff]; } } /* * create a new tuple from the values and isnull arrays */ newTuple = heap_form_tuple(tupleDesc, values, isnull); pfree(values); pfree(isnull); /* * copy the identification info of the old tuple: t_ctid, t_self, and OID * (if any) */ newTuple->t_data->t_ctid = tuple->t_data->t_ctid; newTuple->t_self = tuple->t_self; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); return newTuple; }
/* ---------------- * NamespaceCreate * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid forceOid) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; TupleDesc tupDesc; int i; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); /* make sure there is no existing namespace of same name */ if (SearchSysCacheExists(NAMESPACENAME, PointerGetDatum(nspName), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_namespace_nspacl - 1] = true; nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); tupDesc = nspdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); if (forceOid != InvalidOid) HeapTupleSetOid(tup, forceOid); /* override heap_insert's OID * selection */ nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); CatalogUpdateIndexes(nspdesc, tup); heap_close(nspdesc, RowExclusiveLock); /* Record dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); return nspoid; }
/* * build_dummy_tuple * Generate a palloc'd HeapTuple that contains the specified key * columns, and NULLs for other columns. * * This is used to store the keys for negative cache entries and CatCList * entries, which don't have real tuples associated with them. */ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys) { HeapTuple ntp; TupleDesc tupDesc = cache->cc_tupdesc; Datum *values; char *nulls; Oid tupOid = InvalidOid; NameData tempNames[4]; int i; values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); nulls = (char *) palloc(tupDesc->natts * sizeof(char)); memset(values, 0, tupDesc->natts * sizeof(Datum)); memset(nulls, 'n', tupDesc->natts * sizeof(char)); for (i = 0; i < nkeys; i++) { int attindex = cache->cc_key[i]; Datum keyval = skeys[i].sk_argument; if (attindex > 0) { /* * Here we must be careful in case the caller passed a C * string where a NAME is wanted: convert the given argument * to a correctly padded NAME. Otherwise the memcpy() done in * heap_formtuple could fall off the end of memory. */ if (cache->cc_isname[i]) { Name newval = &tempNames[i]; namestrcpy(newval, DatumGetCString(keyval)); keyval = NameGetDatum(newval); } values[attindex - 1] = keyval; nulls[attindex - 1] = ' '; } else { Assert(attindex == ObjectIdAttributeNumber); tupOid = DatumGetObjectId(keyval); } } ntp = heap_formtuple(tupDesc, values, nulls); if (tupOid != InvalidOid) HeapTupleSetOid(ntp, tupOid); pfree(values); pfree(nulls); return ntp; }
/* adapted from SPI_modifytuple */ static HeapTuple luaP_copytuple (luaP_Tuple *t) { HeapTuple tuple = heap_form_tuple(t->desc, t->value, t->null); /* copy identification info */ tuple->t_data->t_ctid = t->tuple->t_data->t_ctid; tuple->t_self = t->tuple->t_self; tuple->t_tableOid = t->tuple->t_tableOid; if (t->desc->tdhasoid) HeapTupleSetOid(tuple, HeapTupleGetOid(t->tuple)); return SPI_copytuple(tuple); /* in upper mem context */ }
Datum caql_insert_to_in_memory_pg_class(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); char *tblname = text_to_cstring(PG_GETARG_TEXT_P(1)); Oid nspid = PG_GETARG_OID(2); Datum values[Natts_pg_class]; bool nulls[Natts_pg_class]; for (int i = 0; i < Natts_pg_class; i++) { nulls[i] = true; values[i] = (Datum) 0; } NameData name; namestrcpy(&name, tblname); values[Anum_pg_class_relname - 1] = NameGetDatum(&name); values[Anum_pg_class_relnamespace - 1] = ObjectIdGetDatum(nspid); nulls[Anum_pg_class_relname - 1] = false; nulls[Anum_pg_class_relnamespace - 1] = false; cqContext *pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_class", NULL)); HeapTuple tup = caql_form_tuple(pcqCtx, values, nulls); HeapTupleSetOid(tup, relid); caql_insert_inmem(pcqCtx, tup); caql_endscan(pcqCtx); StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "inserted tuple to pg_class (oid %d, relname %s, relnamespace %d)", relid, tblname, nspid); PG_RETURN_TEXT_P(cstring_to_text(buf.data)); }
/* * Create a large object having the given LO identifier. * * We create a new large object by inserting an entry into * pg_largeobject_metadata without any data pages, so that the object * will appear to exist with size 0. */ Oid LargeObjectCreate(Oid loid) { Relation pg_lo_meta; HeapTuple ntup; Oid loid_new; Datum values[Natts_pg_largeobject_metadata]; bool nulls[Natts_pg_largeobject_metadata]; pg_lo_meta = heap_open(LargeObjectMetadataRelationId, RowExclusiveLock); /* * Insert metadata of the largeobject */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); values[Anum_pg_largeobject_metadata_lomowner - 1] = ObjectIdGetDatum(GetUserId()); nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true; ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta), values, nulls); if (OidIsValid(loid)) HeapTupleSetOid(ntup, loid); loid_new = simple_heap_insert(pg_lo_meta, ntup); Assert(!OidIsValid(loid) || loid == loid_new); CatalogUpdateIndexes(pg_lo_meta, ntup); heap_freetuple(ntup); heap_close(pg_lo_meta, RowExclusiveLock); return loid_new; }
/* * ExtProtocolCreateWithOid */ Oid ExtProtocolCreateWithOid(const char *protocolName, List *readfuncName, List *writefuncName, List *validatorfuncName, Oid protOid, bool trusted) { Relation rel; HeapTuple tup; bool nulls[Natts_pg_extprotocol]; Datum values[Natts_pg_extprotocol]; Oid readfn = InvalidOid; Oid writefn = InvalidOid; Oid validatorfn = InvalidOid; NameData prtname; int i; ObjectAddress myself, referenced; Oid ownerId = GetUserId(); cqContext cqc; cqContext cqc2; cqContext *pcqCtx; /* sanity checks (caller should have caught these) */ if (!protocolName) elog(ERROR, "no protocol name supplied"); if (!readfuncName && !writefuncName) elog(ERROR, "protocol must have at least one of readfunc or writefunc"); /* * Until we add system protocols to pg_extprotocol, make sure no * protocols with the same name are created. */ if (strcasecmp(protocolName, "file") == 0 || strcasecmp(protocolName, "http") == 0 || strcasecmp(protocolName, "gpfdist") == 0 || strcasecmp(protocolName, "gpfdists") == 0) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("protocol \"%s\" already exists", protocolName), errhint("pick a different protocol name"))); } rel = heap_open(ExtprotocolRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_extprotocol", NULL)); /* make sure there is no existing protocol of same name */ if (caql_getcount( caql_addrel(cqclr(&cqc2), rel), cql("SELECT COUNT(*) FROM pg_extprotocol " " WHERE ptcname = :1 ", CStringGetDatum((char *) protocolName)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("protocol \"%s\" already exists", protocolName))); } /* * function checks: if supplied, check existence and correct signature in the catalog */ if (readfuncName) readfn = ValidateProtocolFunction(readfuncName, EXTPTC_FUNC_READER); if (writefuncName) writefn = ValidateProtocolFunction(writefuncName, EXTPTC_FUNC_WRITER); if (validatorfuncName) validatorfn = ValidateProtocolFunction(validatorfuncName, EXTPTC_FUNC_VALIDATOR); /* * Everything looks okay. Try to create the pg_extprotocol entry for the * protocol. (This could fail if there's already a conflicting entry.) */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_extprotocol; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&prtname, protocolName); values[Anum_pg_extprotocol_ptcname - 1] = NameGetDatum(&prtname); values[Anum_pg_extprotocol_ptcreadfn - 1] = ObjectIdGetDatum(readfn); values[Anum_pg_extprotocol_ptcwritefn - 1] = ObjectIdGetDatum(writefn); values[Anum_pg_extprotocol_ptcvalidatorfn - 1] = ObjectIdGetDatum(validatorfn); values[Anum_pg_extprotocol_ptcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_extprotocol_ptctrusted - 1] = BoolGetDatum(trusted); nulls[Anum_pg_extprotocol_ptcacl - 1] = true; tup = caql_form_tuple(pcqCtx, values, nulls); if (protOid != (Oid) 0) HeapTupleSetOid(tup, protOid); /* insert a new tuple */ protOid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); heap_close(rel, RowExclusiveLock); /* * Create dependencies for the protocol */ myself.classId = ExtprotocolRelationId; myself.objectId = protOid; myself.objectSubId = 0; /* Depends on read function, if any */ if (OidIsValid(readfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = readfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on write function, if any */ if (OidIsValid(writefn)) { referenced.classId = ProcedureRelationId; referenced.objectId = writefn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ExtprotocolRelationId, protOid, GetUserId()); return protOid; }
/* AddUpdResqueueCapabilityEntryInternal: * * Internal function to add a new entry to pg_resqueuecapability, or * update an existing one. Key cols are queueid, restypint. If * old_tuple is set (ie not InvalidOid), the update the ressetting column, * else insert a new row. * */ static List * AddUpdResqueueCapabilityEntryInternal( cqContext *pcqCtx, List *stmtOptIdList, Oid queueid, int resTypeInt, char *pResSetting, Relation rel, HeapTuple old_tuple) { HeapTuple new_tuple; Datum values[Natts_pg_resqueuecapability]; bool isnull[Natts_pg_resqueuecapability]; bool new_record_repl[Natts_pg_resqueuecapability]; MemSet(isnull, 0, sizeof(bool) * Natts_pg_resqueuecapability); MemSet(new_record_repl, 0, sizeof(bool) * Natts_pg_resqueuecapability); values[Anum_pg_resqueuecapability_resqueueid - 1] = ObjectIdGetDatum(queueid); values[Anum_pg_resqueuecapability_restypid - 1] = resTypeInt; Assert(pResSetting); values[Anum_pg_resqueuecapability_ressetting - 1] = CStringGetTextDatum(pResSetting); /* set this column to update */ new_record_repl[Anum_pg_resqueuecapability_ressetting - 1] = true; ValidateResqueueCapabilityEntry(resTypeInt, pResSetting); if (HeapTupleIsValid(old_tuple)) { new_tuple = caql_modify_current(pcqCtx, values, isnull, new_record_repl); caql_update_current(pcqCtx, new_tuple); /* and Update indexes (implicit) */ } else { Oid s1; new_tuple = caql_form_tuple(pcqCtx, values, isnull); /* MPP-11858: synchronize the oids for CREATE/ALTER options... */ if ((Gp_role != GP_ROLE_DISPATCH) && list_length(stmtOptIdList)) { Oid s2 = list_nth_oid(stmtOptIdList, 0); stmtOptIdList = list_delete_first(stmtOptIdList); if (OidIsValid(s2)) HeapTupleSetOid(new_tuple, s2); } s1 = caql_insert(pcqCtx, new_tuple); /* and Update indexes (implicit) */ if (Gp_role == GP_ROLE_DISPATCH) { stmtOptIdList = lappend_oid(stmtOptIdList, s1); } } if (HeapTupleIsValid(old_tuple)) heap_freetuple(new_tuple); return stmtOptIdList; } /* end AddUpdResqueueCapabilityEntryInternal */
/* * insert or update the existing fast sequence number for (objid, objmod). * * If such an entry exists in the table, it is provided in oldTuple. This tuple * is updated with the new value. Otherwise, a new tuple is inserted into the * table. */ static void insert_or_update_fastsequence(Relation gp_fastsequence_rel, HeapTuple oldTuple, TupleDesc tupleDesc, Oid objid, int64 objmod, int64 newLastSequence) { Datum *values; bool *nulls; HeapTuple newTuple; values = palloc0(sizeof(Datum) * tupleDesc->natts); nulls = palloc0(sizeof(bool) * tupleDesc->natts); /* * If such a tuple does not exist, insert a new one. */ if (!HeapTupleIsValid(oldTuple)) { values[Anum_gp_fastsequence_objid - 1] = ObjectIdGetDatum(objid); values[Anum_gp_fastsequence_objmod - 1] = Int64GetDatum(objmod); values[Anum_gp_fastsequence_last_sequence - 1] = Int64GetDatum(newLastSequence); newTuple = heaptuple_form_to(tupleDesc, values, nulls, NULL, NULL); frozen_heap_insert(gp_fastsequence_rel, newTuple); CatalogUpdateIndexes(gp_fastsequence_rel, newTuple); heap_freetuple(newTuple); } else { #ifdef USE_ASSERT_CHECKING Oid oldObjid; int64 oldObjmod; bool isNull; oldObjid = heap_getattr(oldTuple, Anum_gp_fastsequence_objid, tupleDesc, &isNull); Assert(!isNull); oldObjmod = heap_getattr(oldTuple, Anum_gp_fastsequence_objmod, tupleDesc, &isNull); Assert(!isNull); Assert(oldObjid == objid && oldObjmod == objmod); #endif values[Anum_gp_fastsequence_objid - 1] = ObjectIdGetDatum(objid); values[Anum_gp_fastsequence_objmod - 1] = Int64GetDatum(objmod); values[Anum_gp_fastsequence_last_sequence - 1] = Int64GetDatum(newLastSequence); newTuple = heap_form_tuple(tupleDesc, values, nulls); newTuple->t_data->t_ctid = oldTuple->t_data->t_ctid; newTuple->t_self = oldTuple->t_self; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(oldTuple)); heap_inplace_update(gp_fastsequence_rel, newTuple); heap_freetuple(newTuple); } pfree(values); pfree(nulls); }
/* * 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, Oid ruleOid) { 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; HeapTuple tup, oldtup; Oid rewriteObjectId; ObjectAddress myself, referenced; bool is_update = false; cqContext *pcqCtx; /* * 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++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ /* * Ready to store new pg_rewrite tuple */ /* * Check to see if we are replacing an existing tuple */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(eventrel_oid), CStringGetDatum(rulname))); oldtup = caql_getnext(pcqCtx); 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 = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { tup = caql_form_tuple(pcqCtx, values, nulls); if (OidIsValid(ruleOid)) HeapTupleSetOid(tup, ruleOid); rewriteObjectId = caql_insert(pcqCtx, tup); /* and Update indexes (implicit) */ } heap_freetuple(tup); /* If replacing, get rid of old dependencies and make new ones */ if (is_update) deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId); /* * 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); } caql_endscan(pcqCtx); return rewriteObjectId; }
/* * Returns pre-defined hidden tuples for pg_proc. */ HeapTuple * GetHiddenPgProcTuples(Relation pg_proc, int *len) { HeapTuple *tuples; Datum values[Natts_pg_proc]; bool nulls[Natts_pg_proc]; MemoryContext oldcontext; static HeapTuple *StaticPgProcTuples = NULL; static int StaticPgProcTupleLen = 0; if (StaticPgProcTuples != NULL) { *len = StaticPgProcTupleLen; return StaticPgProcTuples; } #define N_PGPROC_TUPLES 2 oldcontext = MemoryContextSwitchTo(CacheMemoryContext); tuples = palloc(sizeof(HeapTuple) * N_PGPROC_TUPLES); /* * gp_read_error_log * * CREATE FUNCTION pg_catalog.gp_read_error_log( * text, * cmdtime OUT timestamptz, * relname OUT text, * filename OUT text, * linenum OUT int4, * bytenum OUT int4, * errmsg OUT text, * rawdata OUT text, * rawbytes OUT bytea * ) RETURNS SETOF record AS 'gp_read_error_log' * LANGUAGE internal VOLATILE STRICT <RUN ON SEGMENT>; */ { NameData procname = {"gp_read_error_log"}; Oid proargtypes[] = {TEXTOID}; ArrayType *array; Datum allargtypes[9]; Datum proargmodes[9]; Datum proargnames[9]; MemSet(nulls, false, sizeof(bool) * Natts_pg_proc); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(PG_CATALOG_NAMESPACE); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(BOOTSTRAP_SUPERUSERID); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(INTERNALlanguageId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(false); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(false); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(true); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(true); values[Anum_pg_proc_provolatile - 1] = CharGetDatum('v'); values[Anum_pg_proc_pronargs - 1] = Int16GetDatum(1); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(RECORDOID); values[Anum_pg_proc_proiswin - 1] = BoolGetDatum(false); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(buildoidvector(proargtypes, 1)); allargtypes[0] = TEXTOID; allargtypes[1] = TIMESTAMPTZOID; allargtypes[2] = TEXTOID; allargtypes[3] = TEXTOID; allargtypes[4] = INT4OID; allargtypes[5] = INT4OID; allargtypes[6] = TEXTOID; allargtypes[7] = TEXTOID; allargtypes[8] = BYTEAOID; array = construct_array(allargtypes, 9, OIDOID, 4, true, 'i'); values[Anum_pg_proc_proallargtypes - 1] = PointerGetDatum(array); proargmodes[0] = CharGetDatum('i'); proargmodes[1] = CharGetDatum('o'); proargmodes[2] = CharGetDatum('o'); proargmodes[3] = CharGetDatum('o'); proargmodes[4] = CharGetDatum('o'); proargmodes[5] = CharGetDatum('o'); proargmodes[6] = CharGetDatum('o'); proargmodes[7] = CharGetDatum('o'); proargmodes[8] = CharGetDatum('o'); array = construct_array(proargmodes, 9, CHAROID, 1, true, 'c'); values[Anum_pg_proc_proargmodes - 1] = PointerGetDatum(array); proargnames[0] = CStringGetTextDatum(""); proargnames[1] = CStringGetTextDatum("cmdtime"); proargnames[2] = CStringGetTextDatum("relname"); proargnames[3] = CStringGetTextDatum("filename"); proargnames[4] = CStringGetTextDatum("linenum"); proargnames[5] = CStringGetTextDatum("bytenum"); proargnames[6] = CStringGetTextDatum("errmsg"); proargnames[7] = CStringGetTextDatum("rawdata"); proargnames[8] = CStringGetTextDatum("rawbytes"); array = construct_array(proargnames, 9, TEXTOID, -1, false, 'i'); values[Anum_pg_proc_proargnames - 1] = PointerGetDatum(array); values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum("gp_read_error_log"); values[Anum_pg_proc_probin - 1] = (Datum) 0; nulls[Anum_pg_proc_probin - 1] = true; values[Anum_pg_proc_proacl - 1] = (Datum) 0; nulls[Anum_pg_proc_proacl - 1] = true; /* special data access property, "segment" */ values[Anum_pg_proc_prodataaccess - 1] = CharGetDatum('s'); tuples[0] = heap_form_tuple(RelationGetDescr(pg_proc), values, nulls); ItemPointerSetHidden(&tuples[0]->t_self, F_GP_READ_ERROR_LOG); HeapTupleSetOid(tuples[0], F_GP_READ_ERROR_LOG); } /* * gp_truncate_error_log * * CREATE FUNCTION pg_catalog.gp_truncate_error_log(text) * RETURNS bool AS 'gp_truncate_error_log' * LANGUAGE internal VOLATILE STRICT MODIFIES SQL DATA; */ { NameData procname = {"gp_truncate_error_log"}; Oid proargtypes[] = {TEXTOID}; MemSet(nulls, false, sizeof(bool) * Natts_pg_proc); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(PG_CATALOG_NAMESPACE); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(BOOTSTRAP_SUPERUSERID); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(INTERNALlanguageId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(false); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(false); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(true); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(false); values[Anum_pg_proc_provolatile - 1] = CharGetDatum('v'); values[Anum_pg_proc_pronargs - 1] = Int16GetDatum(1); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(BOOLOID); values[Anum_pg_proc_proiswin - 1] = BoolGetDatum(false); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(buildoidvector(proargtypes, 1)); values[Anum_pg_proc_proallargtypes - 1] = (Datum) 0; nulls[Anum_pg_proc_proallargtypes - 1] = true; values[Anum_pg_proc_proargmodes - 1] = (Datum) 0; nulls[Anum_pg_proc_proargmodes - 1] = true; values[Anum_pg_proc_proargnames - 1] = (Datum) 0; nulls[Anum_pg_proc_proargnames - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum("gp_truncate_error_log"); values[Anum_pg_proc_probin - 1] = (Datum) 0; nulls[Anum_pg_proc_probin - 1] = true; values[Anum_pg_proc_proacl - 1] = (Datum) 0; nulls[Anum_pg_proc_proacl - 1] = true; values[Anum_pg_proc_prodataaccess - 1] = CharGetDatum('m'); tuples[1] = heap_form_tuple(RelationGetDescr(pg_proc), values, nulls); ItemPointerSetHidden(&tuples[1]->t_self, F_GP_TRUNCATE_ERROR_LOG); HeapTupleSetOid(tuples[1], F_GP_TRUNCATE_ERROR_LOG); } StaticPgProcTuples = tuples; StaticPgProcTupleLen = N_PGPROC_TUPLES; *len = StaticPgProcTupleLen; MemoryContextSwitchTo(oldcontext); return tuples; }
/* * EnumValuesCreate * Create an entry in pg_enum for each of the supplied enum values. * * vals is a list of Value strings. */ void EnumValuesCreate(Oid enumTypeOid, List *vals) { Relation pg_enum; TupleDesc tupDesc; NameData enumlabel; Oid *oids; int i, n; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; n = list_length(vals); /* * XXX we do not bother to check the list of values for duplicates --- if * you have any, you'll get a less-than-friendly unique-index violation. * Is it worth trying harder? */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); tupDesc = pg_enum->rd_att; /* * Allocate oids. While this method does not absolutely guarantee that we * generate no duplicate oids (since we haven't entered each oid into the * table before allocating the next), trouble could only occur if the oid * counter wraps all the way around before we finish. Which seems * unlikely. */ oids = (Oid *) palloc(n * sizeof(Oid)); for (i = 0; i < n; i++) { /* * In QE node, however, use the OIDs assigned by the master (they are delivered * out-of-band, see oid_dispatch.c. */ if (Gp_role == GP_ROLE_EXECUTE) oids[i] = InvalidOid; else oids[i] = GetNewOid(pg_enum); } /* sort them, just in case counter wrapped from high to low */ qsort(oids, n, sizeof(Oid), oid_cmp); /* and make the entries */ memset(nulls, false, sizeof(nulls)); i = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(tupDesc, values, nulls); HeapTupleSetOid(tup, oids[i]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); i++; }
/* * EnumValuesCreate * Create an entry in pg_enum for each of the supplied enum values. * * vals is a list of Value strings. */ void EnumValuesCreate(Oid enumTypeOid, List *vals, Oid binary_upgrade_next_pg_enum_oid) { Relation pg_enum; TupleDesc tupDesc; NameData enumlabel; Oid *oids; int elemno, num_elems; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; num_elems = list_length(vals); /* * XXX we do not bother to check the list of values for duplicates --- if * you have any, you'll get a less-than-friendly unique-index violation. * Is it worth trying harder? */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); tupDesc = pg_enum->rd_att; /* * Allocate oids */ oids = (Oid *) palloc(num_elems * sizeof(Oid)); if (OidIsValid(binary_upgrade_next_pg_enum_oid)) { if (num_elems != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EnumValuesCreate() can only set a single OID"))); oids[0] = binary_upgrade_next_pg_enum_oid; binary_upgrade_next_pg_enum_oid = InvalidOid; } else { /* * While this method does not absolutely guarantee that we generate no * duplicate oids (since we haven't entered each oid into the table * before allocating the next), trouble could only occur if the oid * counter wraps all the way around before we finish. Which seems * unlikely. */ for (elemno = 0; elemno < num_elems; elemno++) { /* * The pg_enum.oid is stored in user tables. This oid must be * preserved by binary upgrades. */ oids[elemno] = GetNewOid(pg_enum); } /* sort them, just in case counter wrapped from high to low */ qsort(oids, num_elems, sizeof(Oid), oid_cmp); } /* and make the entries */ memset(nulls, false, sizeof(nulls)); elemno = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(tupDesc, values, nulls); HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); elemno++; }
/* ---------------------------------------------------------------- * TypeCreate * * This does all the necessary work needed to define a new type. * * Returns the OID assigned to the new type. If newTypeOid is * zero (the normal case), a new OID is created; otherwise we * use exactly that OID. * ---------------------------------------------------------------- */ Oid TypeCreate(Oid newTypeOid, const char *typeName, Oid typeNamespace, Oid relationOid, /* only for relation rowtypes */ char relationKind, /* ditto */ Oid ownerId, int16 internalSize, char typeType, char typeCategory, bool typePreferred, char typDelim, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid typmodinProcedure, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, bool isImplicitArray, Oid arrayType, Oid baseType, const char *defaultTypeValue, /* human readable rep */ char *defaultTypeBin, /* cooked rep */ bool passedByValue, char alignment, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, Oid typeCollation) { Relation pg_type_desc; Oid typeObjectId; bool rebuildDeps = false; HeapTuple tup; bool nulls[Natts_pg_type]; bool replaces[Natts_pg_type]; Datum values[Natts_pg_type]; NameData name; int i; Acl *typacl = NULL; /* * We assume that the caller validated the arguments individually, but did * not check for bad combinations. * * Validate size specifications: either positive (fixed-length) or -1 * (varlena) or -2 (cstring). */ if (!(internalSize > 0 || internalSize == -1 || internalSize == -2)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type internal size %d", internalSize))); if (passedByValue) { /* * Pass-by-value types must have a fixed length that is one of the * values supported by fetch_att() and store_att_byval(); and the * alignment had better agree, too. All this code must match * access/tupmacs.h! */ if (internalSize == (int16) sizeof(char)) { if (alignment != 'c') ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d", alignment, internalSize))); } else if (internalSize == (int16) sizeof(int16)) { if (alignment != 's') ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d", alignment, internalSize))); } else if (internalSize == (int16) sizeof(int32)) { if (alignment != 'i') ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d", alignment, internalSize))); } #if SIZEOF_DATUM == 8 else if (internalSize == (int16) sizeof(Datum)) { if (alignment != 'd') ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d", alignment, internalSize))); } #endif else ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("internal size %d is invalid for passed-by-value type", internalSize))); } else { /* varlena types must have int align or better */ if (internalSize == -1 && !(alignment == 'i' || alignment == 'd')) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for variable-length type", alignment))); /* cstring must have char alignment */ if (internalSize == -2 && !(alignment == 'c')) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alignment \"%c\" is invalid for variable-length type", alignment))); } /* Only varlena types can be toasted */ if (storage != 'p' && internalSize != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("fixed-size types must have storage PLAIN"))); /* * initialize arrays needed for heap_form_tuple or heap_modify_tuple */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; replaces[i] = true; values[i] = (Datum) 0; } /* * insert data values */ namestrcpy(&name, typeName); values[Anum_pg_type_typname - 1] = NameGetDatum(&name); values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace); values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_type_typlen - 1] = Int16GetDatum(internalSize); values[Anum_pg_type_typbyval - 1] = BoolGetDatum(passedByValue); values[Anum_pg_type_typtype - 1] = CharGetDatum(typeType); values[Anum_pg_type_typcategory - 1] = CharGetDatum(typeCategory); values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(typePreferred); values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(true); values[Anum_pg_type_typdelim - 1] = CharGetDatum(typDelim); values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(relationOid); values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(elementType); values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(arrayType); values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(inputProcedure); values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(outputProcedure); values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(receiveProcedure); values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(sendProcedure); values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(typmodinProcedure); values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(typmodoutProcedure); values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(analyzeProcedure); values[Anum_pg_type_typalign - 1] = CharGetDatum(alignment); values[Anum_pg_type_typstorage - 1] = CharGetDatum(storage); values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(typeNotNull); values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(baseType); values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod); values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation); /* * initialize the default binary value for this type. Check for nulls of * course. */ if (defaultTypeBin) values[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(defaultTypeBin); else nulls[Anum_pg_type_typdefaultbin - 1] = true; /* * initialize the default value for this type. */ if (defaultTypeValue) values[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultTypeValue); else nulls[Anum_pg_type_typdefault - 1] = true; typacl = get_user_default_acl(ACL_OBJECT_TYPE, ownerId, typeNamespace); if (typacl != NULL) values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl); else nulls[Anum_pg_type_typacl - 1] = true; /* * open pg_type and prepare to insert or update a row. * * NOTE: updating will not work correctly in bootstrap mode; but we don't * expect to be overwriting any shell types in bootstrap mode. */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tup = SearchSysCacheCopy2(TYPENAMENSP, CStringGetDatum(typeName), ObjectIdGetDatum(typeNamespace)); if (HeapTupleIsValid(tup)) { /* * check that the type is not already defined. It may exist as a * shell type, however. */ if (((Form_pg_type) GETSTRUCT(tup))->typisdefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", typeName))); /* * shell type must have been created by same owner */ if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); /* trouble if caller wanted to force the OID */ if (OidIsValid(newTypeOid)) elog(ERROR, "cannot assign new OID to existing shell type"); /* * Okay to update existing shell type tuple */ tup = heap_modify_tuple(tup, RelationGetDescr(pg_type_desc), values, nulls, replaces); simple_heap_update(pg_type_desc, &tup->t_self, tup); typeObjectId = HeapTupleGetOid(tup); rebuildDeps = true; /* get rid of shell type's dependencies */ } else { tup = heap_form_tuple(RelationGetDescr(pg_type_desc), values, nulls); /* Force the OID if requested by caller */ if (OidIsValid(newTypeOid)) HeapTupleSetOid(tup, newTypeOid); /* Use binary-upgrade override for pg_type.oid, if supplied. */ else if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_type_oid)) { HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid); binary_upgrade_next_pg_type_oid = InvalidOid; } /* else allow system to assign oid */ typeObjectId = simple_heap_insert(pg_type_desc, tup); } /* Update indexes */ CatalogUpdateIndexes(pg_type_desc, tup); /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typeObjectId, relationOid, relationKind, ownerId, inputProcedure, outputProcedure, receiveProcedure, sendProcedure, typmodinProcedure, typmodoutProcedure, analyzeProcedure, elementType, isImplicitArray, baseType, typeCollation, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), rebuildDeps); /* Post creation hook for new type */ InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typeObjectId, 0, NULL); /* * finish up */ heap_close(pg_type_desc, RowExclusiveLock); return typeObjectId; }
Oid TypeShellMakeWithOid(const char *typeName, Oid typeNamespace, Oid ownerId, Oid shelltypeOid) { int i; HeapTuple tup; Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; NameData name; cqContext *pcqCtx; Assert(PointerIsValid(typeName)); /* * open pg_type */ pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_type ", NULL)); /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; values[i] = (Datum) 0; /* redundant, but safe */ } /* * initialize *values with the type name and dummy values * * The representational details are the same as int4 ... it doesn't really * matter what they are so long as they are consistent. Also note that we * give it typtype = 'p' (pseudotype) as extra insurance that it won't be * mistaken for a usable type. */ i = 0; namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(ownerId); /* typowner */ values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */ values[i++] = BoolGetDatum(true); /* typbyval */ values[i++] = CharGetDatum('p'); /* typtype */ values[i++] = BoolGetDatum(false); /* typisdefined */ values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */ values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */ values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */ values[i++] = CharGetDatum('i'); /* typalign */ values[i++] = CharGetDatum('p'); /* typstorage */ values[i++] = BoolGetDatum(false); /* typnotnull */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ values[i++] = Int32GetDatum(-1); /* typtypmod */ values[i++] = Int32GetDatum(0); /* typndims */ nulls[i++] = true; /* typdefaultbin */ nulls[i++] = true; /* typdefault */ /* * create a new type tuple */ tup = caql_form_tuple(pcqCtx, values, nulls); /* * MPP: If we are on the QEs, we need to use the same Oid as the QD used */ if (shelltypeOid != InvalidOid) HeapTupleSetOid(tup, shelltypeOid); /* * insert the tuple in the relation and get the tuple's oid. */ typoid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typoid, InvalidOid, 0, ownerId, F_SHELL_IN, F_SHELL_OUT, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, NULL, false); /* * clean up and return the type-oid */ heap_freetuple(tup); caql_endscan(pcqCtx); return typoid; }
/* ---------------------------------------------------------------- * TypeCreate * * This does all the necessary work needed to define a new type. * * Returns the OID assigned to the new type. * ---------------------------------------------------------------- */ Oid TypeCreateWithOid(const char *typeName, Oid typeNamespace, Oid relationOid, /* only for 'c'atalog types */ char relationKind, /* ditto */ Oid ownerId, int16 internalSize, char typeType, char typDelim, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid analyzeProcedure, Oid elementType, Oid baseType, const char *defaultTypeValue, /* human readable rep */ char *defaultTypeBin, /* cooked rep */ bool passedByValue, char alignment, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, Oid newtypeOid, Datum typoptions) { Relation pg_type_desc; Oid typeObjectId; bool rebuildDeps = false; HeapTuple tup; bool nulls[Natts_pg_type]; bool replaces[Natts_pg_type]; Datum values[Natts_pg_type]; NameData name; int i; cqContext *pcqCtx; cqContext cqc; /* * We assume that the caller validated the arguments individually, but did * not check for bad combinations. * * Validate size specifications: either positive (fixed-length) or -1 * (varlena) or -2 (cstring). Pass-by-value types must have a fixed * length not more than sizeof(Datum). */ if (!(internalSize > 0 || internalSize == -1 || internalSize == -2)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type internal size %d", internalSize))); if (passedByValue && (internalSize <= 0 || internalSize > (int16) sizeof(Datum))) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("internal size %d is invalid for passed-by-value type", internalSize))); /* Only varlena types can be toasted */ if (storage != 'p' && internalSize != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("fixed-size types must have storage PLAIN"))); /* * initialize arrays needed for heap_form_tuple or heap_modify_tuple */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; replaces[i] = true; values[i] = (Datum) 0; } /* * initialize the *values information */ i = 0; namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(ownerId); /* typowner */ values[i++] = Int16GetDatum(internalSize); /* typlen */ values[i++] = BoolGetDatum(passedByValue); /* typbyval */ values[i++] = CharGetDatum(typeType); /* typtype */ values[i++] = BoolGetDatum(true); /* typisdefined */ values[i++] = CharGetDatum(typDelim); /* typdelim */ values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(elementType); /* typelem */ values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */ values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */ values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */ values[i++] = ObjectIdGetDatum(sendProcedure); /* typsend */ values[i++] = ObjectIdGetDatum(analyzeProcedure); /* typanalyze */ values[i++] = CharGetDatum(alignment); /* typalign */ values[i++] = CharGetDatum(storage); /* typstorage */ values[i++] = BoolGetDatum(typeNotNull); /* typnotnull */ values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */ values[i++] = Int32GetDatum(typeMod); /* typtypmod */ values[i++] = Int32GetDatum(typNDims); /* typndims */ /* * initialize the default binary value for this type. Check for nulls of * course. */ if (defaultTypeBin) values[i] = CStringGetTextDatum(defaultTypeBin); else nulls[i] = true; i++; /* typdefaultbin */ /* * initialize the default value for this type. */ if (defaultTypeValue) values[i] = CStringGetTextDatum(defaultTypeValue); else nulls[i] = true; i++; /* typdefault */ /* * open pg_type and prepare to insert or update a row. * * NOTE: updating will not work correctly in bootstrap mode; but we don't * expect to be overwriting any shell types in bootstrap mode. */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); pcqCtx = caql_addrel(cqclr(&cqc), pg_type_desc); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_type " " WHERE typname = :1 " " AND typnamespace = :2 " " FOR UPDATE ", CStringGetDatum((char *) typeName), ObjectIdGetDatum(typeNamespace))); if (HeapTupleIsValid(tup)) { /* * check that the type is not already defined. It may exist as a * shell type, however. */ if (((Form_pg_type) GETSTRUCT(tup))->typisdefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", typeName))); /* * shell type must have been created by same owner */ if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); /* * Okay to update existing shell type tuple */ tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ typeObjectId = HeapTupleGetOid(tup); rebuildDeps = true; /* get rid of shell type's dependencies */ } else { tup = caql_form_tuple(pcqCtx, values, nulls); if (newtypeOid != InvalidOid) { elog(DEBUG5," Setting Oid in new pg_type tuple"); HeapTupleSetOid(tup, newtypeOid); } else if (Gp_role == GP_ROLE_EXECUTE) elog(ERROR," newtypeOid NULL"); /* Insert tuple into the relation */ typeObjectId = caql_insert(pcqCtx, tup); /* and Update indexes (implicit) */ } /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typeObjectId, relationOid, relationKind, ownerId, inputProcedure, outputProcedure, receiveProcedure, sendProcedure, analyzeProcedure, elementType, baseType, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), rebuildDeps); /* * finish up with pg_type */ heap_close(pg_type_desc, RowExclusiveLock); /* now pg_type_encoding */ if (DatumGetPointer(typoptions) != NULL) add_type_encoding(typeObjectId, typoptions); return typeObjectId; }
/* * Executes INSERT and DELETE DML operations. The * action is specified within the TupleTableSlot at * plannode->actionColIdx.The ctid of the tuple to delete * is in position plannode->ctidColIdx in the current slot. * */ TupleTableSlot* ExecDML(DMLState *node) { PlanState *outerNode = outerPlanState(node); DML *plannode = (DML *) node->ps.plan; Assert(outerNode != NULL); TupleTableSlot *slot = ExecProcNode(outerNode); if (TupIsNull(slot)) { return NULL; } bool isnull = false; int action = DatumGetUInt32(slot_getattr(slot, plannode->actionColIdx, &isnull)); Assert(!isnull); isnull = false; Datum oid = slot_getattr(slot, plannode->oidColIdx, &isnull); slot->tts_tableOid = DatumGetUInt32(oid); bool isUpdate = false; if (node->ps.state->es_plannedstmt->commandType == CMD_UPDATE) { isUpdate = true; } Assert(action == DML_INSERT || action == DML_DELETE); /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ ExprContext *econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* Prepare cleaned-up tuple by projecting it and filtering junk columns */ econtext->ecxt_outertuple = slot; TupleTableSlot *projectedSlot = ExecProject(node->ps.ps_ProjInfo, NULL); /* remove 'junk' columns from tuple */ node->cleanedUpSlot = ExecFilterJunk(node->junkfilter, projectedSlot); if (DML_INSERT == action) { /* Respect any given tuple Oid when updating a tuple. */ if(isUpdate && plannode->tupleoidColIdx != 0) { isnull = false; oid = slot_getattr(slot, plannode->tupleoidColIdx, &isnull); HeapTuple htuple = ExecFetchSlotHeapTuple(node->cleanedUpSlot); Assert(htuple == node->cleanedUpSlot->PRIVATE_tts_heaptuple); HeapTupleSetOid(htuple, oid); } /* The plan origin is required since ExecInsert performs different actions * depending on the type of plan (constraint enforcement and triggers.) */ ExecInsert(node->cleanedUpSlot, NULL /* destReceiver */, node->ps.state, PLANGEN_OPTIMIZER /* Plan origin */, isUpdate); } else /* DML_DELETE */ { Datum ctid = slot_getattr(slot, plannode->ctidColIdx, &isnull); Assert(!isnull); ItemPointer tupleid = (ItemPointer) DatumGetPointer(ctid); ItemPointerData tuple_ctid = *tupleid; tupleid = &tuple_ctid; /* Correct tuple count by ignoring deletes when splitting tuples. */ ExecDelete(tupleid, node->cleanedUpSlot, NULL /* DestReceiver */, node->ps.state, PLANGEN_OPTIMIZER /* Plan origin */, isUpdate); } return slot; }
Oid OperatorCreateWithOid(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, List *procedureName, List *commutatorName, List *negatorName, List *restrictionName, List *joinName, bool canMerge, bool canHash, Oid newOid) { Relation pg_operator_desc; HeapTuple tup; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; Oid procOid; Oid operResultType; Oid commutatorId, negatorId, restOid, joinOid; bool selfCommutator = false; Oid typeId[4]; /* only need up to 4 args here */ int nargs; NameData oname; int i; cqContext cqc; cqContext *pcqCtx; /* * Sanity checks */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); if (!OidIsValid(leftTypeId) && !OidIsValid(rightTypeId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("at least one of leftarg or rightarg must be specified"))); if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) { /* If it's not a binary op, these things mustn't be set: */ if (commutatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have commutators"))); if (joinName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have join selectivity"))); if (canMerge) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can merge join"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can hash"))); } operatorObjectId = OperatorGet(operatorName, operatorNamespace, leftTypeId, rightTypeId, &operatorAlreadyDefined); if (operatorAlreadyDefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("operator %s already exists", operatorName))); /* * At this point, if operatorObjectId is not InvalidOid then we are * filling in a previously-created shell. */ /* * Look up registered procedures -- find the return type of procedureName * to place in "result" field. Do this before shells are created so we * don't have to worry about deleting them later. */ if (!OidIsValid(leftTypeId)) { typeId[0] = rightTypeId; nargs = 1; } else if (!OidIsValid(rightTypeId)) { typeId[0] = leftTypeId; nargs = 1; } else { typeId[0] = leftTypeId; typeId[1] = rightTypeId; nargs = 2; } procOid = LookupFuncName(procedureName, nargs, typeId, false); operResultType = get_func_rettype(procOid); /* * find restriction estimator */ if (restrictionName) { typeId[0] = INTERNALOID; /* Query */ typeId[1] = OIDOID; /* operator OID */ typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT4OID; /* varRelid */ restOid = LookupFuncName(restrictionName, 4, typeId, false); } else restOid = InvalidOid; /* * find join estimator */ if (joinName) { typeId[0] = INTERNALOID; /* Query */ typeId[1] = OIDOID; /* operator OID */ typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT2OID; /* jointype */ joinOid = LookupFuncName(joinName, 4, typeId, false); } else joinOid = InvalidOid; /* * set up values in the operator tuple */ for (i = 0; i < Natts_pg_operator; ++i) { values[i] = 0; replaces[i] = true; nulls[i] = false; } i = 0; namestrcpy(&oname, operatorName); values[i++] = NameGetDatum(&oname); /* oprname */ values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */ values[i++] = ObjectIdGetDatum(GetUserId()); /* oprowner */ values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */ values[i++] = BoolGetDatum(canMerge); /* oprcanmerge */ values[i++] = BoolGetDatum(canHash); /* oprcanhash */ values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */ values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */ values[i++] = ObjectIdGetDatum(operResultType); /* oprresult */ /* * Set up the other operators. If they do not currently exist, create * shells in order to get ObjectId's. */ if (commutatorName) { /* commutator has reversed arg types */ commutatorId = get_other_operator(commutatorName, rightTypeId, leftTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, true); /* * self-linkage to this operator; will fix below. Note that only * self-linkage for commutation makes sense. */ if (!OidIsValid(commutatorId)) selfCommutator = true; } else commutatorId = InvalidOid; values[i++] = ObjectIdGetDatum(commutatorId); /* oprcom */ if (negatorName) { /* negator has same arg types */ negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else negatorId = InvalidOid; values[i++] = ObjectIdGetDatum(negatorId); /* oprnegate */ values[i++] = ObjectIdGetDatum(procOid); /* oprcode */ values[i++] = ObjectIdGetDatum(restOid); /* oprrest */ values[i++] = ObjectIdGetDatum(joinOid); /* oprjoin */ pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); pcqCtx = caql_addrel(cqclr(&cqc), pg_operator_desc); /* * If we are replacing an operator shell, update; else insert */ if (operatorObjectId) { tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_operator " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(operatorObjectId))); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for operator %u", operatorObjectId); tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ } else { tup = caql_form_tuple(pcqCtx, values, nulls); if (newOid != (Oid) 0) HeapTupleSetOid(tup, newOid); operatorObjectId = caql_insert(pcqCtx, tup); /* and Update indexes (implicit) */ } /* Add dependencies for the entry */ makeOperatorDependencies(tup); heap_close(pg_operator_desc, RowExclusiveLock); /* * If a commutator and/or negator link is provided, update the other * operator(s) to point at this one, if they don't already have a link. * This supports an alternative style of operator definition wherein the * user first defines one operator without giving negator or commutator, * then defines the other operator of the pair with the proper commutator * or negator attribute. That style doesn't require creation of a shell, * and it's the only style that worked right before Postgres version 6.5. * This code also takes care of the situation where the new operator is * its own commutator. */ if (selfCommutator) commutatorId = operatorObjectId; if (OidIsValid(commutatorId) || OidIsValid(negatorId)) OperatorUpd(operatorObjectId, commutatorId, negatorId); return operatorObjectId; }
/* ---------------- * NamespaceCreate * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid forceOid) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; int i; cqContext cqc; cqContext cqc2; cqContext *pcqCtx; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), nspdesc), cql("INSERT INTO pg_namespace", NULL)); /* make sure there is no existing namespace of same name in the current database */ if (caql_getcount( caql_addrel(cqclr(&cqc2), nspdesc), cql("SELECT COUNT(*) FROM pg_namespace " " WHERE nspname = :1 and nspdboid = :2", PointerGetDatum((char *) nspName), ObjectIdGetDatum((Oid) NSPDBOID_CURRENT)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); } /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); nulls[Anum_pg_namespace_nspacl - 1] = true; values[Anum_pg_namespace_nspdboid - 1] = ObjectIdGetDatum((Oid) NSPDBOID_CURRENT); tup = caql_form_tuple(pcqCtx, values, nulls); if (forceOid != InvalidOid) HeapTupleSetOid(tup, forceOid); /* override heap_insert's OID * selection */ /* insert a new tuple */ nspoid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ Assert(OidIsValid(nspoid)); caql_endscan(pcqCtx); heap_close(nspdesc, RowExclusiveLock); /* Record dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); return nspoid; }
/* ---------------------------------------------------------------- * TypeShellMake * * This procedure inserts a "shell" tuple into the pg_type relation. * The type tuple inserted has valid but dummy values, and its * "typisdefined" field is false indicating it's not really defined. * * This is used so that a tuple exists in the catalogs. The I/O * functions for the type will link to this tuple. When the full * CREATE TYPE command is issued, the bogus values will be replaced * with correct ones, and "typisdefined" will be set to true. * ---------------------------------------------------------------- */ Oid TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId, Oid shelloid) { Relation pg_type_desc; TupleDesc tupDesc; int i; HeapTuple tup; Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; NameData name; Assert(PointerIsValid(typeName)); /* * open pg_type */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tupDesc = pg_type_desc->rd_att; /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; values[i] = (Datum) 0; /* redundant, but safe */ } /* * initialize *values with the type name and dummy values * * The representational details are the same as int4 ... it doesn't really * matter what they are so long as they are consistent. Also note that we * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be * mistaken for a usable type. */ i = 0; namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(ownerId); /* typowner */ values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */ values[i++] = BoolGetDatum(true); /* typbyval */ values[i++] = CharGetDatum(TYPTYPE_PSEUDO); /* typtype */ values[i++] = BoolGetDatum(false); /* typisdefined */ values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typarray */ values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */ values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodin */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodout */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */ values[i++] = CharGetDatum('i'); /* typalign */ values[i++] = CharGetDatum('p'); /* typstorage */ values[i++] = BoolGetDatum(false); /* typnotnull */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ values[i++] = Int32GetDatum(-1); /* typtypmod */ values[i++] = Int32GetDatum(0); /* typndims */ nulls[i++] = true; /* typdefaultbin */ nulls[i++] = true; /* typdefault */ /* * create a new type tuple */ tup = heap_form_tuple(tupDesc, values, nulls); /* * MPP: If we are on the QEs, we need to use the same Oid as the QD used */ if (shelloid != InvalidOid) HeapTupleSetOid(tup, shelloid); /* * insert the tuple in the relation and get the tuple's oid. */ typoid = simple_heap_insert(pg_type_desc, tup); CatalogUpdateIndexes(pg_type_desc, tup); /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typoid, InvalidOid, 0, ownerId, F_SHELL_IN, F_SHELL_OUT, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, false, InvalidOid, NULL, false); /* * clean up and return the type-oid */ heap_freetuple(tup); heap_close(pg_type_desc, RowExclusiveLock); return typoid; }
/* * Guts of language creation. */ static void create_proc_lang(const char *languageName, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted, Oid *plangoid) { Relation rel; TupleDesc tupDesc; Datum values[Natts_pg_language]; bool nulls[Natts_pg_language]; NameData langname; HeapTuple tup; ObjectAddress myself, referenced; /* * Insert the new language into pg_language */ rel = heap_open(LanguageRelationId, RowExclusiveLock); tupDesc = rel->rd_att; memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); namestrcpy(&langname, languageName); values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname); values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner); values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true); values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted); values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid); values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid); values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid); nulls[Anum_pg_language_lanacl - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); /* Keep oids synchronized between master and segments */ if (OidIsValid(*plangoid)) HeapTupleSetOid(tup, *plangoid); *plangoid = simple_heap_insert(rel, tup); CatalogUpdateIndexes(rel, tup); /* * Create dependencies for language */ myself.classId = LanguageRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; /* dependency on owner of language */ referenced.classId = AuthIdRelationId; referenced.objectId = languageOwner; referenced.objectSubId = 0; recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER); /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; referenced.objectId = handlerOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on the inline handler function, if any */ if (OidIsValid(inlineOid)) { referenced.classId = ProcedureRelationId; referenced.objectId = inlineOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on the validator function, if any */ if (OidIsValid(valOid)) { referenced.classId = ProcedureRelationId; referenced.objectId = valOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } heap_close(rel, RowExclusiveLock); }
/* * EnumValuesCreate * Create an entry in pg_enum for each of the supplied enum values. * * vals is a list of Value strings. */ void EnumValuesCreate(Oid enumTypeOid, List *vals) { Relation pg_enum; NameData enumlabel; Oid *oids; int elemno, num_elems; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; num_elems = list_length(vals); /* * We do not bother to check the list of values for duplicates --- if you * have any, you'll get a less-than-friendly unique-index violation. It is * probably not worth trying harder. */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); /* * Allocate OIDs for the enum's members. * * While this method does not absolutely guarantee that we generate no * duplicate OIDs (since we haven't entered each oid into the table before * allocating the next), trouble could only occur if the OID counter wraps * all the way around before we finish. Which seems unlikely. */ oids = (Oid *) palloc(num_elems * sizeof(Oid)); for (elemno = 0; elemno < num_elems; elemno++) { /* * We assign even-numbered OIDs to all the new enum labels. This * tells the comparison functions the OIDs are in the correct sort * order and can be compared directly. */ Oid new_oid; do { new_oid = GetNewOid(pg_enum); } while (new_oid & 1); oids[elemno] = new_oid; } /* sort them, just in case OID counter wrapped from high to low */ qsort(oids, num_elems, sizeof(Oid), oid_cmp); /* and make the entries */ memset(nulls, false, sizeof(nulls)); elemno = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); elemno++; }
Datum caql_insert_to_in_memory_pg_namespace(PG_FUNCTION_ARGS) { Oid nspid = PG_GETARG_OID(0); char *nspname = text_to_cstring(PG_GETARG_TEXT_P(1)); Oid nspdboid = PG_GETARG_OID(2); HeapTuple tuple; Datum values[Natts_pg_namespace]; bool nulls[Natts_pg_namespace]; /* initialize nulls and values */ for (int i = 0; i < Natts_pg_namespace; i++) { nulls[i] = true; values[i] = (Datum) 0; } NameData name; namestrcpy(&name, nspname); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&name); values[Anum_pg_namespace_nspdboid - 1] = ObjectIdGetDatum(nspdboid); nulls[Anum_pg_namespace_nspname - 1] = false; nulls[Anum_pg_namespace_nspdboid - 1] = false; cqContext *pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_namespace", NULL)); HeapTuple tup = caql_form_tuple(pcqCtx, values, nulls); HeapTupleSetOid(tup, nspid); caql_insert_inmem(pcqCtx, tup); caql_endscan(pcqCtx); StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "inserted tuple to pg_namespace (oid %d, nspname %s, nspdboid %d)", nspid, nspname, nspdboid); pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_namespace " "WHERE nspname = :1", CStringGetDatum((char *) nspname))); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { Oid thisoid = HeapTupleGetOid(tuple); bool isNull; Datum data = caql_getattr(pcqCtx, Anum_pg_namespace_nspdboid, &isNull); Oid namespacedboid = DatumGetObjectId(data); data = caql_getattr(pcqCtx, Anum_pg_namespace_nspname, &isNull); char *namespacename = DatumGetCString(data); appendStringInfo(&buf, "\nresult tuple: (oid %d, nspname %s, nspdboid %d)", thisoid, namespacename, namespacedboid); } caql_endscan(pcqCtx); PG_RETURN_TEXT_P(cstring_to_text(buf.data)); }
/* ---------------------------------------------------------------- * TypeShellMake * * This procedure inserts a "shell" tuple into the pg_type relation. * The type tuple inserted has valid but dummy values, and its * "typisdefined" field is false indicating it's not really defined. * * This is used so that a tuple exists in the catalogs. The I/O * functions for the type will link to this tuple. When the full * CREATE TYPE command is issued, the bogus values will be replaced * with correct ones, and "typisdefined" will be set to true. * ---------------------------------------------------------------- */ Oid TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) { Relation pg_type_desc; TupleDesc tupDesc; int i; HeapTuple tup; Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; NameData name; Assert(PointerIsValid(typeName)); /* * open pg_type */ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tupDesc = pg_type_desc->rd_att; /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_type; ++i) { nulls[i] = false; values[i] = (Datum) NULL; /* redundant, but safe */ } /* * initialize *values with the type name and dummy values * * The representational details are the same as int4 ... it doesn't really * matter what they are so long as they are consistent. Also note that we * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be * mistaken for a usable type. */ namestrcpy(&name, typeName); values[Anum_pg_type_typname - 1] = NameGetDatum(&name); values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(typeNamespace); values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_type_typlen - 1] = Int16GetDatum(sizeof(int32)); values[Anum_pg_type_typbyval - 1] = BoolGetDatum(true); values[Anum_pg_type_typtype - 1] = CharGetDatum(TYPTYPE_PSEUDO); values[Anum_pg_type_typcategory - 1] = CharGetDatum(TYPCATEGORY_PSEUDOTYPE); values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(false); values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(false); values[Anum_pg_type_typdelim - 1] = CharGetDatum(DEFAULT_TYPDELIM); values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(F_SHELL_IN); values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(F_SHELL_OUT); values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typalign - 1] = CharGetDatum('i'); values[Anum_pg_type_typstorage - 1] = CharGetDatum('p'); values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(false); values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1); values[Anum_pg_type_typndims - 1] = Int32GetDatum(0); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid); nulls[Anum_pg_type_typdefaultbin - 1] = true; nulls[Anum_pg_type_typdefault - 1] = true; nulls[Anum_pg_type_typacl - 1] = true; /* * create a new type tuple */ tup = heap_form_tuple(tupDesc, values, nulls); /* Use binary-upgrade override for pg_type.oid, if supplied. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_type_oid)) { HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid); binary_upgrade_next_pg_type_oid = InvalidOid; } /* * insert the tuple in the relation and get the tuple's oid. */ typoid = simple_heap_insert(pg_type_desc, tup); CatalogUpdateIndexes(pg_type_desc, tup); /* * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) GenerateTypeDependencies(typeNamespace, typoid, InvalidOid, 0, ownerId, F_SHELL_IN, F_SHELL_OUT, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, InvalidOid, false, InvalidOid, InvalidOid, NULL, false); /* Post creation hook for new shell type */ InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typoid, 0, NULL); /* * clean up and return the type-oid */ heap_freetuple(tup); heap_close(pg_type_desc, RowExclusiveLock); return typoid; }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { Relation rel; Relation filespaceRel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; Oid filespaceoid; Oid ownerId; TablespaceDirNode tablespaceDirNode; ItemPointerData persistentTid; int64 persistentSerialNum; cqContext cqc; cqContext *pcqCtx; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); else ownerId = GetUserId(); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableModsDDL && IsReservedName(stmt->tablespacename)) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"%s\" is reserved for system tablespaces.", GetReservedPrefix(stmt->tablespacename)))); } /* * Check the specified filespace */ filespaceRel = heap_open(FileSpaceRelationId, RowShareLock); filespaceoid = get_filespace_oid(filespaceRel, stmt->filespacename); heap_close(filespaceRel, NoLock); /* hold lock until commit/abort */ if (!OidIsValid(filespaceoid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filespace \"%s\" does not exist", stmt->filespacename))); /* * Filespace pg_system is reserved for system use: * - Used for pg_global and pg_default tablespaces only * * Directory layout is slightly different for the system filespace. * Instead of having subdirectories for individual tablespaces instead * the two system tablespaces have specific locations within it: * pg_global : $PG_SYSTEM/global/relfilenode * pg_default : $PG_SYSTEM/base/dboid/relfilenode * * In other words PG_SYSTEM points to the segments "datadir", or in * postgres vocabulary $PGDATA. * */ if (filespaceoid == SYSTEMFILESPACE_OID && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("filespace %s is reserved for system use", stmt->filespacename))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_tablespace", NULL)); MemSet(nulls, true, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spcfsoid - 1] = ObjectIdGetDatum(filespaceoid); nulls[Anum_pg_tablespace_spcname - 1] = false; nulls[Anum_pg_tablespace_spcowner - 1] = false; nulls[Anum_pg_tablespace_spcfsoid - 1] = false; tuple = caql_form_tuple(pcqCtx, values, nulls); /* Keep oids synchonized between master and segments */ if (OidIsValid(stmt->tsoid)) HeapTupleSetOid(tuple, stmt->tsoid); tablespaceoid = caql_insert(pcqCtx, tuple); /* and Update indexes (implicit) */ heap_freetuple(tuple); /* We keep the lock on pg_tablespace until commit */ caql_endscan(pcqCtx); heap_close(rel, NoLock); /* Create the persistent directory for the tablespace */ tablespaceDirNode.tablespace = tablespaceoid; tablespaceDirNode.filespace = filespaceoid; MirroredFileSysObj_TransactionCreateTablespaceDir( &tablespaceDirNode, &persistentTid, &persistentSerialNum); /* * Record dependency on owner * * We do not record the dependency on pg_filespace because we do not track * dependencies between shared objects. Additionally the pg_tablespace * table itself contains the foreign key back to pg_filespace and can be * used to fulfill the same purpose that an entry in pg_shdepend would. */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* * Create the PG_VERSION file in the target directory. This has several * purposes: to make sure we can write in the directory, to prevent * someone from creating another tablespace pointing at the same directory * (the emptiness check above will fail), and to label tablespace * directories by PG version. */ // set_short_version(sublocation); if (Gp_role == GP_ROLE_DISPATCH) { stmt->tsoid = tablespaceoid; CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NULL); /* MPP-6929: metadata tracking */ MetaTrackAddObject(TableSpaceRelationId, tablespaceoid, GetUserId(), "CREATE", "TABLESPACE" ); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); }
HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, Datum *Values, const char *Nulls) { MemoryContext oldcxt = NULL; HeapTuple mtuple; int numberOfAttributes; Datum *v; char *n; int i; if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL) { SPI_result = SPI_ERROR_ARGUMENT; return NULL; } if (_SPI_curid + 1 == _SPI_connected) /* connected */ { if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) elog(ERROR, "SPI stack corrupted"); oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); } SPI_result = 0; numberOfAttributes = rel->rd_att->natts; v = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); n = (char *) palloc(numberOfAttributes * sizeof(char)); /* fetch old values and nulls */ heap_deformtuple(tuple, rel->rd_att, v, n); /* replace values and nulls */ for (i = 0; i < natts; i++) { if (attnum[i] <= 0 || attnum[i] > numberOfAttributes) break; v[attnum[i] - 1] = Values[i]; n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' '; } if (i == natts) /* no errors in *attnum */ { mtuple = heap_formtuple(rel->rd_att, v, n); /* * copy the identification info of the old tuple: t_ctid, t_self, and * OID (if any) */ mtuple->t_data->t_ctid = tuple->t_data->t_ctid; mtuple->t_self = tuple->t_self; mtuple->t_tableOid = tuple->t_tableOid; if (rel->rd_att->tdhasoid) HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); } else { mtuple = NULL; SPI_result = SPI_ERROR_NOATTRIBUTE; } pfree(v); pfree(n); if (oldcxt) MemoryContextSwitchTo(oldcxt); return mtuple; }
/* * CreateConstraintEntry * Create a constraint table entry. * * Subsidiary records (such as triggers or indexes to implement the * constraint) are *not* created here. But we do make dependency links * from the constraint to the things it depends on. */ Oid CreateConstraintEntry(const char *constraintName, Oid conOid, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, Oid relId, const int16 *constraintKey, int constraintNKeys, Oid domainId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, Oid indexRelId, Node *conExpr, const char *conBin, const char *conSrc) { Relation conDesc; HeapTuple tup; bool nulls[Natts_pg_constraint]; Datum values[Natts_pg_constraint]; ArrayType *conkeyArray; ArrayType *confkeyArray; ArrayType *conpfeqopArray; ArrayType *conppeqopArray; ArrayType *conffeqopArray; NameData cname; int i; ObjectAddress conobject; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); Assert(constraintName); namestrcpy(&cname, constraintName); /* * Convert C arrays into Postgres arrays. */ if (constraintNKeys > 0) { Datum *conkey; conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum)); for (i = 0; i < constraintNKeys; i++) conkey[i] = Int16GetDatum(constraintKey[i]); conkeyArray = construct_array(conkey, constraintNKeys, INT2OID, 2, true, 's'); } else conkeyArray = NULL; if (foreignNKeys > 0) { Datum *fkdatums; fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum)); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = Int16GetDatum(foreignKey[i]); confkeyArray = construct_array(fkdatums, foreignNKeys, INT2OID, 2, true, 's'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]); conpfeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]); conppeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); for (i = 0; i < foreignNKeys; i++) fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]); conffeqopArray = construct_array(fkdatums, foreignNKeys, OIDOID, sizeof(Oid), true, 'i'); } else { confkeyArray = NULL; conpfeqopArray = NULL; conppeqopArray = NULL; conffeqopArray = NULL; } /* initialize nulls and values */ for (i = 0; i < Natts_pg_constraint; i++) { nulls[i] = false; values[i] = (Datum) 0; } values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname); values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace); values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType); values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable); values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred); values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId); values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId); values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId); values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType); values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType); values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType); if (conkeyArray) values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray); else nulls[Anum_pg_constraint_conkey - 1] = true; if (confkeyArray) values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray); else nulls[Anum_pg_constraint_confkey - 1] = true; if (conpfeqopArray) values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray); else nulls[Anum_pg_constraint_conpfeqop - 1] = 'n'; if (conppeqopArray) values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray); else nulls[Anum_pg_constraint_conppeqop - 1] = 'n'; if (conffeqopArray) values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray); else nulls[Anum_pg_constraint_conffeqop - 1] = 'n'; /* * initialize the binary form of the check constraint. */ if (conBin) values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin, CStringGetDatum((char *) conBin)); else nulls[Anum_pg_constraint_conbin - 1] = true; /* * initialize the text form of the check constraint */ if (conSrc) values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin, CStringGetDatum((char *) conSrc)); else nulls[Anum_pg_constraint_consrc - 1] = true; tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls); /* force tuple to have the desired OID */ if (OidIsValid(conOid)) HeapTupleSetOid(tup, conOid); conOid = simple_heap_insert(conDesc, tup); /* update catalog indexes */ CatalogUpdateIndexes(conDesc, tup); conobject.classId = ConstraintRelationId; conobject.objectId = conOid; conobject.objectSubId = 0; heap_close(conDesc, RowExclusiveLock); if (OidIsValid(relId)) { /* * Register auto dependency from constraint to owning relation, or to * specific column(s) if any are mentioned. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = relId; if (constraintNKeys > 0) { for (i = 0; i < constraintNKeys; i++) { relobject.objectSubId = constraintKey[i]; recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); } } else { relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO); } } if (OidIsValid(domainId)) { /* * Register auto dependency from constraint to owning domain */ ObjectAddress domobject; domobject.classId = TypeRelationId; domobject.objectId = domainId; domobject.objectSubId = 0; recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO); } if (OidIsValid(foreignRelId)) { /* * Register normal dependency from constraint to foreign relation, or * to specific column(s) if any are mentioned. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = foreignRelId; if (foreignNKeys > 0) { for (i = 0; i < foreignNKeys; i++) { relobject.objectSubId = foreignKey[i]; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } } else { relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } } if (OidIsValid(indexRelId)) { /* * Register normal dependency on the unique index that supports a * foreign-key constraint. */ ObjectAddress relobject; relobject.classId = RelationRelationId; relobject.objectId = indexRelId; relobject.objectSubId = 0; recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } if (foreignNKeys > 0) { /* * Register normal dependencies on the equality operators that support * a foreign-key constraint. If the PK and FK types are the same then * all three operators for a column are the same; otherwise they are * different. */ ObjectAddress oprobject; oprobject.classId = OperatorRelationId; oprobject.objectSubId = 0; for (i = 0; i < foreignNKeys; i++) { oprobject.objectId = pfEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); if (ppEqOp[i] != pfEqOp[i]) { oprobject.objectId = ppEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); } if (ffEqOp[i] != pfEqOp[i]) { oprobject.objectId = ffEqOp[i]; recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); } } } if (conExpr != NULL) { /* * Register dependencies from constraint to objects mentioned in CHECK * expression. */ recordDependencyOnSingleRelExpr(&conobject, conExpr, relId, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL); } return conOid; }