? GNUmakefile ? config.log ? config.status ? src/Makefile.global ? src/bin/pg_dump/.pg_dump.c.swp ? src/bin/pg_dump/pg_dump ? src/bin/pg_dump/pg_dumpall ? src/bin/pg_dump/pg_restore ? src/bin/psql/psql ? src/include/pg_config.h ? src/include/stamp-h ? src/interfaces/libpq/libpq.so.2 Index: doc/src/sgml/func.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.108 diff -c -r1.108 func.sgml *** doc/src/sgml/func.sgml 2002/08/06 05:40:44 1.108 --- doc/src/sgml/func.sgml 2002/08/16 19:37:59 *************** *** 4765,4770 **** --- 4765,4775 ---- name[] names of schemas in search path optionally including implicit schemas + + current_database() + name + name of current database + *************** *** 5024,5029 **** --- 5029,5039 ---- + + pg_get_fkeydef(relation oid,constraintname) + text + Get ALTER TABLE ADD FOREIGN KEY command for foreign keys + pg_get_viewdef(viewname) text Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.112 diff -c -r1.112 ruleutils.c *** src/backend/utils/adt/ruleutils.c 2002/07/18 23:11:28 1.112 --- src/backend/utils/adt/ruleutils.c 2002/08/16 19:38:00 *************** *** 40,49 **** --- 40,54 ---- #include #include + #include "access/genam.h" + #include "access/heapam.h" #include "catalog/heap.h" + #include "catalog/catname.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" *************** *** 60,65 **** --- 65,72 ---- #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" + #include "utils/array.h" + #include "utils/fmgroids.h" #include "utils/lsyscache.h" *************** *** 521,526 **** --- 528,737 ---- PG_RETURN_TEXT_P(indexdef); } + /* + * pg_get_fkeydef + * + * Returns the definitition statement required to recreate the foreign + * key structure + */ + Datum + pg_get_fkeydef(PG_FUNCTION_ARGS) + { + Oid relId = PG_GETARG_OID(0); + Name cname = PG_GETARG_NAME(1); + text *result; + StringInfoData buf; + Relation conDesc; + HeapTuple tup; + Datum val; + char *string = NULL; + SysScanDesc rcscan; + ScanKeyData skey[2]; + int i = 0; + int len; + bool isnull; + Form_pg_constraint fkCon; + + conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); + + ScanKeyEntryInitialize(&skey[i++], + 0, Anum_pg_constraint_conrelid, F_OIDEQ, + ObjectIdGetDatum(relId)); + + rcscan = systable_beginscan(conDesc, ConstraintRelidIndex, true, + SnapshotNow, + i, skey); + + /* + * Loop until we find the constraint with the requested name + * or an error is thrown due to hitting the end of the list. + */ + for (;;) + { + tup = systable_getnext(rcscan); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "syscache lookup for foreign key %s failed on relation %u", + NameStr(*cname), relId); + + fkCon = (Form_pg_constraint) GETSTRUCT(tup); + + /* Leave the loop once we've found the name we want */ + if (strcmp(NameStr(fkCon->conname), NameStr(*cname)) == 0) + break; + } + + /* We only deal in foreign key constraints */ + if (fkCon->contype != CONSTRAINT_FOREIGN) + elog(ERROR, "pg_get_fkeydef: %s is not a foreign key constraint", NameStr(*cname)); + + /* Start off the statement */ + initStringInfo(&buf); + appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (", + generate_relation_name(fkCon->conrelid), + quote_identifier(NameStr(fkCon->conname))); + + /* Fetch and build column list */ + val = heap_getattr(tup, Anum_pg_constraint_conkey, + RelationGetDescr(conDesc), &isnull); + + if (isnull) + elog(ERROR, "get_pg_fkeydef: Null conkey array for constraint %s", + NameStr(*cname)); + else + { + Datum *keys; + int j; + int nKeys; + deconstruct_array(DatumGetArrayTypeP(val), + true, 2, 's', + &keys, &nKeys); + + for (j = 0; j < nKeys; j++) + { + char *colName; + + colName = get_attname(fkCon->conrelid, + DatumGetInt16(keys[j])); + + if (j == 0) + appendStringInfo(&buf, "%s", + quote_identifier(colName)); + else + appendStringInfo(&buf, ", %s", + quote_identifier(colName)); + } + } + + appendStringInfo(&buf, ")"); + + /* Add Foreign relation and key attributes */ + appendStringInfo(&buf, " REFERENCES %s(", + generate_relation_name(fkCon->confrelid)); + + /* Fetch and build column list */ + val = heap_getattr(tup, Anum_pg_constraint_confkey, + RelationGetDescr(conDesc), &isnull); + + if (isnull) + elog(ERROR, "get_pg_fkeydef: Null confkey array for constraint %s", + NameStr(*cname)); + else + { + Datum *keys; + int j; + int nKeys; + deconstruct_array(DatumGetArrayTypeP(val), + true, 2, 's', + &keys, &nKeys); + + for (j = 0; j < nKeys; j++) + { + char *colName; + + colName = get_attname(fkCon->confrelid, + DatumGetInt16(keys[j])); + + if (j == 0) + appendStringInfo(&buf, "%s", + quote_identifier(colName)); + else + appendStringInfo(&buf, ", %s", + quote_identifier(colName)); + } + } + + appendStringInfo(&buf, ")"); + + /* Add match type */ + switch (fkCon->confmatchtype) + { + case 'p': + string = " MATCH PARTIAL"; + break; + case 'f': + string = " MATCH FULL"; + break; + default: + string = ""; + break; + } + appendStringInfo(&buf, "%s", string); + + /* Add ON UPDATE and ON DELETE clause */ + switch (fkCon->confupdtype) + { + case 'r': + string = "RESTRICT"; + break; + case 'c': + string = "CASCADE"; + break; + case 'n': + string = "SET NULL"; + break; + case 'd': + string = "SET DEFAULT"; + break; + case 'a': + string = "NO ACTION"; + break; + } + appendStringInfo(&buf, " ON UPDATE %s", + string); + + switch (fkCon->confdeltype) + { + case 'r': + string = "RESTRICT"; + break; + case 'c': + string = "CASCADE"; + break; + case 'n': + string = "SET NULL"; + break; + case 'd': + string = "SET DEFAULT"; + break; + case 'a': + string = "NO ACTION"; + break; + } + appendStringInfo(&buf, " ON DELETE %s", + string); + + /* Cleanup */ + systable_endscan(rcscan); + heap_close(conDesc, RowExclusiveLock); + + /* Record the results */ + len = buf.len + VARHDRSZ; + result = (text *) palloc(len); + VARATT_SIZEP(result) = len; + memcpy(VARDATA(result), buf.data, buf.len); + + PG_RETURN_TEXT_P(result); + } /* ---------- * get_expr - Decompile an expression tree Index: src/bin/pg_dump/pg_dump.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/pg_dump/pg_dump.c,v retrieving revision 1.280 diff -c -r1.280 pg_dump.c *** src/bin/pg_dump/pg_dump.c 2002/08/04 05:03:29 1.280 --- src/bin/pg_dump/pg_dump.c 2002/08/16 19:38:02 *************** *** 107,112 **** --- 107,113 ---- 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, *************** *** 616,621 **** --- 617,623 ---- { dumpTriggers(g_fout, tblinfo, numTables); dumpRules(g_fout, tblinfo, numTables); + dumpConstraints(g_fout, tblinfo, numTables); } /* Now sort the output nicely */ *************** *** 5680,5686 **** --- 5682,5789 ---- 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 = createPQExpBuffer(); + PQExpBuffer delqry = createPQExpBuffer(); + PGresult *res; + int i_condef, + i_conoid, + i_conname; + int ntups; + + /* pg_constraint was created in 7.3 */ + if (g_fout->remoteVersion >= 70300) + { + 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 pg_get_fkeydef(conrelid, conname) as condef, " + "oid, conname " + "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_condef = PQfnumber(res, "condef"); + i_conoid = PQfnumber(res, "oid"); + i_conname = PQfnumber(res, "conname"); + + for (j = 0; j < ntups; j++) + { + const char *conName = PQgetvalue(res, j, i_conname); + const char *conOid = PQgetvalue(res, j, i_conoid); + const char *conDef = PQgetvalue(res, j, i_condef); + + resetPQExpBuffer(query); + /* + * Creation query is built by pg_get_fkeydef. + * We don't need to DROP Foreign keys as the + * table drop will take care of that. + */ + appendPQExpBuffer(query, "%s;", + conDef); + + 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) { *************** *** 5728,5734 **** "tgconstrrelid, tginitdeferred, oid, " "tgconstrrelid::pg_catalog.regclass as tgconstrrelname " "from pg_catalog.pg_trigger " ! "where tgrelid = '%s'::pg_catalog.oid", tbinfo->oid); } else --- 5831,5842 ---- "tgconstrrelid, tginitdeferred, oid, " "tgconstrrelid::pg_catalog.regclass as tgconstrrelname " "from pg_catalog.pg_trigger " ! "where tgrelid = '%s'::pg_catalog.oid " ! "and (tgisconstraint is false " ! " OR NOT EXISTS" ! " (SELECT * FROM pg_depend " ! " JOIN pg_constraint con ON (con.oid = refobjid) " ! " WHERE con.contype = 'f' and objid = pg_trigger.oid)) ", tbinfo->oid); } else *************** *** 5752,5758 **** exit_nicely(); } ntups = PQntuples(res); ! if (ntups != tbinfo->ntrig) { write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n", tbinfo->ntrig, tbinfo->relname, ntups); --- 5860,5870 ---- exit_nicely(); } ntups = PQntuples(res); ! /* ! * 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); Index: src/bin/psql/describe.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/describe.c,v retrieving revision 1.57 diff -c -r1.57 describe.c *** src/bin/psql/describe.c 2002/08/02 18:15:08 1.57 --- src/bin/psql/describe.c 2002/08/16 19:38:02 *************** *** 396,402 **** ") AS tt,\n" "pg_description d\n" ! "WHERE tt.oid = d.objoid and tt.tableoid = d.classoid and d.objsubid = 0\n", _("Name"), _("Object"), _("Description"), _("aggregate"), _("function"), _("operator"), --- 396,407 ---- ") AS tt,\n" "pg_description d\n" ! "WHERE (t.tgisconstraint IS FALSE\n" ! " OR NOT EXISTS\n" ! " (SELECT * FROM pg_depend\n" ! " JOIN pg_constraint con ON (con.oid = refobjid)\n" ! " WHERE con.contype = 'f' and objid = t.oid))\n" ! "AND tt.oid = d.objoid and tt.tableoid = d.classoid and d.objsubid = 0\n", _("Name"), _("Object"), _("Description"), _("aggregate"), _("function"), _("operator"), *************** *** 745,753 **** PGresult *result1 = NULL, *result2 = NULL, *result3 = NULL, ! *result4 = NULL; ! int index_count = 0, ! check_count = 0, rule_count = 0, trigger_count = 0; int count_footers = 0; --- 750,760 ---- PGresult *result1 = NULL, *result2 = NULL, *result3 = NULL, ! *result4 = NULL, ! *result5 = NULL; ! int check_count = 0, ! index_count = 0, ! foreignkey_count = 0, rule_count = 0, trigger_count = 0; int count_footers = 0; *************** *** 801,813 **** rule_count = PQntuples(result3); } ! /* count triggers */ if (tableinfo.triggers) { printfPQExpBuffer(&buf, "SELECT t.tgname\n" "FROM pg_trigger t, pg_class c\n" ! "WHERE c.relname='%s' AND c.oid = t.tgrelid", name); result4 = PSQLexec(buf.data); if (!result4) --- 808,826 ---- rule_count = PQntuples(result3); } ! /* count triggers but ignore foreign key triggers */ if (tableinfo.triggers) { printfPQExpBuffer(&buf, "SELECT t.tgname\n" "FROM pg_trigger t, pg_class c\n" ! "WHERE c.relname='%s' AND c.oid = t.tgrelid\n" ! "AND (tgisconstraint IS FALSE\n" ! " OR NOT EXISTS\n" ! " (SELECT * FROM pg_depend\n" ! " JOIN pg_constraint con ON (con.oid = refobjid)\n" ! " WHERE con.contype = 'f' and objid = t.oid))\n" ! " ORDER BY t.tgname", name); result4 = PSQLexec(buf.data); if (!result4) *************** *** 816,821 **** --- 829,849 ---- trigger_count = PQntuples(result4); } + /* Fetch the Foreign Key information */ + printfPQExpBuffer(&buf, + "SELECT conname,\n" + "SUBSTR(pg_get_fkeydef(c.oid, conname),\n" + " POSITION('FOREIGN KEY' in pg_get_fkeydef(c.oid, conname)\n" + " )) as condef\n" + "FROM pg_class as c JOIN pg_constraint ON (c.oid = conrelid)\n" + "WHERE c.relname = '%s' AND contype = 'f'", + name); + result5 = PSQLexec(buf.data); + if (!result5) + goto error_return; + else + foreignkey_count = PQntuples(result5); + footers = xmalloc((index_count + check_count + rule_count + trigger_count + 1) * sizeof(*footers)); *************** *** 862,867 **** --- 890,916 ---- (int) strlen(s), "", PQgetvalue(result2, i, 1), PQgetvalue(result2, i, 0)); + 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); } Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v retrieving revision 1.252 diff -c -r1.252 pg_proc.h *** src/include/catalog/pg_proc.h 2002/08/06 05:40:45 1.252 --- src/include/catalog/pg_proc.h 2002/08/16 19:38:03 *************** *** 2164,2169 **** --- 2164,2171 ---- DESCR("greater-than-or-equal"); /* System-view support functions */ + DATA(insert OID = 1387 ( pg_get_fkeydef PGNSP PGUID 12 f f t f s 2 25 "26 19" pg_get_fkeydef - _null_ )); + DESCR("alter table foreign key creation statement"); DATA(insert OID = 1573 ( pg_get_ruledef PGNSP PGUID 12 f f t f s 1 25 "26" pg_get_ruledef - _null_ )); DESCR("source text of a rule"); DATA(insert OID = 1640 ( pg_get_viewdef PGNSP PGUID 12 f f t f s 1 25 "25" pg_get_viewdef_name - _null_ )); Index: src/include/utils/builtins.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.189 diff -c -r1.189 builtins.h *** src/include/utils/builtins.h 2002/08/06 14:11:05 1.189 --- src/include/utils/builtins.h 2002/08/16 19:38:04 *************** *** 347,352 **** --- 347,353 ---- extern char *format_operator(Oid operator_oid); /* ruleutils.c */ + extern Datum pg_get_fkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_ruledef(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS);