Datum dbms_assert_schema_name(PG_FUNCTION_ARGS) { Oid namespaceId; AclResult aclresult; text *sname; char *nspname; List *names; if (PG_ARGISNULL(0)) INVALID_SCHEMA_NAME_EXCEPTION(); sname = PG_GETARG_TEXT_P(0); if (EMPTY_STR(sname)) INVALID_SCHEMA_NAME_EXCEPTION(); nspname = text_to_cstring(sname); names = stringToQualifiedNameList(nspname); if (list_length(names) != 1) INVALID_SCHEMA_NAME_EXCEPTION(); namespaceId = GetSysCacheOid(NAMESPACENAME, CStringGetDatum(strVal(linitial(names))), 0, 0, 0); if (!OidIsValid(namespaceId)) INVALID_SCHEMA_NAME_EXCEPTION(); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) INVALID_SCHEMA_NAME_EXCEPTION(); PG_RETURN_TEXT_P(sname); }
/* * AlterConversionOwner_internal * * Internal routine for changing the owner. rel must be pg_conversion, already * open and suitably locked; it will not be closed. */ static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) { Form_pg_conversion convForm; HeapTuple tup; Assert(RelationGetRelid(rel) == ConversionRelationId); tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); convForm = (Form_pg_conversion) 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 (convForm->conowner != newOwnerId) { AclResult aclresult; /* Superusers can always do it */ if (!superuser()) { /* Otherwise, must be owner of the existing object */ if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(convForm->conname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(convForm->connamespace, newOwnerId, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(convForm->connamespace)); } /* * Modify the owner --- okay to scribble on tup because it's a copy */ convForm->conowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); /* Update owner dependency reference */ changeDependencyOnOwner(ConversionRelationId, conversionOid, newOwnerId); } heap_freetuple(tup); }
/* * DefineAggregate * * "oldstyle" signals the old (pre-8.2) style where the aggregate input type * is specified by a BASETYPE element in the parameters. Otherwise, * "args" defines the input type(s). */ Oid DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) { char *aggName; Oid aggNamespace; AclResult aclresult; List *transfuncName = NIL; List *finalfuncName = NIL; List *sortoperatorName = NIL; TypeName *baseType = NULL; TypeName *transType = NULL; char *initval = NULL; Oid *aggArgTypes; int numArgs; Oid transTypeId; char transTypeType; ListCell *pl; /* Convert list of names to a name and namespace */ aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace)); foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); /* * sfunc1, stype1, and initcond1 are accepted as obsolete spellings * for sfunc, stype, initcond. */ if (pg_strcasecmp(defel->defname, "sfunc") == 0) transfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "sfunc1") == 0) transfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "finalfunc") == 0) finalfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "sortop") == 0) sortoperatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "basetype") == 0) baseType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "stype") == 0) transType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "stype1") == 0) transType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "initcond") == 0) initval = defGetString(defel); else if (pg_strcasecmp(defel->defname, "initcond1") == 0) initval = defGetString(defel); else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("aggregate attribute \"%s\" not recognized", defel->defname))); }
/* * CREATE CONVERSION */ void CreateConversionCommand(CreateConversionStmt *stmt) { Oid namespaceId; char *conversion_name; AclResult aclresult; int from_encoding; int to_encoding; Oid funcoid; const char *from_encoding_name = stmt->for_encoding_name; const char *to_encoding_name = stmt->to_encoding_name; List *func_name = stmt->func_name; static Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID}; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name, &conversion_name); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); /* Check the encoding names */ from_encoding = pg_char_to_encoding(from_encoding_name); if (from_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source encoding \"%s\" does not exist", from_encoding_name))); to_encoding = pg_char_to_encoding(to_encoding_name); if (to_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("destination encoding \"%s\" does not exist", to_encoding_name))); /* * Check the existence of the conversion function. Function name could * be a qualified name. */ funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid), funcargs, false); /* Check we have EXECUTE rights for the function */ aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(func_name)); /* * All seem ok, go ahead (possible failure would be a duplicate * conversion name) */ ConversionCreate(conversion_name, namespaceId, GetUserId(), from_encoding, to_encoding, funcoid, stmt->def); }
/* * CREATE COLLATION */ ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists) { char *collName; Oid collNamespace; AclResult aclresult; ListCell *pl; DefElem *fromEl = NULL; DefElem *localeEl = NULL; DefElem *lccollateEl = NULL; DefElem *lcctypeEl = NULL; DefElem *providerEl = NULL; DefElem *versionEl = NULL; char *collcollate = NULL; char *collctype = NULL; char *collproviderstr = NULL; int collencoding; char collprovider = 0; char *collversion = NULL; Oid newoid; ObjectAddress address; collNamespace = QualifiedNameGetCreationNamespace(names, &collName); aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(collNamespace)); foreach(pl, parameters) { DefElem *defel = lfirst_node(DefElem, pl); DefElem **defelp; if (pg_strcasecmp(defel->defname, "from") == 0) defelp = &fromEl; else if (pg_strcasecmp(defel->defname, "locale") == 0) defelp = &localeEl; else if (pg_strcasecmp(defel->defname, "lc_collate") == 0) defelp = &lccollateEl; else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0) defelp = &lcctypeEl; else if (pg_strcasecmp(defel->defname, "provider") == 0) defelp = &providerEl; else if (pg_strcasecmp(defel->defname, "version") == 0) defelp = &versionEl; else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("collation attribute \"%s\" not recognized", defel->defname), parser_errposition(pstate, defel->location))); break; } *defelp = defel; }
/* * Rename conversion */ void RenameConversion(List *name, const char *newname) { Oid conversionOid; Oid namespaceOid; HeapTuple tup; Relation rel; AclResult aclresult; rel = heap_openr(ConversionRelationName, RowExclusiveLock); conversionOid = FindConversionByName(name); if (!OidIsValid(conversionOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("conversion \"%s\" does not exist", NameListToString(name)))); tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); namespaceOid = ((Form_pg_conversion) GETSTRUCT(tup))->connamespace; /* make sure the new name doesn't exist */ if (SearchSysCacheExists(CONNAMENSP, CStringGetDatum(newname), ObjectIdGetDatum(namespaceOid), 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists in schema \"%s\"", newname, get_namespace_name(namespaceOid)))); /* must be owner */ if (!superuser() && ((Form_pg_conversion) GETSTRUCT(tup))->conowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(name)); /* must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
/* * Try to lookup another operator (commutator, etc) * * If not found, check to see if it is exactly the operator we are trying * to define; if so, return InvalidOid. (Note that this case is only * sensible for a commutator, so we error out otherwise.) If it is not * the same operator, create a shell operator. */ static Oid get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, bool isCommutator) { Oid other_oid; bool otherDefined; char *otherName; Oid otherNamespace; AclResult aclresult; other_oid = OperatorLookup(otherOp, otherLeftTypeId, otherRightTypeId, &otherDefined); if (OidIsValid(other_oid)) { /* other op already in catalogs */ return other_oid; } otherNamespace = QualifiedNameGetCreationNamespace(otherOp, &otherName); if (strcmp(otherName, operatorName) == 0 && otherNamespace == operatorNamespace && otherLeftTypeId == leftTypeId && otherRightTypeId == rightTypeId) { /* * self-linkage to this operator; caller will fix later. Note that * only self-linkage for commutation makes sense. */ if (!isCommutator) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("operator cannot be its own negator or sort operator"))); return InvalidOid; } /* not in catalogs, different from operator, so make shell */ aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(otherNamespace)); other_oid = OperatorShellMake(otherName, otherNamespace, otherLeftTypeId, otherRightTypeId); return other_oid; }
/* * CREATE COLLATION */ Oid DefineCollation(List *names, List *parameters) { char *collName; Oid collNamespace; AclResult aclresult; ListCell *pl; DefElem *fromEl = NULL; DefElem *localeEl = NULL; DefElem *lccollateEl = NULL; DefElem *lcctypeEl = NULL; char *collcollate = NULL; char *collctype = NULL; Oid newoid; collNamespace = QualifiedNameGetCreationNamespace(names, &collName); aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(collNamespace)); foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); DefElem **defelp; if (pg_strcasecmp(defel->defname, "from") == 0) defelp = &fromEl; else if (pg_strcasecmp(defel->defname, "locale") == 0) defelp = &localeEl; else if (pg_strcasecmp(defel->defname, "lc_collate") == 0) defelp = &lccollateEl; else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0) defelp = &lcctypeEl; else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("collation attribute \"%s\" not recognized", defel->defname))); break; } *defelp = defel; }
/* * DefineOperator * this function extracts all the information from the * parameter list generated by the parser and then has * OperatorCreate() do all the actual work. * * 'parameters' is a list of DefElem */ ObjectAddress DefineOperator(List *names, List *parameters) { char *oprName; Oid oprNamespace; AclResult aclresult; bool canMerge = false; /* operator merges */ bool canHash = false; /* operator hashes */ List *functionName = NIL; /* function for operator */ TypeName *typeName1 = NULL; /* first type name */ TypeName *typeName2 = NULL; /* second type name */ Oid typeId1 = InvalidOid; /* types converted to OID */ Oid typeId2 = InvalidOid; Oid rettype; List *commutatorName = NIL; /* optional commutator operator name */ List *negatorName = NIL; /* optional negator operator name */ List *restrictionName = NIL; /* optional restrict. sel. procedure */ List *joinName = NIL; /* optional join sel. procedure */ Oid functionOid; /* functions converted to OID */ Oid restrictionOid; Oid joinOid; Oid typeId[2]; /* to hold left and right arg */ int nargs; ListCell *pl; /* Convert list of names to a name and namespace */ oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprNamespace)); /* * loop over the definition list and extract the information we need. */ foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); if (pg_strcasecmp(defel->defname, "leftarg") == 0) { typeName1 = defGetTypeName(defel); if (typeName1->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SETOF type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "rightarg") == 0) { typeName2 = defGetTypeName(defel); if (typeName2->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SETOF type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "procedure") == 0) functionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "commutator") == 0) commutatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "negator") == 0) negatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "restrict") == 0) restrictionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "join") == 0) joinName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "hashes") == 0) canHash = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "merges") == 0) canMerge = defGetBoolean(defel); /* These obsolete options are taken as meaning canMerge */ else if (pg_strcasecmp(defel->defname, "sort1") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "sort2") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "ltcmp") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "gtcmp") == 0) canMerge = true; else { /* WARNING, not ERROR, for historical backwards-compatibility */ ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("operator attribute \"%s\" not recognized", defel->defname))); } }
/* * AlterConversionOwner_internal * * Internal routine for changing the owner. rel must be pg_conversion, already * open and suitably locked; it will not be closed. */ static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) { Form_pg_conversion convForm; HeapTuple tup; cqContext cqc; cqContext *pcqCtx; Assert(RelationGetRelid(rel) == ConversionRelationId); pcqCtx = caql_addrel(cqclr(&cqc), rel); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_conversion " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(conversionOid))); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); convForm = (Form_pg_conversion) 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 (convForm->conowner == newOwnerId) { heap_freetuple(tup); return; } AclResult aclresult; /* Superusers can always do it */ if (!superuser()) { /* Otherwise, must be owner of the existing object */ if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(convForm->conname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(convForm->connamespace, newOwnerId, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(convForm->connamespace)); } /* * Modify the owner --- okay to scribble on tup because it's a copy */ convForm->conowner = newOwnerId; caql_update_current(pcqCtx, tup); /* implicit update of index as well */ /* Update owner dependency reference */ changeDependencyOnOwner(ConversionRelationId, conversionOid, newOwnerId); heap_freetuple(tup); }
/* * Rename conversion */ void RenameConversion(List *name, const char *newname) { Oid conversionOid; Oid namespaceOid; HeapTuple tup; Relation rel; AclResult aclresult; cqContext cqc2; cqContext cqc; cqContext *pcqCtx; rel = heap_open(ConversionRelationId, RowExclusiveLock); conversionOid = FindConversionByName(name); if (!OidIsValid(conversionOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("conversion \"%s\" does not exist", NameListToString(name)))); pcqCtx = caql_addrel(cqclr(&cqc), rel); tup = caql_getfirst( pcqCtx, cql("SELECT * FROM pg_conversion " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(conversionOid))); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); namespaceOid = ((Form_pg_conversion) GETSTRUCT(tup))->connamespace; /* make sure the new name doesn't exist */ if (caql_getcount( caql_addrel(cqclr(&cqc2), rel), cql("SELECT COUNT(*) FROM pg_conversion " " WHERE conname = :1 " " AND connamespace = :2 ", CStringGetDatum((char *) newname), ObjectIdGetDatum(namespaceOid)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists in schema \"%s\"", newname, get_namespace_name(namespaceOid)))); } /* must be owner */ if (!pg_conversion_ownercheck(conversionOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(name)); /* must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); caql_update_current(pcqCtx, tup); /* implicit update of index as well */ heap_close(rel, NoLock); heap_freetuple(tup); }
/* * DefineOperator * this function extracts all the information from the * parameter list generated by the parser and then has * OperatorCreate() do all the actual work. * * 'parameters' is a list of DefElem */ void DefineOperator(List *names, List *parameters) { char *oprName; Oid oprNamespace; AclResult aclresult; bool canMerge = false; /* operator merges */ bool canHash = false; /* operator hashes */ List *functionName = NIL; /* function for operator */ TypeName *typeName1 = NULL; /* first type name */ TypeName *typeName2 = NULL; /* second type name */ Oid typeId1 = InvalidOid; /* types converted to OID */ Oid typeId2 = InvalidOid; Oid rettype; List *commutatorName = NIL; /* optional commutator operator name */ List *negatorName = NIL; /* optional negator operator name */ List *restrictionName = NIL; /* optional restrict. sel. procedure */ List *joinName = NIL; /* optional join sel. procedure */ Oid functionOid; /* functions converted to OID */ Oid restrictionOid; Oid joinOid; Oid typeId[5]; /* only need up to 5 args here */ int nargs; ListCell *pl; /* Convert list of names to a name and namespace */ oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); /* * The SQL standard committee has decided that => should be used for named * parameters; therefore, a future release of PostgreSQL may disallow it * as the name of a user-defined operator. */ if (strcmp(oprName, "=>") == 0) ereport(WARNING, (errmsg("=> is deprecated as an operator name"), errdetail("This name may be disallowed altogether in future versions of PostgreSQL."))); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprNamespace)); /* * loop over the definition list and extract the information we need. */ foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); if (pg_strcasecmp(defel->defname, "leftarg") == 0) { typeName1 = defGetTypeName(defel); if (typeName1->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SETOF type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "rightarg") == 0) { typeName2 = defGetTypeName(defel); if (typeName2->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SETOF type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "procedure") == 0) functionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "commutator") == 0) commutatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "negator") == 0) negatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "restrict") == 0) restrictionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "join") == 0) joinName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "hashes") == 0) canHash = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "merges") == 0) canMerge = defGetBoolean(defel); /* These obsolete options are taken as meaning canMerge */ else if (pg_strcasecmp(defel->defname, "sort1") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "sort2") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "ltcmp") == 0) canMerge = true; else if (pg_strcasecmp(defel->defname, "gtcmp") == 0) canMerge = true; else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("operator attribute \"%s\" not recognized", defel->defname))); }
/* * DefineAggregate * * "oldstyle" signals the old (pre-8.2) style where the aggregate input type * is specified by a BASETYPE element in the parameters. Otherwise, * "args" is a pair, whose first element is a list of FunctionParameter structs * defining the agg's arguments (both direct and aggregated), and whose second * element is an Integer node with the number of direct args, or -1 if this * isn't an ordered-set aggregate. * "parameters" is a list of DefElem representing the agg's definition clauses. */ ObjectAddress DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, const char *queryString) { char *aggName; Oid aggNamespace; AclResult aclresult; char aggKind = AGGKIND_NORMAL; List *transfuncName = NIL; List *finalfuncName = NIL; List *combinefuncName = NIL; List *serialfuncName = NIL; List *deserialfuncName = NIL; List *mtransfuncName = NIL; List *minvtransfuncName = NIL; List *mfinalfuncName = NIL; bool finalfuncExtraArgs = false; bool mfinalfuncExtraArgs = false; List *sortoperatorName = NIL; TypeName *baseType = NULL; TypeName *transType = NULL; TypeName *serialType = NULL; TypeName *mtransType = NULL; int32 transSpace = 0; int32 mtransSpace = 0; char *initval = NULL; char *minitval = NULL; char *parallel = NULL; int numArgs; int numDirectArgs = 0; oidvector *parameterTypes; ArrayType *allParameterTypes; ArrayType *parameterModes; ArrayType *parameterNames; List *parameterDefaults; Oid variadicArgType; Oid transTypeId; Oid serialTypeId = InvalidOid; Oid mtransTypeId = InvalidOid; char transTypeType; char mtransTypeType = 0; char proparallel = PROPARALLEL_UNSAFE; ListCell *pl; /* Convert list of names to a name and namespace */ aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace)); /* Deconstruct the output of the aggr_args grammar production */ if (!oldstyle) { Assert(list_length(args) == 2); numDirectArgs = intVal(lsecond(args)); if (numDirectArgs >= 0) aggKind = AGGKIND_ORDERED_SET; else numDirectArgs = 0; args = (List *) linitial(args); } /* Examine aggregate's definition clauses */ foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); /* * sfunc1, stype1, and initcond1 are accepted as obsolete spellings * for sfunc, stype, initcond. */ if (pg_strcasecmp(defel->defname, "sfunc") == 0) transfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "sfunc1") == 0) transfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "finalfunc") == 0) finalfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "combinefunc") == 0) combinefuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "serialfunc") == 0) serialfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "deserialfunc") == 0) deserialfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "msfunc") == 0) mtransfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "minvfunc") == 0) minvtransfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "mfinalfunc") == 0) mfinalfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "finalfunc_extra") == 0) finalfuncExtraArgs = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "mfinalfunc_extra") == 0) mfinalfuncExtraArgs = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "sortop") == 0) sortoperatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "basetype") == 0) baseType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "hypothetical") == 0) { if (defGetBoolean(defel)) { if (aggKind == AGGKIND_NORMAL) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only ordered-set aggregates can be hypothetical"))); aggKind = AGGKIND_HYPOTHETICAL; } } else if (pg_strcasecmp(defel->defname, "stype") == 0) transType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "serialtype") == 0) serialType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "stype1") == 0) transType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "sspace") == 0) transSpace = defGetInt32(defel); else if (pg_strcasecmp(defel->defname, "mstype") == 0) mtransType = defGetTypeName(defel); else if (pg_strcasecmp(defel->defname, "msspace") == 0) mtransSpace = defGetInt32(defel); else if (pg_strcasecmp(defel->defname, "initcond") == 0) initval = defGetString(defel); else if (pg_strcasecmp(defel->defname, "initcond1") == 0) initval = defGetString(defel); else if (pg_strcasecmp(defel->defname, "minitcond") == 0) minitval = defGetString(defel); else if (pg_strcasecmp(defel->defname, "parallel") == 0) parallel = defGetString(defel); else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("aggregate attribute \"%s\" not recognized", defel->defname))); }
/* * Executes an ALTER OBJECT / RENAME TO statement. Based on the object * type, the function appropriate to that type is executed. */ void ExecRenameStmt(RenameStmt *stmt) { switch (stmt->renameType) { case OBJECT_AGGREGATE: RenameAggregate(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_CONVERSION: RenameConversion(stmt->object, stmt->newname); break; case OBJECT_DATABASE: RenameDatabase(stmt->subname, stmt->newname); break; case OBJECT_EXTPROTOCOL: RenameExtProtocol(stmt->subname, stmt->newname); break; case OBJECT_FUNCTION: RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_LANGUAGE: RenameLanguage(stmt->subname, stmt->newname); break; case OBJECT_OPCLASS: RenameOpClass(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_OPFAMILY: RenameOpFamily(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_ROLE: RenameRole(stmt->subname, stmt->newname); break; case OBJECT_SCHEMA: RenameSchema(stmt->subname, stmt->newname); break; case OBJECT_TABLESPACE: RenameTableSpace(stmt->subname, stmt->newname); break; case OBJECT_FILESPACE: RenameFileSpace(stmt->subname, stmt->newname); break; case OBJECT_TABLE: case OBJECT_SEQUENCE: case OBJECT_VIEW: case OBJECT_INDEX: { if (Gp_role == GP_ROLE_DISPATCH) { CheckRelationOwnership(stmt->relation, true); stmt->objid = RangeVarGetRelid(stmt->relation, false); } /* * RENAME TABLE requires that we (still) hold * CREATE rights on the containing namespace, as * well as ownership of the table. */ Oid namespaceId = get_rel_namespace(stmt->objid); AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); renamerel(stmt->objid, stmt->newname, stmt->renameType, stmt); break; } case OBJECT_COLUMN: case OBJECT_TRIGGER: { Oid relid; CheckRelationOwnership(stmt->relation, true); relid = RangeVarGetRelid(stmt->relation, false); switch (stmt->renameType) { case OBJECT_COLUMN: renameatt(relid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ interpretInhOption(stmt->relation->inhOpt), /* recursive? */ false); /* recursing already? */ break; case OBJECT_TRIGGER: renametrig(relid, stmt->subname, /* old att name */ stmt->newname); /* new att name */ break; default: /* can't happen */ ; } break; } case OBJECT_TSPARSER: RenameTSParser(stmt->object, stmt->newname); break; case OBJECT_TSDICTIONARY: RenameTSDictionary(stmt->object, stmt->newname); break; case OBJECT_TSTEMPLATE: RenameTSTemplate(stmt->object, stmt->newname); break; case OBJECT_TSCONFIGURATION: RenameTSConfiguration(stmt->object, stmt->newname); break; default: elog(ERROR, "unrecognized rename stmt type: %d", (int) stmt->renameType); } if (Gp_role == GP_ROLE_DISPATCH) { CdbDispatchUtilityStatement((Node *) stmt, "ExecRenameStmt"); } }
/* * DefineIndex * Creates a new index. * * 'heapRelation': the relation the index will apply to. * 'indexRelationName': the name for the new index, or NULL to indicate * that a nonconflicting default name should be picked. * 'indexRelationId': normally InvalidOid, but during bootstrap can be * nonzero to specify a preselected OID for the index. * 'accessMethodName': name of the AM to use. * 'tableSpaceName': name of the tablespace to create the index in. * NULL specifies using the appropriate default. * 'attributeList': a list of IndexElem specifying columns and expressions * to index on. * 'predicate': the partial-index condition, or NULL if none. * 'rangetable': needed to interpret the predicate. * 'options': reloptions from WITH (in list-of-DefElem form). * 'unique': make the index enforce uniqueness. * 'primary': mark the index as a primary key in the catalogs. * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint, * so build a pg_constraint entry for it. * 'is_alter_table': this is due to an ALTER rather than a CREATE operation. * 'check_rights': check for CREATE rights in the namespace. (This should * be true except when ALTER is deleting/recreating an index.) * 'skip_build': make the catalog entries but leave the index file empty; * it will be filled later. * 'quiet': suppress the NOTICE chatter ordinarily provided for constraints. * 'concurrent': avoid blocking writers to the table while building. */ void DefineIndex(RangeVar *heapRelation, char *indexRelationName, Oid indexRelationId, char *accessMethodName, char *tableSpaceName, List *attributeList, Expr *predicate, List *rangetable, List *options, bool unique, bool primary, bool isconstraint, bool is_alter_table, bool check_rights, bool skip_build, bool quiet, bool concurrent) { Oid *classObjectId; Oid accessMethodId; Oid relationId; Oid namespaceId; Oid tablespaceId; Relation rel; HeapTuple tuple; Form_pg_am accessMethodForm; RegProcedure amoptions; Datum reloptions; IndexInfo *indexInfo; int numberOfAttributes; List *old_xact_list; ListCell *lc; uint32 ixcnt; LockRelId heaprelid; LOCKTAG heaplocktag; Snapshot snapshot; Relation pg_index; HeapTuple indexTuple; Form_pg_index indexForm; /* * count attributes in index */ numberOfAttributes = list_length(attributeList); if (numberOfAttributes <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column"))); if (numberOfAttributes > INDEX_MAX_KEYS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS))); /* * Open heap relation, acquire a suitable lock on it, remember its OID * * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE * (but not VACUUM). */ rel = heap_openrv(heapRelation, (concurrent ? ShareUpdateExclusiveLock : ShareLock)); relationId = RelationGetRelid(rel); namespaceId = RelationGetNamespace(rel); /* Note: during bootstrap may see uncataloged relation */ if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_UNCATALOGED) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", heapRelation->relname))); /* * Don't try to CREATE INDEX on temp tables of other backends. */ if (isOtherTempNamespace(namespaceId)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot create indexes on temporary tables of other sessions"))); /* * Verify we (still) have CREATE rights in the rel's namespace. * (Presumably we did when the rel was created, but maybe not anymore.) * Skip check if caller doesn't want it. Also skip check if * bootstrapping, since permissions machinery may not be working yet. */ if (check_rights && !IsBootstrapProcessingMode()) { AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); } /* * Select tablespace to use. If not specified, use default_tablespace * (which may in turn default to database's default). */ if (tableSpaceName) { tablespaceId = get_tablespace_oid(tableSpaceName); if (!OidIsValid(tablespaceId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", tableSpaceName))); } else { tablespaceId = GetDefaultTablespace(); /* note InvalidOid is OK in this case */ } /* Check permissions except when using database's default */ if (OidIsValid(tablespaceId)) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceId)); } /* * Force shared indexes into the pg_global tablespace. This is a bit of a * hack but seems simpler than marking them in the BKI commands. */ if (rel->rd_rel->relisshared) tablespaceId = GLOBALTABLESPACE_OID; /* * Select name for index if caller didn't specify */ if (indexRelationName == NULL) { if (primary) indexRelationName = ChooseRelationName(RelationGetRelationName(rel), NULL, "pkey", namespaceId); else { IndexElem *iparam = (IndexElem *) linitial(attributeList); indexRelationName = ChooseRelationName(RelationGetRelationName(rel), iparam->name, "key", namespaceId); } } /* * look up the access method, verify it can handle the requested features */ tuple = SearchSysCache(AMNAME, PointerGetDatum(accessMethodName), 0, 0, 0); if (!HeapTupleIsValid(tuple)) { /* * Hack to provide more-or-less-transparent updating of old RTREE * indexes to GIST: if RTREE is requested and not found, use GIST. */ if (strcmp(accessMethodName, "rtree") == 0) { ereport(NOTICE, (errmsg("substituting access method \"gist\" for obsolete method \"rtree\""))); accessMethodName = "gist"; tuple = SearchSysCache(AMNAME, PointerGetDatum(accessMethodName), 0, 0, 0); } if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("access method \"%s\" does not exist", accessMethodName))); } accessMethodId = HeapTupleGetOid(tuple); accessMethodForm = (Form_pg_am) GETSTRUCT(tuple); if (unique && !accessMethodForm->amcanunique) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support unique indexes", accessMethodName))); if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support multicolumn indexes", accessMethodName))); amoptions = accessMethodForm->amoptions; ReleaseSysCache(tuple); /* * If a range table was created then check that only the base rel is * mentioned. */ if (rangetable != NIL) { if (list_length(rangetable) != 1 || getrelid(1, rangetable) != relationId) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("index expressions and predicates may refer only to the table being indexed"))); } /* * Validate predicate, if given */ if (predicate) CheckPredicate(predicate); /* * Extra checks when creating a PRIMARY KEY index. */ if (primary) { List *cmds; ListCell *keys; /* * If ALTER TABLE, check that there isn't already a PRIMARY KEY. In * CREATE TABLE, we have faith that the parser rejected multiple pkey * clauses; and CREATE INDEX doesn't have a way to say PRIMARY KEY, so * it's no problem either. */ if (is_alter_table && relationHasPrimaryKey(rel)) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("multiple primary keys for table \"%s\" are not allowed", RelationGetRelationName(rel)))); } /* * Check that all of the attributes in a primary key are marked as not * null, otherwise attempt to ALTER TABLE .. SET NOT NULL */ cmds = NIL; foreach(keys, attributeList) { IndexElem *key = (IndexElem *) lfirst(keys); HeapTuple atttuple; if (!key->name) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("primary keys cannot be expressions"))); /* System attributes are never null, so no problem */ if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) continue; atttuple = SearchSysCacheAttName(relationId, key->name); if (HeapTupleIsValid(atttuple)) { if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) { /* Add a subcommand to make this one NOT NULL */ AlterTableCmd *cmd = makeNode(AlterTableCmd); cmd->subtype = AT_SetNotNull; cmd->name = key->name; cmds = lappend(cmds, cmd); } ReleaseSysCache(atttuple); } else { /* * This shouldn't happen during CREATE TABLE, but can happen * during ALTER TABLE. Keep message in sync with * transformIndexConstraints() in parser/analyze.c. */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", key->name))); } } /* * XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade to child * tables? Currently, since the PRIMARY KEY itself doesn't cascade, * we don't cascade the notnull constraint(s) either; but this is * pretty debatable. * * XXX: possible future improvement: when being called from ALTER * TABLE, it would be more efficient to merge this with the outer * ALTER TABLE, so as to avoid two scans. But that seems to * complicate DefineIndex's API unduly. */ if (cmds) AlterTableInternal(relationId, cmds, false); }
/* * DefineFileSystem */ void DefineFileSystem(List *name, List *parameters, Oid newOid, bool trusted) { char *fsysName; Oid fsysNamespace; AclResult aclresult; List *funcNames[FSYS_FUNC_TOTALNUM]; char *fsysLibFile = NULL; int funcNum = 0; ListCell *pl; Oid fsysOid; /* Convert list of names to a name and namespace */ fsysNamespace = QualifiedNameGetCreationNamespace(name, &fsysName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(fsysNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(fsysNamespace)); for(int i = 0; i < FSYS_FUNC_TOTALNUM; i++) funcNames[i] = NIL; foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); int funcType; if (pg_strcasecmp(defel->defname, fsysLibFileName) == 0) { if(fsysLibFile == NULL) { fsysLibFile = strVal(linitial(defGetQualifiedName(defel))); } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("filesystem attribute \"%s\" duplicated", defel->defname))); } continue; } for(funcType = 0; funcType < FSYS_FUNC_TOTALNUM; funcType++) { if(pg_strcasecmp(defel->defname, fsys_func_type_to_name(funcType)) == 0) break; } if (funcType >= FSYS_FUNC_TOTALNUM) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("filesystem attribute \"%s\" not recognized", defel->defname))); if(funcNames[funcType] == NIL) funcNames[funcType] = defGetQualifiedName(defel); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("filesystem function \"%s\" duplicated", defel->defname))); funcNum++; }
/* * CREATE CONVERSION */ ObjectAddress CreateConversionCommand(CreateConversionStmt *stmt) { Oid namespaceId; char *conversion_name; AclResult aclresult; int from_encoding; int to_encoding; Oid funcoid; const char *from_encoding_name = stmt->for_encoding_name; const char *to_encoding_name = stmt->to_encoding_name; List *func_name = stmt->func_name; static const Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID}; char result[1]; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name, &conversion_name); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); /* Check the encoding names */ from_encoding = pg_char_to_encoding(from_encoding_name); if (from_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source encoding \"%s\" does not exist", from_encoding_name))); to_encoding = pg_char_to_encoding(to_encoding_name); if (to_encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("destination encoding \"%s\" does not exist", to_encoding_name))); /* * Check the existence of the conversion function. Function name could be * a qualified name. */ funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid), funcargs, false); /* Check it returns VOID, else it's probably the wrong function */ if (get_func_rettype(funcoid) != VOIDOID) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("encoding conversion function %s must return type %s", NameListToString(func_name), "void"))); /* Check we have EXECUTE rights for the function */ aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(func_name)); /* * Check that the conversion function is suitable for the requested source * and target encodings. We do that by calling the function with an empty * string; the conversion function should throw an error if it can't * perform the requested conversion. */ OidFunctionCall5(funcoid, Int32GetDatum(from_encoding), Int32GetDatum(to_encoding), CStringGetDatum(""), CStringGetDatum(result), Int32GetDatum(0)); /* * All seem ok, go ahead (possible failure would be a duplicate conversion * name) */ return ConversionCreate(conversion_name, namespaceId, GetUserId(), from_encoding, to_encoding, funcoid, stmt->def); }
/* * Examine the RETURNS clause of the CREATE FUNCTION statement * and return information about it as *prorettype_p and *returnsSet. * * This is more complex than the average typename lookup because we want to * allow a shell type to be used, or even created if the specified return type * doesn't exist yet. (Without this, there's no way to define the I/O procs * for a new type.) But SQL function creation won't cope, so error out if * the target language is SQL. (We do this here, not in the SQL-function * validator, so as not to produce a NOTICE and then an ERROR for the same * condition.) */ static void compute_return_type(TypeName *returnType, Oid languageOid, Oid *prorettype_p, bool *returnsSet_p) { Oid rettype; Type typtup; AclResult aclresult; typtup = LookupTypeName(NULL, returnType, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) { if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot return shell type %s", TypeNameToString(returnType)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("return type %s is only a shell", TypeNameToString(returnType)))); } rettype = typeTypeId(typtup); ReleaseSysCache(typtup); } else { char *typnam = TypeNameToString(returnType); Oid namespaceId; AclResult aclresult; char *typname; /* * Only C-coded functions can be I/O functions. We enforce this * restriction here mainly to prevent littering the catalogs with * shell types due to simple typos in user-defined function * definitions. */ if (languageOid != INTERNALlanguageId && languageOid != ClanguageId) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typnam))); /* Reject if there's typmod decoration, too */ if (returnType->typmods != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("type modifier cannot be specified for shell type \"%s\"", typnam))); /* Otherwise, go ahead and make a shell type */ ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" is not yet defined", typnam), errdetail("Creating a shell type definition."))); namespaceId = QualifiedNameGetCreationNamespace(returnType->names, &typname); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); rettype = TypeShellMake(typname, namespaceId, GetUserId()); Assert(OidIsValid(rettype)); } aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, rettype); *prorettype_p = rettype; *returnsSet_p = returnType->setof; }
/* * Executes an ALTER OBJECT / RENAME TO statement. Based on the object * type, the function appropriate to that type is executed. */ void ExecRenameStmt(RenameStmt *stmt) { switch (stmt->renameType) { case OBJECT_AGGREGATE: RenameAggregate(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_CONVERSION: RenameConversion(stmt->object, stmt->newname); break; case OBJECT_DATABASE: RenameDatabase(stmt->subname, stmt->newname); break; case OBJECT_FUNCTION: RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_LANGUAGE: RenameLanguage(stmt->subname, stmt->newname); break; case OBJECT_OPCLASS: RenameOpClass(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_ROLE: RenameRole(stmt->subname, stmt->newname); break; case OBJECT_SCHEMA: RenameSchema(stmt->subname, stmt->newname); break; case OBJECT_TABLESPACE: RenameTableSpace(stmt->subname, stmt->newname); break; case OBJECT_TABLE: case OBJECT_INDEX: case OBJECT_COLUMN: case OBJECT_TRIGGER: { Oid relid; CheckRelationOwnership(stmt->relation, true); relid = RangeVarGetRelid(stmt->relation, false); switch (stmt->renameType) { case OBJECT_TABLE: case OBJECT_INDEX: { /* * RENAME TABLE requires that we (still) hold * CREATE rights on the containing namespace, as * well as ownership of the table. */ Oid namespaceId = get_rel_namespace(relid); AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); renamerel(relid, stmt->newname); break; } case OBJECT_COLUMN: renameatt(relid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ interpretInhOption(stmt->relation->inhOpt), /* recursive? */ false); /* recursing already? */ break; case OBJECT_TRIGGER: renametrig(relid, stmt->subname, /* old att name */ stmt->newname); /* new att name */ break; default: /* can't happen */ ; } break; } default: elog(ERROR, "unrecognized rename stmt type: %d", (int) stmt->renameType); } }
/* * DefineOperator * this function extracts all the information from the * parameter list generated by the parser and then has * OperatorCreate() do all the actual work. * * 'parameters' is a list of DefElem */ void DefineOperator(List *names, List *parameters) { char *oprName; Oid oprNamespace; AclResult aclresult; bool canHash = false; /* operator hashes */ bool canMerge = false; /* operator merges */ List *functionName = NIL; /* function for operator */ TypeName *typeName1 = NULL; /* first type name */ TypeName *typeName2 = NULL; /* second type name */ Oid typeId1 = InvalidOid; /* types converted to OID */ Oid typeId2 = InvalidOid; List *commutatorName = NIL; /* optional commutator operator * name */ List *negatorName = NIL; /* optional negator operator name */ List *restrictionName = NIL; /* optional restrict. sel. * procedure */ List *joinName = NIL; /* optional join sel. procedure */ List *leftSortName = NIL; /* optional left sort operator */ List *rightSortName = NIL; /* optional right sort operator */ List *ltCompareName = NIL; /* optional < compare operator */ List *gtCompareName = NIL; /* optional > compare operator */ List *pl; /* Convert list of names to a name and namespace */ oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprNamespace)); /* * loop over the definition list and extract the information we need. */ foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); if (strcasecmp(defel->defname, "leftarg") == 0) { typeName1 = defGetTypeName(defel); if (typeName1->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("setof type not allowed for operator argument"))); } else if (strcasecmp(defel->defname, "rightarg") == 0) { typeName2 = defGetTypeName(defel); if (typeName2->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("setof type not allowed for operator argument"))); } else if (strcasecmp(defel->defname, "procedure") == 0) functionName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "commutator") == 0) commutatorName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "negator") == 0) negatorName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "restrict") == 0) restrictionName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "join") == 0) joinName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "hashes") == 0) canHash = TRUE; else if (strcasecmp(defel->defname, "merges") == 0) canMerge = TRUE; else if (strcasecmp(defel->defname, "sort1") == 0) leftSortName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "sort2") == 0) rightSortName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "ltcmp") == 0) ltCompareName = defGetQualifiedName(defel); else if (strcasecmp(defel->defname, "gtcmp") == 0) gtCompareName = defGetQualifiedName(defel); else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("operator attribute \"%s\" not recognized", defel->defname))); }