<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.111 2002/08/14 02:45:09 ishii Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.112 2002/08/16 23:01:18 tgl Exp $
PostgreSQL documentation
-->
<entry>Get CREATE INDEX command for index</entry>
</row>
<row>
+ <entry><function>pg_get_constraintdef</function>(<parameter>constraintOID</parameter>)</entry>
+ <entry><type>text</type></entry>
+ <entry>Get definition of a constraint</entry>
+ </row>
+ <row>
<entry><function>pg_get_userbyid</function>(<parameter>userid</parameter>)</entry>
<entry><type>name</type></entry>
<entry>Get user name given ID</entry>
</indexterm>
<indexterm zone="functions-misc">
+ <primary>pg_get_constraintdef</primary>
+ </indexterm>
+
+ <indexterm zone="functions-misc">
<primary>pg_get_userbyid</primary>
</indexterm>
<para>
These functions extract information from the system catalogs.
- <function>pg_get_viewdef()</function>, <function>pg_get_ruledef()</function>, and
- <function>pg_get_indexdef()</function> respectively reconstruct the creating
- command for a view, rule, or index. (Note that this is a decompiled
+ <function>pg_get_viewdef()</function>,
+ <function>pg_get_ruledef()</function>,
+ <function>pg_get_indexdef()</function>, and
+ <function>pg_get_constraintdef()</function> respectively reconstruct the
+ creating command for a view, rule, index, or constraint.
+ (Note that this is a decompiled
reconstruction, not the verbatim text of the command.)
+ At present <function>pg_get_constraintdef()</function> only works for
+ foreign-key constraints.
<function>pg_get_userbyid()</function> extracts a user's name given a
<structfield>usesysid</structfield> value.
</para>
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.115 2002/08/16 20:55:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.116 2002/08/16 23:01:19 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include <unistd.h>
#include <fcntl.h>
+#include "access/genam.h"
+#include "catalog/catname.h"
#include "catalog/heap.h"
#include "catalog/index.h"
+#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_cast.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+#include "utils/array.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
* ----------
*/
static text *pg_do_getviewdef(Oid viewoid);
+static void decompile_column_index_array(Datum column_index_array, Oid relId,
+ StringInfo buf);
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
}
+/*
+ * pg_get_constraintdef
+ *
+ * Returns the definition for the constraint, ie, everything that needs to
+ * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
+ *
+ * XXX The present implementation only works for foreign-key constraints, but
+ * it could and should handle anything pg_constraint stores.
+ */
+Datum
+pg_get_constraintdef(PG_FUNCTION_ARGS)
+{
+ Oid constraintId = PG_GETARG_OID(0);
+ text *result;
+ StringInfoData buf;
+ int len;
+ Relation conDesc;
+ SysScanDesc conscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+ Form_pg_constraint conForm;
+
+ /*
+ * Fetch the pg_constraint row. There's no syscache for pg_constraint
+ * so we must do it the hard way.
+ */
+ conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(constraintId));
+
+ conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(conscan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "Failed to find constraint with OID %u", constraintId);
+ conForm = (Form_pg_constraint) GETSTRUCT(tup);
+
+ initStringInfo(&buf);
+
+ switch (conForm->contype)
+ {
+ case CONSTRAINT_FOREIGN:
+ {
+ Datum val;
+ bool isnull;
+ const char *string;
+
+ /* Start off the constraint definition */
+ appendStringInfo(&buf, "FOREIGN KEY (");
+
+ /* Fetch and build referencing-column list */
+ val = heap_getattr(tup, Anum_pg_constraint_conkey,
+ RelationGetDescr(conDesc), &isnull);
+ if (isnull)
+ elog(ERROR, "pg_get_constraintdef: Null conkey for constraint %u",
+ constraintId);
+
+ decompile_column_index_array(val, conForm->conrelid, &buf);
+
+ /* add foreign relation name */
+ appendStringInfo(&buf, ") REFERENCES %s(",
+ generate_relation_name(conForm->confrelid));
+
+ /* Fetch and build referenced-column list */
+ val = heap_getattr(tup, Anum_pg_constraint_confkey,
+ RelationGetDescr(conDesc), &isnull);
+ if (isnull)
+ elog(ERROR, "pg_get_constraintdef: Null confkey for constraint %u",
+ constraintId);
+
+ decompile_column_index_array(val, conForm->confrelid, &buf);
+
+ appendStringInfo(&buf, ")");
+
+ /* Add match type */
+ switch (conForm->confmatchtype)
+ {
+ case FKCONSTR_MATCH_FULL:
+ string = " MATCH FULL";
+ break;
+ case FKCONSTR_MATCH_PARTIAL:
+ string = " MATCH PARTIAL";
+ break;
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ string = "";
+ break;
+ default:
+ elog(ERROR, "pg_get_constraintdef: Unknown confmatchtype '%c' for constraint %u",
+ conForm->confmatchtype, constraintId);
+ string = ""; /* keep compiler quiet */
+ break;
+ }
+ appendStringInfo(&buf, "%s", string);
+
+ /* Add ON UPDATE and ON DELETE clauses */
+ switch (conForm->confupdtype)
+ {
+ case FKCONSTR_ACTION_NOACTION:
+ string = "NO ACTION";
+ break;
+ case FKCONSTR_ACTION_RESTRICT:
+ string = "RESTRICT";
+ break;
+ case FKCONSTR_ACTION_CASCADE:
+ string = "CASCADE";
+ break;
+ case FKCONSTR_ACTION_SETNULL:
+ string = "SET NULL";
+ break;
+ case FKCONSTR_ACTION_SETDEFAULT:
+ string = "SET DEFAULT";
+ break;
+ default:
+ elog(ERROR, "pg_get_constraintdef: Unknown confupdtype '%c' for constraint %u",
+ conForm->confupdtype, constraintId);
+ string = ""; /* keep compiler quiet */
+ break;
+ }
+ appendStringInfo(&buf, " ON UPDATE %s", string);
+
+ switch (conForm->confdeltype)
+ {
+ case FKCONSTR_ACTION_NOACTION:
+ string = "NO ACTION";
+ break;
+ case FKCONSTR_ACTION_RESTRICT:
+ string = "RESTRICT";
+ break;
+ case FKCONSTR_ACTION_CASCADE:
+ string = "CASCADE";
+ break;
+ case FKCONSTR_ACTION_SETNULL:
+ string = "SET NULL";
+ break;
+ case FKCONSTR_ACTION_SETDEFAULT:
+ string = "SET DEFAULT";
+ break;
+ default:
+ elog(ERROR, "pg_get_constraintdef: Unknown confdeltype '%c' for constraint %u",
+ conForm->confdeltype, constraintId);
+ string = ""; /* keep compiler quiet */
+ break;
+ }
+ appendStringInfo(&buf, " ON DELETE %s", string);
+
+ break;
+ }
+
+ /*
+ * XXX Add more code here for other contypes
+ */
+ default:
+ elog(ERROR, "pg_get_constraintdef: unsupported constraint type '%c'",
+ conForm->contype);
+ break;
+ }
+
+ /* Record the results */
+ len = buf.len + VARHDRSZ;
+ result = (text *) palloc(len);
+ VARATT_SIZEP(result) = len;
+ memcpy(VARDATA(result), buf.data, buf.len);
+
+ /* Cleanup */
+ pfree(buf.data);
+ systable_endscan(conscan);
+ heap_close(conDesc, AccessShareLock);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+
+/*
+ * Convert an int16[] Datum into a comma-separated list of column names
+ * for the indicated relation; append the list to buf.
+ */
+static void
+decompile_column_index_array(Datum column_index_array, Oid relId,
+ StringInfo buf)
+{
+ Datum *keys;
+ int nKeys;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ true, 2, 's',
+ &keys, &nKeys);
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+
+ colName = get_attname(relId, DatumGetInt16(keys[j]));
+
+ if (j == 0)
+ appendStringInfo(buf, "%s",
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s",
+ quote_identifier(colName));
+ }
+}
+
+
/* ----------
* get_expr - Decompile an expression tree
*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.283 2002/08/16 21:03:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.284 2002/08/16 23:01:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *tag, const char *nspname,
const char *usename, const char *acl, const char *objoid);
+static void dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables);
static void dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables);
static void dumpRules(Archive *fout, TableInfo *tblinfo, int numTables);
static void formatStringLiteral(PQExpBuffer buf, const char *str,
if (!dataOnly) /* dump indexes and triggers at the end
* for performance */
{
+ dumpConstraints(g_fout, tblinfo, numTables);
dumpTriggers(g_fout, tblinfo, numTables);
dumpRules(g_fout, tblinfo, numTables);
}
destroyPQExpBuffer(delqry);
}
+/*
+ * dumpConstraints
+ *
+ * Dump out constraints after all table creation statements in
+ * an alter table format. Currently handles foreign keys only.
+ *
+ * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that
+ * the current table data is not processed
+ */
+static void
+dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables)
+{
+ int i,
+ j;
+ PQExpBuffer query;
+ PQExpBuffer delqry;
+ PGresult *res;
+ int i_condef,
+ i_conoid,
+ i_conname;
+ int ntups;
+
+ /* pg_constraint was created in 7.3, so nothing to do if older */
+ if (g_fout->remoteVersion < 70300)
+ return;
+
+ query = createPQExpBuffer();
+ delqry = createPQExpBuffer();
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ if (tbinfo->ntrig == 0 || !tbinfo->dump)
+ continue;
+
+ if (g_verbose)
+ write_msg(NULL, "dumping triggers for table %s\n",
+ tbinfo->relname);
+
+ /* select table schema to ensure regproc name is qualified if needed */
+ selectSourceSchema(tbinfo->relnamespace->nspname);
+
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query,
+ "SELECT oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) as condef "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE conrelid = '%s'::pg_catalog.oid "
+ "AND contype = 'f'",
+ tbinfo->oid);
+ res = PQexec(g_conn, query->data);
+ if (!res ||
+ PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ write_msg(NULL, "query to obtain list of foreign key definitions failed: %s", PQerrorMessage(g_conn));
+ exit_nicely();
+ }
+ ntups = PQntuples(res);
+
+ i_conoid = PQfnumber(res, "oid");
+ i_conname = PQfnumber(res, "conname");
+ i_condef = PQfnumber(res, "condef");
+
+ for (j = 0; j < ntups; j++)
+ {
+ const char *conOid = PQgetvalue(res, j, i_conoid);
+ const char *conName = PQgetvalue(res, j, i_conname);
+ const char *conDef = PQgetvalue(res, j, i_condef);
+
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "ALTER TABLE ONLY %s ",
+ fmtId(tbinfo->relname, force_quotes));
+ appendPQExpBuffer(query, "ADD CONSTRAINT %s %s;\n",
+ fmtId(conName, force_quotes),
+ conDef);
+
+ /* DROP must be fully qualified in case same name appears in pg_catalog */
+ resetPQExpBuffer(delqry);
+ appendPQExpBuffer(delqry, "ALTER TABLE ONLY %s.",
+ fmtId(tbinfo->relnamespace->nspname, force_quotes));
+ appendPQExpBuffer(delqry, "%s ",
+ fmtId(tbinfo->relname, force_quotes));
+ appendPQExpBuffer(delqry, "DROP CONSTRAINT %s;\n",
+ fmtId(conName, force_quotes));
+
+ ArchiveEntry(fout, conOid,
+ conName,
+ tbinfo->relnamespace->nspname,
+ tbinfo->usename,
+ "CONSTRAINT", NULL,
+ query->data, delqry->data,
+ NULL, NULL, NULL);
+
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "CONSTRAINT %s ",
+ fmtId(conName, force_quotes));
+ appendPQExpBuffer(query, "ON %s",
+ fmtId(tbinfo->relname, force_quotes));
+
+ dumpComment(fout, query->data,
+ tbinfo->relnamespace->nspname, tbinfo->usename,
+ conOid, "pg_constraint", 0, NULL);
+ }
+
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(delqry);
+}
static void
dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 70300)
{
+ /* We ignore triggers that are tied to a foreign-key constraint */
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, oid, "
"tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
- "from pg_catalog.pg_trigger "
- "where tgrelid = '%s'::pg_catalog.oid",
+ "from pg_catalog.pg_trigger t "
+ "where tgrelid = '%s'::pg_catalog.oid "
+ "and (not tgisconstraint "
+ " OR NOT EXISTS"
+ " (SELECT 1 FROM pg_catalog.pg_depend d "
+ " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
+ " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
tbinfo->oid);
}
else
exit_nicely();
}
ntups = PQntuples(res);
- if (ntups != tbinfo->ntrig)
+ /*
+ * We may have less triggers than recorded due to constraint triggers
+ * which are dumped by dumpConstraints
+ */
+ if (ntups > tbinfo->ntrig)
{
write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
tbinfo->ntrig, tbinfo->relname, ntups);
*
* Copyright 2000-2002 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.61 2002/08/15 16:36:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.62 2002/08/16 23:01:19 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
PGresult *result1 = NULL,
*result2 = NULL,
*result3 = NULL,
- *result4 = NULL;
- int index_count = 0,
- check_count = 0,
+ *result4 = NULL,
+ *result5 = NULL;
+ int check_count = 0,
+ index_count = 0,
+ foreignkey_count = 0,
rule_count = 0,
trigger_count = 0;
int count_footers = 0;
rule_count = PQntuples(result3);
}
- /* count triggers */
+ /* count triggers (but ignore foreign-key triggers) */
if (tableinfo.triggers)
{
printfPQExpBuffer(&buf,
"SELECT t.tgname\n"
"FROM pg_catalog.pg_trigger t\n"
- "WHERE t.tgrelid = '%s'",
+ "WHERE t.tgrelid = '%s' "
+ "and (not tgisconstraint "
+ " OR NOT EXISTS"
+ " (SELECT 1 FROM pg_catalog.pg_depend d "
+ " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
+ " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
oid);
result4 = PSQLexec(buf.data);
if (!result4)
trigger_count = PQntuples(result4);
}
- footers = xmalloc((index_count + check_count + rule_count + trigger_count + 1)
+ /* count foreign-key constraints (there are none if no triggers) */
+ if (tableinfo.triggers)
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT conname,\n"
+ " pg_catalog.pg_get_constraintdef(oid) as condef\n"
+ "FROM pg_catalog.pg_constraint r\n"
+ "WHERE r.conrelid = '%s' AND r.contype = 'f'",
+ oid);
+ result5 = PSQLexec(buf.data);
+ if (!result5)
+ goto error_return;
+ else
+ foreignkey_count = PQntuples(result5);
+ }
+
+ footers = xmalloc((index_count + check_count + rule_count + trigger_count + foreignkey_count + 1)
* sizeof(*footers));
/* print indexes */
footers[count_footers++] = xstrdup(buf.data);
}
+ /* print foreign key constraints */
+ for (i = 0; i < foreignkey_count; i++)
+ {
+ char *s = _("Foreign Key constraints");
+
+ if (i == 0)
+ printfPQExpBuffer(&buf, _("%s: %s %s"),
+ s,
+ PQgetvalue(result5, i, 0),
+ PQgetvalue(result5, i, 1));
+ else
+ printfPQExpBuffer(&buf, _("%*s %s %s"),
+ (int) strlen(s), "",
+ PQgetvalue(result5, i, 0),
+ PQgetvalue(result5, i, 1));
+ if (i < foreignkey_count - 1)
+ appendPQExpBuffer(&buf, ",");
+
+ footers[count_footers++] = xstrdup(buf.data);
+ }
+
/* print rules */
for (i = 0; i < rule_count; i++)
{
PQclear(result2);
PQclear(result3);
PQclear(result4);
+ PQclear(result5);
}
printTable(title.data, headers,
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.148 2002/08/13 20:40:44 momjian Exp $
+ * $Id: catversion.h,v 1.149 2002/08/16 23:01:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200208131
+#define CATALOG_VERSION_NO 200208161
#endif
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.254 2002/08/15 02:51:27 momjian Exp $
+ * $Id: pg_proc.h,v 1.255 2002/08/16 23:01:20 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
DESCR("user name by UID (with fallback)");
DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 f f t f s 1 25 "26" pg_get_indexdef - _null_ ));
DESCR("index description");
+DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 f f t f s 1 25 "26" pg_get_constraintdef - _null_ ));
+DESCR("constraint description");
DATA(insert OID = 1716 ( pg_get_expr PGNSP PGUID 12 f f t f s 2 25 "25 26" pg_get_expr - _null_ ));
DESCR("deparse an encoded expression");
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: builtins.h,v 1.191 2002/08/15 02:51:27 momjian Exp $
+ * $Id: builtins.h,v 1.192 2002/08/16 23:01:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum pg_get_viewdef(PG_FUNCTION_ARGS);
extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS);
extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
+extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS);
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext,