mirror of
https://github.com/postgres/postgres.git
synced 2025-12-04 12:02:48 +03:00
Add transforms feature
This provides a mechanism for specifying conversions between SQL data types and procedural languages. As examples, there are transforms for hstore and ltree for PL/Perl and PL/Python. reviews by Pavel Stěhule and Andres Freund
This commit is contained in:
@@ -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 \
|
||||
)
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"
|
||||
@@ -334,6 +335,12 @@ static const ObjectPropertyType ObjectProperty[] =
|
||||
ACL_KIND_TABLESPACE,
|
||||
true
|
||||
},
|
||||
{
|
||||
TransformRelationId,
|
||||
TransformOidIndexId,
|
||||
TRFOID,
|
||||
InvalidAttrNumber
|
||||
},
|
||||
{
|
||||
TriggerRelationId,
|
||||
TriggerOidIndexId,
|
||||
@@ -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;
|
||||
|
||||
@@ -545,6 +545,7 @@ AggregateCreate(const char *aggName,
|
||||
parameterModes, /* parameterModes */
|
||||
parameterNames, /* parameterNames */
|
||||
parameterDefaults, /* parameterDefaults */
|
||||
PointerGetDatum(NULL), /* trftypes */
|
||||
PointerGetDatum(NULL), /* proconfig */
|
||||
1, /* procost */
|
||||
0); /* prorows */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1779,6 +1779,18 @@ _equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportFore
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
@@ -7297,6 +7329,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:
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
@@ -2350,6 +2345,30 @@ is_input_argument(int nth, const char *argmodes)
|
||||
|| argmodes[nth] == PROARGMODE_VARIADIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
71
src/backend/utils/cache/lsyscache.c
vendored
71
src/backend/utils/cache/lsyscache.c
vendored
@@ -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 ---------- */
|
||||
|
||||
/*
|
||||
|
||||
23
src/backend/utils/cache/syscache.c
vendored
23
src/backend/utils/cache/syscache.c
vendored
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user