/* convert schema type from old dboid to newdboid */ bool convert_schema_type(char *schema, Oid oldDboid, Oid newDboid) { Assert(NSPDBOID_CURRENT == oldDboid || NSPDBOID_CURRENT == newDboid); Assert(HcatalogDbOid == oldDboid || HcatalogDbOid == newDboid); Assert(newDboid != oldDboid); /* create tuples for pg_class table */ HeapTuple nsptup = NULL; HeapTuple copytup = NULL; cqContext cqc; Assert(Gp_role != GP_ROLE_EXECUTE); Relation rel = heap_open(NamespaceRelationId, RowExclusiveLock); cqContext *pcqCtx = caql_addrel(cqclr(&cqc), rel); nsptup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_namespace " " WHERE nspname = :1 and nspdboid = :2", CStringGetDatum(schema), ObjectIdGetDatum(oldDboid))); if (!HeapTupleIsValid(nsptup)) { heap_close(rel, NoLock); return false; } Datum values[Natts_pg_namespace]; bool nulls[Natts_pg_namespace]; bool replaces[Natts_pg_namespace]; MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); MemSet(replaces, false, sizeof(replaces)); replaces[Anum_pg_namespace_nspdboid - 1] = true; values[Anum_pg_namespace_nspdboid - 1] = newDboid; copytup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, copytup); heap_close(rel, NoLock); heap_freetuple(copytup); return true; }
/* 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 */
/* * 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; }
/* * Sets the policy of a table into the gp_distribution_policy table * from a GpPolicy structure. * */ void GpPolicyReplace(Oid tbloid, const GpPolicy *policy) { Relation gp_policy_rel; HeapTuple gp_policy_tuple = NULL; cqContext cqc; cqContext *pcqCtx; ArrayType *attrnums; bool nulls[2]; Datum values[2]; bool repl[2]; Insist(policy->ptype == POLICYTYPE_PARTITIONED); /* * Open and lock the gp_distribution_policy catalog. */ gp_policy_rel = heap_open(GpPolicyRelationId, RowExclusiveLock); pcqCtx = caql_addrel(cqclr(&cqc), gp_policy_rel); /* * Convert C arrays into Postgres arrays. */ if (policy->nattrs > 0) { int i; Datum *akey; akey = (Datum *) palloc(policy->nattrs * sizeof(Datum)); for (i = 0; i < policy->nattrs; i++) akey[i] = Int16GetDatum(policy->attrs[i]); attrnums = construct_array(akey, policy->nattrs, INT2OID, 2, true, 's'); } else { attrnums = NULL; } nulls[0] = false; nulls[1] = false; values[0] = ObjectIdGetDatum(tbloid); if (attrnums) values[1] = PointerGetDatum(attrnums); else nulls[1] = true; repl[0] = false; repl[1] = true; /* * Select by value of the localoid field */ gp_policy_tuple = caql_getfirst( pcqCtx, cql("SELECT * FROM gp_distribution_policy " " WHERE localoid = :1 " " FOR UPDATE ", ObjectIdGetDatum(tbloid))); /* * Read first (and only ) tuple */ if (HeapTupleIsValid(gp_policy_tuple)) { HeapTuple newtuple = caql_modify_current(pcqCtx, values, nulls, repl); caql_update_current(pcqCtx, newtuple); /* and Update indexes (implicit) */ heap_freetuple(newtuple); } else { gp_policy_tuple = caql_form_tuple(pcqCtx, values, nulls); caql_insert(pcqCtx, gp_policy_tuple); /* and Update indexes (implicit) */ } /* * Close the gp_distribution_policy relcache entry without unlocking. * We have updated the catalog: consequently the lock must be held until * end of transaction. */ heap_close(gp_policy_rel, NoLock); } /* GpPolicyReplace */
/* * Change tablespace owner */ void AlterTableSpaceOwner(const char *name, Oid newOwnerId) { Relation rel; Oid tablespaceoid; cqContext cqc; cqContext *pcqCtx; Form_pg_tablespace spcForm; HeapTuple tup; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); pcqCtx = caql_addrel(cqclr(&cqc), rel); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_tablespace " " WHERE spcname = :1 " " FOR UPDATE ", CStringGetDatum(name))); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", name))); tablespaceoid = HeapTupleGetOid(tup); spcForm = (Form_pg_tablespace) GETSTRUCT(tup); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (spcForm->spcowner != newOwnerId) { Datum repl_val[Natts_pg_tablespace]; bool repl_null[Natts_pg_tablespace]; bool repl_repl[Natts_pg_tablespace]; Acl *newAcl; Datum aclDatum; bool isNull; HeapTuple newtuple; /* Otherwise, must be owner of the existing object */ if (!pg_tablespace_ownercheck(tablespaceoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, name); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* * Normally we would also check for create permissions here, but there * are none for tablespaces so we follow what rename tablespace does * and omit the create permissions check. * * NOTE: Only superusers may create tablespaces to begin with and so * initially only a superuser would be able to change its ownership * anyway. */ memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); repl_repl[Anum_pg_tablespace_spcowner - 1] = true; repl_val[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only * necessary when the ACL is non-null. */ aclDatum = heap_getattr(tup, Anum_pg_tablespace_spcacl, RelationGetDescr(rel), &isNull); if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), spcForm->spcowner, newOwnerId); repl_repl[Anum_pg_tablespace_spcacl - 1] = true; repl_val[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(newAcl); } newtuple = caql_modify_current(pcqCtx, repl_val, repl_null, repl_repl); caql_update_current(pcqCtx, newtuple); /* and Update indexes (implicit) */ /* MPP-6929: metadata tracking */ if (Gp_role == GP_ROLE_DISPATCH) MetaTrackUpdObject(TableSpaceRelationId, tablespaceoid, GetUserId(), "ALTER", "OWNER" ); heap_freetuple(newtuple); /* Update owner dependency reference */ changeDependencyOnOwner(TableSpaceRelationId, HeapTupleGetOid(tup), newOwnerId); } heap_close(rel, NoLock); }
/* * OperatorUpd * * For a given operator, look up its negator and commutator operators. * If they are defined, but their negator and commutator fields * (respectively) are empty, then use the new operator for neg or comm. * This solves a problem for users who need to insert two new operators * which are the negator or commutator of each other. */ static void OperatorUpd(Oid baseId, Oid commId, Oid negId) { int i; Relation pg_operator_desc; HeapTuple tup; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; cqContext cqc; cqContext *pcqCtx; for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) 0; replaces[i] = false; nulls[i] = false; } /* * check and update the commutator & negator, if necessary * * We need a CommandCounterIncrement here in case of a self-commutator * operator: we'll need to update the tuple that we just inserted. */ CommandCounterIncrement(); pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); pcqCtx = caql_addrel(cqclr(&cqc), pg_operator_desc); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_operator " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(commId))); /* * if the commutator and negator are the same operator, do one update. XXX * this is probably useless code --- I doubt it ever makes sense for * commutator and negator to be the same thing... */ if (commId == negId) { if (HeapTupleIsValid(tup)) { Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate)) { if (!OidIsValid(t->oprnegate)) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = true; } if (!OidIsValid(t->oprcom)) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = true; } tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ } } heap_close(pg_operator_desc, RowExclusiveLock); return; } /* if commutator and negator are different, do two updates */ if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom))) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = true; tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ /* XXX XXX XXX XXX XXX XXX * NOTE: reset the values/replaces arrays *after* * this update because we might re-use them in the next update * XXX XXX XXX XXX XXX XXX */ values[Anum_pg_operator_oprcom - 1] = (Datum) 0; replaces[Anum_pg_operator_oprcom - 1] = false; } /* check and update the negator, if necessary */ /* reset the context and re-use it */ pcqCtx = caql_addrel(cqclr(&cqc), pg_operator_desc); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_operator " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(negId))); if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate))) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = true; tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ } heap_close(pg_operator_desc, RowExclusiveLock); }
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; }
/* ---------------------------------------------------------------- * 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; }