diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/catalog/Makefile | 1 | ||||
| -rw-r--r-- | src/backend/catalog/dependency.c | 8 | ||||
| -rw-r--r-- | src/backend/catalog/information_schema.sql | 34 | ||||
| -rw-r--r-- | src/backend/catalog/objectaddress.c | 65 | ||||
| -rw-r--r-- | src/backend/catalog/pg_aggregate.c | 1 | ||||
| -rw-r--r-- | src/backend/catalog/pg_proc.c | 46 | ||||
| -rw-r--r-- | src/backend/commands/dropcmds.c | 8 | ||||
| -rw-r--r-- | src/backend/commands/event_trigger.c | 3 | ||||
| -rw-r--r-- | src/backend/commands/functioncmds.c | 343 | ||||
| -rw-r--r-- | src/backend/commands/proclang.c | 3 | ||||
| -rw-r--r-- | src/backend/commands/typecmds.c | 1 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 17 | ||||
| -rw-r--r-- | src/backend/nodes/equalfuncs.c | 15 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 90 | ||||
| -rw-r--r-- | src/backend/tcop/utility.c | 16 | ||||
| -rw-r--r-- | src/backend/utils/adt/ruleutils.c | 39 | ||||
| -rw-r--r-- | src/backend/utils/cache/lsyscache.c | 71 | ||||
| -rw-r--r-- | src/backend/utils/cache/syscache.c | 23 | ||||
| -rw-r--r-- | src/backend/utils/fmgr/funcapi.c | 44 |
19 files changed, 799 insertions, 29 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index a403c64360..c73f20d6a5 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -41,6 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_table.h pg_policy.h \ pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \ + pg_transform.h \ toasting.h indexing.h \ ) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index bacb242e4e..6271f8f5a0 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -47,6 +47,7 @@ #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_tablespace.h" +#include "catalog/pg_transform.h" #include "catalog/pg_trigger.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" @@ -1265,6 +1266,10 @@ doDeletion(const ObjectAddress *object, int flags) RemovePolicyById(object->objectId); break; + case OCLASS_TRANSFORM: + DropTransformById(object->objectId); + break; + default: elog(ERROR, "unrecognized object class: %u", object->classId); @@ -2373,6 +2378,9 @@ getObjectClass(const ObjectAddress *object) case PolicyRelationId: return OCLASS_POLICY; + + case TransformRelationId: + return OCLASS_TRANSFORM; } /* shouldn't get here */ diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 3c6a049b6d..6e1b241956 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1928,7 +1928,39 @@ GRANT SELECT ON tables TO PUBLIC; * TRANSFORMS view */ --- feature not supported +CREATE VIEW transforms AS + SELECT CAST(current_database() AS sql_identifier) AS udt_catalog, + CAST(nt.nspname AS sql_identifier) AS udt_schema, + CAST(t.typname AS sql_identifier) AS udt_name, + CAST(current_database() AS sql_identifier) AS specific_catalog, + CAST(np.nspname AS sql_identifier) AS specific_schema, + CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name, + CAST(l.lanname AS sql_identifier) AS group_name, + CAST('FROM SQL' AS character_data) AS transform_type + FROM pg_type t JOIN pg_transform x ON t.oid = x.trftype + JOIN pg_language l ON x.trflang = l.oid + JOIN pg_proc p ON x.trffromsql = p.oid + JOIN pg_namespace nt ON t.typnamespace = nt.oid + JOIN pg_namespace np ON p.pronamespace = np.oid + + UNION + + SELECT CAST(current_database() AS sql_identifier) AS udt_catalog, + CAST(nt.nspname AS sql_identifier) AS udt_schema, + CAST(t.typname AS sql_identifier) AS udt_name, + CAST(current_database() AS sql_identifier) AS specific_catalog, + CAST(np.nspname AS sql_identifier) AS specific_schema, + CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name, + CAST(l.lanname AS sql_identifier) AS group_name, + CAST('TO SQL' AS character_data) AS transform_type + FROM pg_type t JOIN pg_transform x ON t.oid = x.trftype + JOIN pg_language l ON x.trflang = l.oid + JOIN pg_proc p ON x.trftosql = p.oid + JOIN pg_namespace nt ON t.typnamespace = nt.oid + JOIN pg_namespace np ON p.pronamespace = np.oid + + ORDER BY udt_catalog, udt_schema, udt_name, group_name, transform_type -- some sensible grouping for interactive use +; /* diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 30cb699072..5e1bda4ed2 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -45,6 +45,7 @@ #include "catalog/pg_policy.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_tablespace.h" +#include "catalog/pg_transform.h" #include "catalog/pg_trigger.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" @@ -335,6 +336,12 @@ static const ObjectPropertyType ObjectProperty[] = true }, { + TransformRelationId, + TransformOidIndexId, + TRFOID, + InvalidAttrNumber + }, + { TriggerRelationId, TriggerOidIndexId, -1, @@ -760,6 +767,19 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, address.objectSubId = 0; } break; + case OBJECT_TRANSFORM: + { + TypeName *typename = (TypeName *) linitial(objname); + char *langname = (char *) linitial(objargs); + Oid type_id = LookupTypeNameOid(NULL, typename, missing_ok); + Oid lang_id = get_language_oid(langname, missing_ok); + + address.classId = TransformRelationId; + address.objectId = + get_transform_oid(type_id, lang_id, missing_ok); + address.objectSubId = 0; + } + break; case OBJECT_TSPARSER: address.classId = TSParserRelationId; address.objectId = get_ts_parser_oid(objname, missing_ok); @@ -2006,6 +2026,15 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, format_type_be(targettypeid)))); } break; + case OBJECT_TRANSFORM: + { + TypeName *typename = (TypeName *) linitial(objname); + Oid typeid = typenameTypeId(NULL, typename); + + if (!pg_type_ownercheck(typeid, roleid)) + aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid); + } + break; case OBJECT_TABLESPACE: if (!pg_tablespace_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, @@ -2467,19 +2496,10 @@ getObjectDescription(const ObjectAddress *object) } case OCLASS_LANGUAGE: - { - HeapTuple langTup; + appendStringInfo(&buffer, _("language %s"), + get_language_name(object->objectId, false)); + break; - langTup = SearchSysCache1(LANGOID, - ObjectIdGetDatum(object->objectId)); - if (!HeapTupleIsValid(langTup)) - elog(ERROR, "cache lookup failed for language %u", - object->objectId); - appendStringInfo(&buffer, _("language %s"), - NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname)); - ReleaseSysCache(langTup); - break; - } case OCLASS_LARGEOBJECT: appendStringInfo(&buffer, _("large object %u"), object->objectId); @@ -2667,6 +2687,27 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_TRANSFORM: + { + HeapTuple trfTup; + Form_pg_transform trfForm; + + trfTup = SearchSysCache1(TRFOID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(trfTup)) + elog(ERROR, "could not find tuple for transform %u", + object->objectId); + + trfForm = (Form_pg_transform) GETSTRUCT(trfTup); + + appendStringInfo(&buffer, _("transform for %s language %s"), + format_type_be(trfForm->trftype), + get_language_name(trfForm->trflang, false)); + + ReleaseSysCache(trfTup); + break; + } + case OCLASS_TRIGGER: { Relation trigDesc; diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index df7bdfb03c..5f211dacde 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -545,6 +545,7 @@ AggregateCreate(const char *aggName, parameterModes, /* parameterModes */ parameterNames, /* parameterNames */ parameterDefaults, /* parameterDefaults */ + PointerGetDatum(NULL), /* trftypes */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ 0); /* prorows */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index fd5060aa7c..122982951e 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -23,7 +23,9 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_proc_fn.h" +#include "catalog/pg_transform.h" #include "catalog/pg_type.h" +#include "commands/defrem.h" #include "executor/functions.h" #include "funcapi.h" #include "mb/pg_wchar.h" @@ -59,7 +61,7 @@ static bool match_prosrc_to_literal(const char *prosrc, const char *literal, /* ---------------------------------------------------------------- * ProcedureCreate * - * Note: allParameterTypes, parameterModes, parameterNames, and proconfig + * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h. * ---------------------------------------------------------------- @@ -86,6 +88,7 @@ ProcedureCreate(const char *procedureName, Datum parameterModes, Datum parameterNames, List *parameterDefaults, + Datum trftypes, Datum proconfig, float4 procost, float4 prorows) @@ -116,6 +119,7 @@ ProcedureCreate(const char *procedureName, ObjectAddress myself, referenced; int i; + Oid trfid; /* * sanity checks @@ -360,6 +364,10 @@ ProcedureCreate(const char *procedureName, values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; + if (trftypes != PointerGetDatum(NULL)) + values[Anum_pg_proc_protrftypes - 1] = trftypes; + else + nulls[Anum_pg_proc_protrftypes - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); @@ -624,6 +632,15 @@ ProcedureCreate(const char *procedureName, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* dependency on transform used by return type, if any */ + if ((trfid = get_transform_oid(returnType, languageObjectId, true))) + { + referenced.classId = TransformRelationId; + referenced.objectId = trfid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + /* dependency on parameter types */ for (i = 0; i < allParamCount; i++) { @@ -631,6 +648,15 @@ ProcedureCreate(const char *procedureName, referenced.objectId = allParams[i]; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on transform used by parameter type, if any */ + if ((trfid = get_transform_oid(allParams[i], languageObjectId, true))) + { + referenced.classId = TransformRelationId; + referenced.objectId = trfid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* dependency on parameter default expressions */ @@ -1128,3 +1154,21 @@ fail: *newcursorpos = newcp; return false; } + +List * +oid_array_to_list(Datum datum) +{ + ArrayType *array = DatumGetArrayTypeP(datum); + Datum *values; + int nelems; + int i; + List *result = NIL; + + deconstruct_array(array, + OIDOID, + sizeof(Oid), true, 'i', + &values, NULL, &nelems); + for (i = 0; i < nelems; i++) + result = lappend_oid(result, values[i]); + return result; +} diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index a1b0d4d2fa..80a0eb6e9e 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -366,6 +366,14 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) } } break; + case OBJECT_TRANSFORM: + if (!type_in_list_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("transform for type %s language %s does not exist, skipping"); + name = TypeNameToString((TypeName *) linitial(objname)); + args = (char *) linitial(objargs); + } + break; case OBJECT_TRIGGER: if (!owningrel_does_not_exist_skipping(objname, &msg, &name)) { diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index f07fd06337..0110b0603d 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -98,6 +98,7 @@ static event_trigger_support_data event_trigger_support[] = { {"SERVER", true}, {"TABLE", true}, {"TABLESPACE", false}, + {"TRANSFORM", true}, {"TRIGGER", true}, {"TEXT SEARCH CONFIGURATION", true}, {"TEXT SEARCH DICTIONARY", true}, @@ -1090,6 +1091,7 @@ EventTriggerSupportsObjectType(ObjectType obtype) case OBJECT_SEQUENCE: case OBJECT_TABCONSTRAINT: case OBJECT_TABLE: + case OBJECT_TRANSFORM: case OBJECT_TRIGGER: case OBJECT_TSCONFIGURATION: case OBJECT_TSDICTIONARY: @@ -1137,6 +1139,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass) case OCLASS_REWRITE: case OCLASS_TRIGGER: case OCLASS_SCHEMA: + case OCLASS_TRANSFORM: case OCLASS_TSPARSER: case OCLASS_TSDICT: case OCLASS_TSTEMPLATE: diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index f4725056da..9a92fdcff7 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -45,6 +45,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_proc_fn.h" +#include "catalog/pg_transform.h" #include "catalog/pg_type.h" #include "catalog/pg_type_fn.h" #include "commands/alter.h" @@ -583,6 +584,7 @@ static void compute_attributes_sql_style(List *options, List **as, char **language, + Node **transform, bool *windowfunc_p, char *volatility_p, bool *strict_p, @@ -595,6 +597,7 @@ compute_attributes_sql_style(List *options, ListCell *option; DefElem *as_item = NULL; DefElem *language_item = NULL; + DefElem *transform_item = NULL; DefElem *windowfunc_item = NULL; DefElem *volatility_item = NULL; DefElem *strict_item = NULL; @@ -624,6 +627,14 @@ compute_attributes_sql_style(List *options, errmsg("conflicting or redundant options"))); language_item = defel; } + else if (strcmp(defel->defname, "transform") == 0) + { + if (transform_item) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + transform_item = defel; + } else if (strcmp(defel->defname, "window") == 0) { if (windowfunc_item) @@ -671,6 +682,8 @@ compute_attributes_sql_style(List *options, } /* process optional items */ + if (transform_item) + *transform = transform_item->arg; if (windowfunc_item) *windowfunc_p = intVal(windowfunc_item->arg); if (volatility_item) @@ -807,7 +820,6 @@ interpret_AS_clause(Oid languageOid, const char *languageName, } - /* * CreateFunction * Execute a CREATE FUNCTION utility statement. @@ -822,6 +834,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) char *language; Oid languageOid; Oid languageValidator; + Node *transformDefElem = NULL; char *funcname; Oid namespaceId; AclResult aclresult; @@ -831,6 +844,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) ArrayType *parameterNames; List *parameterDefaults; Oid variadicArgType; + List *trftypes_list = NIL; + ArrayType *trftypes; Oid requiredResultType; bool isWindowFunc, isStrict, @@ -866,7 +881,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, - &as_clause, &language, + &as_clause, &language, &transformDefElem, &isWindowFunc, &volatility, &isStrict, &security, &isLeakProof, &proconfig, &procost, &prorows); @@ -915,6 +930,23 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("only superuser can define a leakproof function"))); + if (transformDefElem) + { + ListCell *lc; + + Assert(IsA(transformDefElem, List)); + + foreach (lc, (List *) transformDefElem) + { + Oid typeid = typenameTypeId(NULL, lfirst(lc)); + Oid elt = get_base_element_type(typeid); + typeid = elt ? elt : typeid; + + get_transform_oid(typeid, languageOid, false); + trftypes_list = lappend_oid(trftypes_list, typeid); + } + } + /* * Convert remaining parameters of CREATE to form wanted by * ProcedureCreate. @@ -958,6 +990,25 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) returnsSet = false; } + if (list_length(trftypes_list) > 0) + { + ListCell *lc; + Datum *arr; + int i; + + arr = palloc(list_length(trftypes_list) * sizeof(Datum)); + i = 0; + foreach (lc, trftypes_list) + arr[i++] = ObjectIdGetDatum(lfirst_oid(lc)); + trftypes = construct_array(arr, list_length(trftypes_list), + OIDOID, sizeof(Oid), true, 'i'); + } + else + { + /* store SQL NULL instead of emtpy array */ + trftypes = NULL; + } + compute_attributes_with_style(stmt->withClause, &isStrict, &volatility); interpret_AS_clause(languageOid, language, funcname, as_clause, @@ -1014,6 +1065,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) PointerGetDatum(parameterModes), PointerGetDatum(parameterNames), parameterDefaults, + PointerGetDatum(trftypes), PointerGetDatum(proconfig), procost, prorows); @@ -1653,6 +1705,293 @@ DropCastById(Oid castOid) heap_close(relation, RowExclusiveLock); } + +static void +check_transform_function(Form_pg_proc procstruct) +{ + if (procstruct->provolatile == PROVOLATILE_VOLATILE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("transform function must not be volatile"))); + if (procstruct->proisagg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("transform function must not be an aggregate function"))); + if (procstruct->proiswindow) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("transform function must not be a window function"))); + if (procstruct->proretset) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("transform function must not return a set"))); + if (procstruct->pronargs != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("transform function must take one argument"))); + if (procstruct->proargtypes.values[0] != INTERNALOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("first argument of transform function must be type \"internal\""))); +} + + +/* + * CREATE TRANSFORM + */ +Oid +CreateTransform(CreateTransformStmt *stmt) +{ + Oid typeid; + char typtype; + Oid langid; + Oid fromsqlfuncid; + Oid tosqlfuncid; + AclResult aclresult; + Form_pg_proc procstruct; + Datum values[Natts_pg_transform]; + bool nulls[Natts_pg_transform]; + bool replaces[Natts_pg_transform]; + Oid transformid; + HeapTuple tuple; + HeapTuple newtuple; + Relation relation; + ObjectAddress myself, + referenced; + bool is_replace; + + /* + * Get the type + */ + typeid = typenameTypeId(NULL, stmt->type_name); + typtype = get_typtype(typeid); + + if (typtype == TYPTYPE_PSEUDO) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("data type %s is a pseudo-type", + TypeNameToString(stmt->type_name)))); + + if (typtype == TYPTYPE_DOMAIN) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("data type %s is a domain", + TypeNameToString(stmt->type_name)))); + + if (!pg_type_ownercheck(typeid, GetUserId())) + aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid); + + aclresult = pg_type_aclcheck(typeid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, typeid); + + /* + * Get the language + */ + langid = get_language_oid(stmt->lang, false); + + aclresult = pg_language_aclcheck(langid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_LANGUAGE, stmt->lang); + + /* + * Get the functions + */ + if (stmt->fromsql) + { + fromsqlfuncid = LookupFuncNameTypeNames(stmt->fromsql->funcname, stmt->fromsql->funcargs, false); + + if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->fromsql->funcname)); + + aclresult = pg_proc_aclcheck(fromsqlfuncid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(stmt->fromsql->funcname)); + + tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fromsqlfuncid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", fromsqlfuncid); + procstruct = (Form_pg_proc) GETSTRUCT(tuple); + if (procstruct->prorettype != INTERNALOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("return data type of FROM SQL function must be \"internal\""))); + check_transform_function(procstruct); + ReleaseSysCache(tuple); + } + else + fromsqlfuncid = InvalidOid; + + if (stmt->tosql) + { + tosqlfuncid = LookupFuncNameTypeNames(stmt->tosql->funcname, stmt->tosql->funcargs, false); + + if (!pg_proc_ownercheck(tosqlfuncid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->tosql->funcname)); + + aclresult = pg_proc_aclcheck(tosqlfuncid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(stmt->tosql->funcname)); + + tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(tosqlfuncid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", tosqlfuncid); + procstruct = (Form_pg_proc) GETSTRUCT(tuple); + if (procstruct->prorettype != typeid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("return data type of TO SQL function must be the transform data type"))); + check_transform_function(procstruct); + ReleaseSysCache(tuple); + } + else + tosqlfuncid = InvalidOid; + + /* + * Ready to go + */ + values[Anum_pg_transform_trftype - 1] = ObjectIdGetDatum(typeid); + values[Anum_pg_transform_trflang - 1] = ObjectIdGetDatum(langid); + values[Anum_pg_transform_trffromsql - 1] = ObjectIdGetDatum(fromsqlfuncid); + values[Anum_pg_transform_trftosql - 1] = ObjectIdGetDatum(tosqlfuncid); + + MemSet(nulls, false, sizeof(nulls)); + + relation = heap_open(TransformRelationId, RowExclusiveLock); + + tuple = SearchSysCache2(TRFTYPELANG, + ObjectIdGetDatum(typeid), + ObjectIdGetDatum(langid)); + if (HeapTupleIsValid(tuple)) + { + if (!stmt->replace) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("transform for type %s language %s already exists", + format_type_be(typeid), + stmt->lang))); + + MemSet(replaces, false, sizeof(replaces)); + replaces[Anum_pg_transform_trffromsql - 1] = true; + replaces[Anum_pg_transform_trftosql - 1] = true; + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + simple_heap_update(relation, &newtuple->t_self, newtuple); + + transformid = HeapTupleGetOid(tuple); + ReleaseSysCache(tuple); + is_replace = true; + } + else + { + newtuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); + transformid = simple_heap_insert(relation, newtuple); + is_replace = false; + } + + CatalogUpdateIndexes(relation, newtuple); + + if (is_replace) + deleteDependencyRecordsFor(TransformRelationId, transformid, true); + + /* make dependency entries */ + myself.classId = TransformRelationId; + myself.objectId = transformid; + myself.objectSubId = 0; + + /* dependency on language */ + referenced.classId = LanguageRelationId; + referenced.objectId = langid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on type */ + referenced.classId = TypeRelationId; + referenced.objectId = typeid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependencies on functions */ + if (OidIsValid(fromsqlfuncid)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = fromsqlfuncid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + if (OidIsValid(tosqlfuncid)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = tosqlfuncid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself, is_replace); + + /* Post creation hook for new transform */ + InvokeObjectPostCreateHook(TransformRelationId, transformid, 0); + + heap_freetuple(newtuple); + + heap_close(relation, RowExclusiveLock); + + return transformid; +} + + +/* + * get_transform_oid - given type OID and language OID, look up a transform OID + * + * If missing_ok is false, throw an error if the transform is not found. If + * true, just return InvalidOid. + */ +Oid +get_transform_oid(Oid type_id, Oid lang_id, bool missing_ok) +{ + Oid oid; + + oid = GetSysCacheOid2(TRFTYPELANG, + ObjectIdGetDatum(type_id), + ObjectIdGetDatum(lang_id)); + if (!OidIsValid(oid) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("transform for type %s language \"%s\" does not exist", + format_type_be(type_id), + get_language_name(lang_id, false)))); + return oid; +} + + +void +DropTransformById(Oid transformOid) +{ + Relation relation; + ScanKeyData scankey; + SysScanDesc scan; + HeapTuple tuple; + + relation = heap_open(TransformRelationId, RowExclusiveLock); + + ScanKeyInit(&scankey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(transformOid)); + scan = systable_beginscan(relation, TransformOidIndexId, true, + NULL, 1, &scankey); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for transform %u", transformOid); + simple_heap_delete(relation, &tuple->t_self); + + systable_endscan(scan); + heap_close(relation, RowExclusiveLock); +} + + /* * Subroutine for ALTER FUNCTION/AGGREGATE SET SCHEMA/RENAME * diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 11e6213e80..3995a575e4 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -141,6 +141,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), + PointerGetDatum(NULL), 1, 0); handlerOid = tmpAddr.objectId; @@ -179,6 +180,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), + PointerGetDatum(NULL), 1, 0); inlineOid = tmpAddr.objectId; @@ -220,6 +222,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), + PointerGetDatum(NULL), 1, 0); valOid = tmpAddr.objectId; diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 7d8561022a..ab13be225c 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1616,6 +1616,7 @@ makeRangeConstructors(const char *name, Oid namespace, PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* trftypes */ PointerGetDatum(NULL), /* proconfig */ 1.0, /* procost */ 0.0); /* prorows */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 59c755d7a5..1685efe254 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3625,6 +3625,20 @@ _copyImportForeignSchemaStmt(const ImportForeignSchemaStmt *from) return newnode; } +static CreateTransformStmt * +_copyCreateTransformStmt(const CreateTransformStmt *from) +{ + CreateTransformStmt *newnode = makeNode(CreateTransformStmt); + + COPY_SCALAR_FIELD(replace); + COPY_NODE_FIELD(type_name); + COPY_STRING_FIELD(lang); + COPY_NODE_FIELD(fromsql); + COPY_NODE_FIELD(tosql); + + return newnode; +} + static CreateTrigStmt * _copyCreateTrigStmt(const CreateTrigStmt *from) { @@ -4568,6 +4582,9 @@ copyObject(const void *from) case T_ImportForeignSchemaStmt: retval = _copyImportForeignSchemaStmt(from); break; + case T_CreateTransformStmt: + retval = _copyCreateTransformStmt(from); + break; case T_CreateTrigStmt: retval = _copyCreateTrigStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3bc81762af..578ead5463 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1780,6 +1780,18 @@ _equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportFore } static bool +_equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStmt *b) +{ + COMPARE_SCALAR_FIELD(replace); + COMPARE_NODE_FIELD(type_name); + COMPARE_STRING_FIELD(lang); + COMPARE_NODE_FIELD(fromsql); + COMPARE_NODE_FIELD(tosql); + + return true; +} + +static bool _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b) { COMPARE_STRING_FIELD(trigname); @@ -2991,6 +3003,9 @@ equal(const void *a, const void *b) case T_ImportForeignSchemaStmt: retval = _equalImportForeignSchemaStmt(a, b); break; + case T_CreateTransformStmt: + retval = _equalCreateTransformStmt(a, b); + break; case T_CreateTrigStmt: retval = _equalCreateTrigStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5818858a29..962a69d22a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -241,12 +241,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt - CreateAssertStmt CreateTrigStmt CreateEventTrigStmt + CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropPolicyStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt + DropTransformStmt DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt @@ -366,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); opt_enum_val_list enum_val_list table_func_column_list create_generic_options alter_generic_options relation_expr_list dostmt_opt_list + transform_element_list transform_type_list %type <list> opt_fdw_options fdw_options %type <defelt> fdw_option @@ -611,12 +613,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE - SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME STABLE STANDALONE_P START + SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SYMMETRIC SYSID SYSTEM_P TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP - TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P + TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P TRUNCATE TRUSTED TYPE_P TYPES_P UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED @@ -790,6 +792,7 @@ stmt : | CreateSeqStmt | CreateStmt | CreateTableSpaceStmt + | CreateTransformStmt | CreateTrigStmt | CreateEventTrigStmt | CreateRoleStmt @@ -815,6 +818,7 @@ stmt : | DropRuleStmt | DropStmt | DropTableSpaceStmt + | DropTransformStmt | DropTrigStmt | DropRoleStmt | DropUserStmt @@ -4083,6 +4087,16 @@ AlterExtensionContentsStmt: n->objname = list_make1(makeString($6)); $$ = (Node *)n; } + | ALTER EXTENSION name add_drop TRANSFORM FOR Typename LANGUAGE name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TRANSFORM; + n->objname = list_make1($7); + n->objargs = list_make1($9); + $$ = (Node *)n; + } | ALTER EXTENSION name add_drop TYPE_P Typename { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); @@ -5736,6 +5750,15 @@ CommentStmt: n->comment = $6; $$ = (Node *) n; } + | COMMENT ON TRANSFORM FOR Typename LANGUAGE name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_TRANSFORM; + n->objname = list_make1($5); + n->objargs = list_make1($7); + n->comment = $9; + $$ = (Node *) n; + } | COMMENT ON TRIGGER name ON any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); @@ -7015,6 +7038,10 @@ createfunc_opt_item: { $$ = makeDefElem("language", (Node *)makeString($2)); } + | TRANSFORM transform_type_list + { + $$ = makeDefElem("transform", (Node *)$2); + } | WINDOW { $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); @@ -7032,6 +7059,11 @@ func_as: Sconst { $$ = list_make1(makeString($1)); } } ; +transform_type_list: + FOR TYPE_P Typename { $$ = list_make1($3); } + | transform_type_list ',' FOR TYPE_P Typename { $$ = lappend($1, $5); } + ; + opt_definition: WITH definition { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } @@ -7299,6 +7331,56 @@ opt_if_exists: IF_P EXISTS { $$ = TRUE; } /***************************************************************************** * + * CREATE TRANSFORM / DROP TRANSFORM + * + *****************************************************************************/ + +CreateTransformStmt: CREATE opt_or_replace TRANSFORM FOR Typename LANGUAGE name '(' transform_element_list ')' + { + CreateTransformStmt *n = makeNode(CreateTransformStmt); + n->replace = $2; + n->type_name = $5; + n->lang = $7; + n->fromsql = linitial($9); + n->tosql = lsecond($9); + $$ = (Node *)n; + } + ; + +transform_element_list: FROM SQL_P WITH FUNCTION function_with_argtypes ',' TO SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($5, $11); + } + | TO SQL_P WITH FUNCTION function_with_argtypes ',' FROM SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($11, $5); + } + | FROM SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2($5, NULL); + } + | TO SQL_P WITH FUNCTION function_with_argtypes + { + $$ = list_make2(NULL, $5); + } + ; + + +DropTransformStmt: DROP TRANSFORM opt_if_exists FOR Typename LANGUAGE name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_TRANSFORM; + n->objects = list_make1(list_make1($5)); + n->arguments = list_make1(list_make1($7)); + n->behavior = $8; + n->missing_ok = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * * QUERY: * * REINDEX type <name> @@ -13460,6 +13542,7 @@ unreserved_keyword: | SIMPLE | SKIP | SNAPSHOT + | SQL_P | STABLE | STANDALONE_P | START @@ -13479,6 +13562,7 @@ unreserved_keyword: | TEMPORARY | TEXT_P | TRANSACTION + | TRANSFORM | TRIGGER | TRUNCATE | TRUSTED diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index fd09d3ad9e..31e9d4cf8b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -174,6 +174,7 @@ check_xact_readonly(Node *parsetree) case T_CreateTableAsStmt: case T_RefreshMatViewStmt: case T_CreateTableSpaceStmt: + case T_CreateTransformStmt: case T_CreateTrigStmt: case T_CompositeTypeStmt: case T_CreateEnumStmt: @@ -1314,6 +1315,10 @@ ProcessUtilitySlow(Node *parsetree, DefineOpFamily((CreateOpFamilyStmt *) parsetree); break; + case T_CreateTransformStmt: + CreateTransform((CreateTransformStmt *) parsetree); + break; + case T_AlterOpFamilyStmt: AlterOpFamily((AlterOpFamilyStmt *) parsetree); break; @@ -2004,6 +2009,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_POLICY: tag = "DROP POLICY"; break; + case OBJECT_TRANSFORM: + tag = "DROP TRANSFORM"; + break; default: tag = "???"; } @@ -2263,6 +2271,10 @@ CreateCommandTag(Node *parsetree) } break; + case T_CreateTransformStmt: + tag = "CREATE TRANSFORM"; + break; + case T_CreateTrigStmt: tag = "CREATE TRIGGER"; break; @@ -2888,6 +2900,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_CreateTransformStmt: + lev = LOGSTMT_DDL; + break; + case T_AlterOpFamilyStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 29b5b1b894..5ffb712472 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -306,6 +306,7 @@ static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults); static void print_function_rettype(StringInfo buf, HeapTuple proctup); +static void print_function_trftypes(StringInfo buf, HeapTuple proctup); static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, Bitmapset *rels_used); static bool refname_is_unique(char *refname, deparse_namespace *dpns, @@ -1912,9 +1913,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS) StringInfoData buf; StringInfoData dq; HeapTuple proctup; - HeapTuple langtup; Form_pg_proc proc; - Form_pg_language lang; Datum tmp; bool isnull; const char *prosrc; @@ -1937,12 +1936,6 @@ pg_get_functiondef(PG_FUNCTION_ARGS) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", name))); - /* Need its pg_language tuple for the language name */ - langtup = SearchSysCache1(LANGOID, ObjectIdGetDatum(proc->prolang)); - if (!HeapTupleIsValid(langtup)) - elog(ERROR, "cache lookup failed for language %u", proc->prolang); - lang = (Form_pg_language) GETSTRUCT(langtup); - /* * We always qualify the function name, to ensure the right function gets * replaced. @@ -1953,8 +1946,11 @@ pg_get_functiondef(PG_FUNCTION_ARGS) (void) print_function_arguments(&buf, proctup, false, true); appendStringInfoString(&buf, ")\n RETURNS "); print_function_rettype(&buf, proctup); + + print_function_trftypes(&buf, proctup); + appendStringInfo(&buf, "\n LANGUAGE %s\n", - quote_identifier(NameStr(lang->lanname))); + quote_identifier(get_language_name(proc->prolang, false))); /* Emit some miscellaneous options on one line */ oldlen = buf.len; @@ -2074,7 +2070,6 @@ pg_get_functiondef(PG_FUNCTION_ARGS) appendStringInfoChar(&buf, '\n'); - ReleaseSysCache(langtup); ReleaseSysCache(proctup); PG_RETURN_TEXT_P(string_to_text(buf.data)); @@ -2351,6 +2346,30 @@ is_input_argument(int nth, const char *argmodes) } /* + * Append used transformated types to specified buffer + */ +static void +print_function_trftypes(StringInfo buf, HeapTuple proctup) +{ + Oid *trftypes; + int ntypes; + + ntypes = get_func_trftypes(proctup, &trftypes); + if (ntypes > 0) + { + int i; + + appendStringInfoString(buf, "\n TRANSFORM "); + for (i = 0; i < ntypes; i++) + { + if (i != 0) + appendStringInfoString(buf, ", "); + appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i])); + } + } +} + +/* * Get textual representation of a function argument's default value. The * second argument of this function is the argument number among all arguments * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 6a39863890..1dc293297d 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -24,12 +24,14 @@ #include "catalog/pg_amproc.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" +#include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_range.h" #include "catalog/pg_statistic.h" +#include "catalog/pg_transform.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -977,6 +979,30 @@ get_constraint_name(Oid conoid) return NULL; } +/* ---------- LANGUAGE CACHE ---------- */ + +char * +get_language_name(Oid langoid, bool missing_ok) +{ + HeapTuple tp; + + tp = SearchSysCache1(LANGOID, ObjectIdGetDatum(langoid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_language lantup = (Form_pg_language) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(lantup->lanname)); + ReleaseSysCache(tp); + return result; + } + + if (!missing_ok) + elog(ERROR, "cache lookup failed for language %u", + langoid); + return NULL; +} + /* ---------- OPCLASS CACHE ---------- */ /* @@ -1743,6 +1769,51 @@ get_rel_tablespace(Oid relid) } +/* ---------- TRANSFORM CACHE ---------- */ + +Oid +get_transform_fromsql(Oid typid, Oid langid, List *trftypes) +{ + HeapTuple tup; + + if (!list_member_oid(trftypes, typid)) + return InvalidOid; + + tup = SearchSysCache2(TRFTYPELANG, typid, langid); + if (HeapTupleIsValid(tup)) + { + Oid funcid; + + funcid = ((Form_pg_transform) GETSTRUCT(tup))->trffromsql; + ReleaseSysCache(tup); + return funcid; + } + else + return InvalidOid; +} + +Oid +get_transform_tosql(Oid typid, Oid langid, List *trftypes) +{ + HeapTuple tup; + + if (!list_member_oid(trftypes, typid)) + return InvalidOid; + + tup = SearchSysCache2(TRFTYPELANG, typid, langid); + if (HeapTupleIsValid(tup)) + { + Oid funcid; + + funcid = ((Form_pg_transform) GETSTRUCT(tup))->trftosql; + ReleaseSysCache(tup); + return funcid; + } + else + return InvalidOid; +} + + /* ---------- TYPE CACHE ---------- */ /* diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index bd271680e5..644bbcc167 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -56,6 +56,7 @@ #include "catalog/pg_shseclabel.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" +#include "catalog/pg_transform.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_config_map.h" #include "catalog/pg_ts_dict.h" @@ -653,6 +654,28 @@ static const struct cachedesc cacheinfo[] = { }, 4 }, + {TransformRelationId, /* TRFOID */ + TransformOidIndexId, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0, + }, + 16 + }, + {TransformRelationId, /* TRFTYPELANG */ + TransformTypeLangIndexId, + 2, + { + Anum_pg_transform_trftype, + Anum_pg_transform_trflang, + 0, + 0, + }, + 16 + }, {TSConfigMapRelationId, /* TSCONFIGMAP */ TSConfigMapIndexId, 3, diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 204e124355..ebd7ddd387 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -877,6 +877,50 @@ get_func_arg_info(HeapTuple procTup, return numargs; } +/* + * get_func_trftypes + * + * Returns a number of transformated types used by function. + */ +int +get_func_trftypes(HeapTuple procTup, + Oid **p_trftypes) +{ + + Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); + Datum protrftypes; + ArrayType *arr; + int nelems; + bool isNull; + + protrftypes = SysCacheGetAttr(PROCOID, procTup, + Anum_pg_proc_protrftypes, + &isNull); + if (!isNull) + { + /* + * We expect the arrays to be 1-D arrays of the right types; verify + * that. For the OID and char arrays, we don't need to use + * deconstruct_array() since the array data is just going to look like + * a C array of values. + */ + arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */ + nelems = ARR_DIMS(arr)[0]; + if (ARR_NDIM(arr) != 1 || + nelems < 0 || + ARR_HASNULL(arr) || + ARR_ELEMTYPE(arr) != OIDOID) + elog(ERROR, "protrftypes is not a 1-D Oid array"); + Assert(nelems >= procStruct->pronargs); + *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid)); + memcpy(*p_trftypes, ARR_DATA_PTR(arr), + nelems * sizeof(Oid)); + + return nelems; + } + else + return 0; +} /* * get_func_input_arg_names |
