summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorPeter Eisentraut2015-04-26 14:33:14 +0000
committerPeter Eisentraut2015-04-26 14:33:14 +0000
commitcac76582053ef8ea07df65fed0757f352da23705 (patch)
tree6ae01041aa61db9d686638b9d4c3ccd30d7c6487 /src/bin
parentf320cbb615e0374b18836337713239da58705cf3 (diff)
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
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/pg_dump/common.c5
-rw-r--r--src/bin/pg_dump/pg_dump.c273
-rw-r--r--src/bin/pg_dump/pg_dump.h11
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c11
4 files changed, 297 insertions, 3 deletions
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 1a0a5874132..d100514cc21 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -92,6 +92,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
int numRules;
int numProcLangs;
int numCasts;
+ int numTransforms;
int numOpclasses;
int numOpfamilies;
int numConversions;
@@ -202,6 +203,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
getCasts(fout, dopt, &numCasts);
if (g_verbose)
+ write_msg(NULL, "reading transforms\n");
+ getTransforms(fout, &numTransforms);
+
+ if (g_verbose)
write_msg(NULL, "reading table inheritance information\n");
inhinfo = getInherits(fout, &numInherits);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fe08c1b15d2..977b72e3449 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -165,6 +165,7 @@ static void dumpShellType(Archive *fout, DumpOptions *dopt, ShellTypeInfo *stinf
static void dumpProcLang(Archive *fout, DumpOptions *dopt, ProcLangInfo *plang);
static void dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo);
static void dumpCast(Archive *fout, DumpOptions *dopt, CastInfo *cast);
+static void dumpTransform(Archive *fout, DumpOptions *dopt, TransformInfo *transform);
static void dumpOpr(Archive *fout, DumpOptions *dopt, OprInfo *oprinfo);
static void dumpOpclass(Archive *fout, DumpOptions *dopt, OpclassInfo *opcinfo);
static void dumpOpfamily(Archive *fout, DumpOptions *dopt, OpfamilyInfo *opfinfo);
@@ -6566,6 +6567,110 @@ getCasts(Archive *fout, DumpOptions *dopt, int *numCasts)
return castinfo;
}
+static char *
+get_language_name(Archive *fout, Oid langid)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ char *lanname;
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
+ res = ExecuteSqlQueryForSingleRow(fout, query->data);
+ lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
+ destroyPQExpBuffer(query);
+ PQclear(res);
+
+ return lanname;
+}
+
+/*
+ * getTransforms
+ * get basic information about every transform in the system
+ *
+ * numTransforms is set to the number of transforms read in
+ */
+TransformInfo *
+getTransforms(Archive *fout, int *numTransforms)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ TransformInfo *transforminfo;
+ int i_tableoid;
+ int i_oid;
+ int i_trftype;
+ int i_trflang;
+ int i_trffromsql;
+ int i_trftosql;
+
+ /* Transforms didn't exist pre-9.5 */
+ if (fout->remoteVersion < 90500)
+ {
+ *numTransforms = 0;
+ return NULL;
+ }
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, "
+ "trftype, trflang, trffromsql::oid, trftosql::oid "
+ "FROM pg_transform "
+ "ORDER BY 3,4");
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numTransforms = ntups;
+
+ transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_trftype = PQfnumber(res, "trftype");
+ i_trflang = PQfnumber(res, "trflang");
+ i_trffromsql = PQfnumber(res, "trffromsql");
+ i_trftosql = PQfnumber(res, "trftosql");
+
+ for (i = 0; i < ntups; i++)
+ {
+ PQExpBufferData namebuf;
+ TypeInfo *typeInfo;
+ char *lanname;
+
+ transforminfo[i].dobj.objType = DO_TRANSFORM;
+ transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&transforminfo[i].dobj);
+ transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
+ transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
+ transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
+ transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
+
+ /*
+ * Try to name transform as concatenation of type and language name.
+ * This is only used for purposes of sorting. If we fail to find
+ * either, the name will be an empty string.
+ */
+ initPQExpBuffer(&namebuf);
+ typeInfo = findTypeByOid(transforminfo[i].trftype);
+ lanname = get_language_name(fout, transforminfo[i].trflang);
+ if (typeInfo && lanname)
+ appendPQExpBuffer(&namebuf, "%s %s",
+ typeInfo->dobj.name, lanname);
+ transforminfo[i].dobj.name = namebuf.data;
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return transforminfo;
+}
+
/*
* getTableAttrs -
* for each interesting table, read info about its attributes
@@ -8182,6 +8287,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
case DO_CAST:
dumpCast(fout, dopt, (CastInfo *) dobj);
break;
+ case DO_TRANSFORM:
+ dumpTransform(fout, dopt, (TransformInfo *) dobj);
+ break;
case DO_TABLE_DATA:
if (((TableDataInfo *) dobj)->tdtable->relkind == RELKIND_SEQUENCE)
dumpSequenceData(fout, (TableDataInfo *) dobj);
@@ -9989,6 +10097,7 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
char *proallargtypes;
char *proargmodes;
char *proargnames;
+ char *protrftypes;
char *proiswindow;
char *provolatile;
char *proisstrict;
@@ -10021,10 +10130,28 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
selectSourceSchema(fout, finfo->dobj.namespace->dobj.name);
/* Fetch function-specific details */
- if (fout->remoteVersion >= 90200)
+ if (fout->remoteVersion >= 90500)
{
/*
- * proleakproof was added at v9.2
+ * protrftypes was added in 9.5
+ */
+ appendPQExpBuffer(query,
+ "SELECT proretset, prosrc, probin, "
+ "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
+ "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
+ "pg_catalog.pg_get_function_result(oid) AS funcresult, "
+ "array_to_string(protrftypes, ' ') AS protrftypes, "
+ "proiswindow, provolatile, proisstrict, prosecdef, "
+ "proleakproof, proconfig, procost, prorows, "
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ "FROM pg_catalog.pg_proc "
+ "WHERE oid = '%u'::pg_catalog.oid",
+ finfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 90200)
+ {
+ /*
+ * proleakproof was added in 9.2
*/
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
@@ -10173,6 +10300,10 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
funcargs = funciargs = funcresult = NULL;
}
+ if (PQfnumber(res, "protrftypes") != -1)
+ protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
+ else
+ protrftypes = NULL;
proiswindow = PQgetvalue(res, 0, PQfnumber(res, "proiswindow"));
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
@@ -10316,6 +10447,22 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
+ if (protrftypes != NULL && strcmp(protrftypes, "") != 0)
+ {
+ Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
+ int i;
+
+ appendPQExpBufferStr(q, " TRANSFORM ");
+ parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
+ for (i = 0; typeids[i]; i++)
+ {
+ if (i != 0)
+ appendPQExpBufferStr(q, ", ");
+ appendPQExpBuffer(q, "FOR TYPE %s",
+ getFormattedTypeName(fout, typeids[i], zeroAsNone));
+ }
+ }
+
if (proiswindow[0] == 't')
appendPQExpBufferStr(q, " WINDOW");
@@ -10540,6 +10687,127 @@ dumpCast(Archive *fout, DumpOptions *dopt, CastInfo *cast)
}
/*
+ * Dump a transform
+ */
+static void
+dumpTransform(Archive *fout, DumpOptions *dopt, TransformInfo *transform)
+{
+ PQExpBuffer defqry;
+ PQExpBuffer delqry;
+ PQExpBuffer labelq;
+ FuncInfo *fromsqlFuncInfo = NULL;
+ FuncInfo *tosqlFuncInfo = NULL;
+ char *lanname;
+
+ /* Skip if not to be dumped */
+ if (!transform->dobj.dump || dopt->dataOnly)
+ return;
+
+ /* Cannot dump if we don't have the transform functions' info */
+ if (OidIsValid(transform->trffromsql))
+ {
+ fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
+ if (fromsqlFuncInfo == NULL)
+ return;
+ }
+ if (OidIsValid(transform->trftosql))
+ {
+ tosqlFuncInfo = findFuncByOid(transform->trftosql);
+ if (tosqlFuncInfo == NULL)
+ return;
+ }
+
+ /* Make sure we are in proper schema (needed for getFormattedTypeName) */
+ selectSourceSchema(fout, "pg_catalog");
+
+ defqry = createPQExpBuffer();
+ delqry = createPQExpBuffer();
+ labelq = createPQExpBuffer();
+
+ lanname = get_language_name(fout, transform->trflang);
+
+ appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
+ getFormattedTypeName(fout, transform->trftype, zeroAsNone),
+ lanname);
+
+ appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
+ getFormattedTypeName(fout, transform->trftype, zeroAsNone),
+ lanname);
+
+ if (!transform->trffromsql && !transform->trftosql)
+ write_msg(NULL, "WARNING: bogus transform definition, at least one of trffromsql and trftosql should be nonzero\n");
+
+ if (transform->trffromsql)
+ {
+ if (fromsqlFuncInfo)
+ {
+ char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
+
+ /*
+ * Always qualify the function name, in case it is not in
+ * pg_catalog schema (format_function_signature won't qualify
+ * it).
+ */
+ appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
+ fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
+ free(fsig);
+ }
+ else
+ write_msg(NULL, "WARNING: bogus value in pg_transform.trffromsql field\n");
+ }
+
+ if (transform->trftosql)
+ {
+ if (transform->trffromsql)
+ appendPQExpBuffer(defqry, ", ");
+
+ if (tosqlFuncInfo)
+ {
+ char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
+
+ /*
+ * Always qualify the function name, in case it is not in
+ * pg_catalog schema (format_function_signature won't qualify
+ * it).
+ */
+ appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
+ fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
+ free(fsig);
+ }
+ else
+ write_msg(NULL, "WARNING: bogus value in pg_transform.trftosql field\n");
+ }
+
+ appendPQExpBuffer(defqry, ");\n");
+
+ appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
+ getFormattedTypeName(fout, transform->trftype, zeroAsNone),
+ lanname);
+
+ if (dopt->binary_upgrade)
+ binary_upgrade_extension_member(defqry, &transform->dobj, labelq->data);
+
+ ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
+ labelq->data,
+ "pg_catalog", NULL, "",
+ false, "TRANSFORM", SECTION_PRE_DATA,
+ defqry->data, delqry->data, NULL,
+ transform->dobj.dependencies, transform->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Transform Comments */
+ dumpComment(fout, dopt, labelq->data,
+ NULL, "",
+ transform->dobj.catId, 0, transform->dobj.dumpId);
+
+ free(lanname);
+ destroyPQExpBuffer(defqry);
+ destroyPQExpBuffer(delqry);
+ destroyPQExpBuffer(labelq);
+}
+
+
+/*
* dumpOpr
* write out a single operator definition
*/
@@ -15658,6 +15926,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_TSCONFIG:
case DO_FDW:
case DO_FOREIGN_SERVER:
+ case DO_TRANSFORM:
case DO_BLOB:
/* Pre-data objects: must come before the pre-data boundary */
addObjectDependency(preDataBound, dobj->dumpId);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a9d3c1016be..4c796ad6a76 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -70,6 +70,7 @@ typedef enum
DO_FDW,
DO_FOREIGN_SERVER,
DO_DEFAULT_ACL,
+ DO_TRANSFORM,
DO_BLOB,
DO_BLOB_DATA,
DO_PRE_DATA_BOUNDARY,
@@ -376,6 +377,15 @@ typedef struct _castInfo
char castmethod;
} CastInfo;
+typedef struct _transformInfo
+{
+ DumpableObject dobj;
+ Oid trftype;
+ Oid trflang;
+ Oid trffromsql;
+ Oid trftosql;
+} TransformInfo;
+
/* InhInfo isn't a DumpableObject, just temporary state */
typedef struct _inhInfo
{
@@ -534,6 +544,7 @@ extern RuleInfo *getRules(Archive *fout, int *numRules);
extern void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables);
extern ProcLangInfo *getProcLangs(Archive *fout, int *numProcLangs);
extern CastInfo *getCasts(Archive *fout, DumpOptions *dopt, int *numCasts);
+extern TransformInfo *getTransforms(Archive *fout, int *numTransforms);
extern void getTableAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo, int numTables);
extern bool shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno);
extern TSParserInfo *getTSParsers(Archive *fout, int *numTSParsers);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index c5ed593e02e..aa3e20a2d5f 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -28,7 +28,7 @@ static const char *modulename = gettext_noop("sorter");
* by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: collations,
* extensions, text search, foreign-data, materialized view, event trigger,
- * policies, and default ACL objects can't really happen here, so the rather
+ * policies, transforms, and default ACL objects can't really happen here, so the rather
* bogus priorities for them don't matter.
*
* NOTE: object-type priorities must match the section assignments made in
@@ -67,6 +67,7 @@ static const int oldObjectTypePriority[] =
4, /* DO_FDW */
4, /* DO_FOREIGN_SERVER */
19, /* DO_DEFAULT_ACL */
+ 4, /* DO_TRANSFORM */
9, /* DO_BLOB */
12, /* DO_BLOB_DATA */
10, /* DO_PRE_DATA_BOUNDARY */
@@ -116,6 +117,7 @@ static const int newObjectTypePriority[] =
16, /* DO_FDW */
17, /* DO_FOREIGN_SERVER */
31, /* DO_DEFAULT_ACL */
+ 3, /* DO_TRANSFORM */
21, /* DO_BLOB */
24, /* DO_BLOB_DATA */
22, /* DO_PRE_DATA_BOUNDARY */
@@ -1400,6 +1402,13 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
((CastInfo *) obj)->casttarget,
obj->dumpId, obj->catId.oid);
return;
+ case DO_TRANSFORM:
+ snprintf(buf, bufsize,
+ "TRANSFORM %u lang %u (ID %d OID %u)",
+ ((TransformInfo *) obj)->trftype,
+ ((TransformInfo *) obj)->trflang,
+ obj->dumpId, obj->catId.oid);
+ return;
case DO_TABLE_DATA:
snprintf(buf, bufsize,
"TABLE DATA %s (ID %d OID %u)",