public inbox for [email protected]
help / color / mirror / Atom feedFrom: Ayush Tiwari <[email protected]>
To: Daniel Gustafsson <[email protected]>
To: Nathan Bossart <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Cc: Álvaro Herrera <[email protected]>
Cc: Tom Lane <[email protected]>
Subject: Re: Remove the refint contrib module (for v20)
Date: Tue, 9 Jun 2026 10:16:37 +0530
Message-ID: <CAJTYsWWm+67r=yf2bCY9bbriYKaK3q79sWJpgqbHQ3endHe9gA@mail.gmail.com> (raw)
In-Reply-To: <CAJTYsWXoWWyB6JNAqi2Fpv7DgapM5TbHei8G=UsmWgXkSAccbg@mail.gmail.com>
References: <CAJTYsWUHq8Ohc6-N-xamOPYz-q3qUYMtwQX-1=Zi=5N1Q_GSEQ@mail.gmail.com>
<aiM6A5ohSuZRHlvH@nathan>
<[email protected]>
<CAJTYsWXoWWyB6JNAqi2Fpv7DgapM5TbHei8G=UsmWgXkSAccbg@mail.gmail.com>
Hi,
On Tue, 9 Jun 2026 at 00:44, Ayush Tiwari <[email protected]>
wrote:
> Hi,
>
> On Sat, 6 Jun 2026 at 03:35, Daniel Gustafsson <[email protected]> wrote:
>
>> > On 5 Jun 2026, at 23:05, Nathan Bossart <[email protected]>
>> wrote:
>> >
>> > On Sat, Jun 06, 2026 at 12:57:56AM +0530, Ayush Tiwari wrote:
>> >> Thoughts? Does this look like the right approach, or is there a
>> >> reason to keep refint around?
>> >
>> > +1. Do we need an appendix-obsolete entry for the docs?
>>
>> Seems like a reasonable thing to add. Once merged, we should probably
>> add (and
>> backpatch) a mention to the docs that the functionality will be removed
>> in v20.
>>
>
> v2 attached. 0001 now adds an appendix-obsolete entry under the same
> contrib-spi-refint id, so existing /current/ documentation links keep
> resolving (mirrors the auth-radius pattern). 0002 is unchanged.
>
> The back-patched "will be removed in v20" notice on v19 (and older)
> I can send it post this merge, as suggested.
>
> Please let me know if I've missed something more. Or if I should
> split v2-0001 into 2 parts, one for removal and the other for appendix-
> obsolete entry.
>
Rebasing.
Regards,
Ayush
Attachments:
[application/octet-stream] v3-0002-Handle-refint-removal-in-cross-version-pg_upgrade-te.patch (1.9K, ../CAJTYsWWm+67r=yf2bCY9bbriYKaK3q79sWJpgqbHQ3endHe9gA@mail.gmail.com/3-v3-0002-Handle-refint-removal-in-cross-version-pg_upgrade-te.patch)
download | inline diff:
From 291d9c390a86820560a6043c6e8672a82f8b421e Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <[email protected]>
Date: Mon, 8 Jun 2026 19:06:31 +0000
Subject: [PATCH v3 2/2] Handle refint removal in cross-version pg_upgrade
tests.
The previous commit removes the refint extension in v20. When cross-
version pg_upgrade tests upgrade from a version that still shipped
refint, the upgraded contrib_regression_spi (and regression_spi)
databases still have the refint extension and its tables (pkeys,
fkeys, fkeys2) in their catalogs, but a fresh v20 install does not.
Without adjustment the dump comparison fails.
Teach AdjustUpgrade.pm to drop the refint extension and its leftover
tables in those databases when the source version is < 20, matching
the existing pattern for prior cross-version test fixtures.
Discussion: https://postgr.es/m/CAJTYsWUHq8Ohc6-N-xamOPYz-q3qUYMtwQX-1%3DZi%3D5N1Q_GSEQ%40mail.gmail.com
---
.../perl/PostgreSQL/Test/AdjustUpgrade.pm | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
index 8a0c6c98a39..18b52d26f3e 100644
--- a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
+++ b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
@@ -141,6 +141,27 @@ sub adjust_database_contents
}
}
+ # refint was removed in v20
+ if ($old_version < 20)
+ {
+ if ($dbnames{"contrib_regression_spi"})
+ {
+ _add_st(
+ $result,
+ 'contrib_regression_spi',
+ 'drop extension if exists refint cascade',
+ 'drop table if exists pkeys, fkeys, fkeys2');
+ }
+ if ($dbnames{"regression_spi"})
+ {
+ _add_st(
+ $result,
+ 'regression_spi',
+ 'drop extension if exists refint cascade',
+ 'drop table if exists pkeys, fkeys, fkeys2');
+ }
+ }
+
# we removed these test-support functions in v18
if ($old_version < 18)
{
--
2.43.0
[application/octet-stream] v3-0001-Remove-the-refint-contrib-module.patch (39.9K, ../CAJTYsWWm+67r=yf2bCY9bbriYKaK3q79sWJpgqbHQ3endHe9gA@mail.gmail.com/4-v3-0001-Remove-the-refint-contrib-module.patch)
download | inline diff:
From ef500c6878cd5e3cf97c82711ad97010751a3249 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <[email protected]>
Date: Mon, 8 Jun 2026 19:06:31 +0000
Subject: [PATCH v3 1/2] Remove the refint contrib module.
refint was sample code from the pre-built-in-FK era and has long been
documented as superseded by the built-in foreign key mechanism. Recent
fixes (cascade-UPDATE crash with NULL keys, removal of the broken private
SPI plan cache) made it clear that the code has more issues than its
sample-code value justifies, and there was consensus on the prior thread
to drop it once v20 opens for development.
Remove the C source, control file, SQL script, example file, and
regression test from contrib/spi/. Adjust the contrib/spi Makefile and
meson.build, drop the refint section from contrib-spi.sgml, and drop the
now-unused EPlan typedef from typedefs.list. Add an appendix-obsolete
entry under the same contrib-spi-refint id so existing /current/
documentation links keep resolving.
The other contrib/spi extensions (autoinc, insert_username, moddatetime)
are unaffected.
Discussion: https://postgr.es/m/CAJTYsWUHq8Ohc6-N-xamOPYz-q3qUYMtwQX-1%3DZi%3D5N1Q_GSEQ%40mail.gmail.com
---
contrib/spi/Makefile | 13 +-
contrib/spi/expected/refint.out | 113 -----
contrib/spi/meson.build | 30 --
contrib/spi/refint--1.0.sql | 14 -
contrib/spi/refint.c | 538 ---------------------
contrib/spi/refint.control | 5 -
contrib/spi/refint.example | 82 ----
contrib/spi/sql/refint.sql | 97 ----
doc/src/sgml/appendix-obsolete-refint.sgml | 24 +
doc/src/sgml/appendix-obsolete.sgml | 1 +
doc/src/sgml/contrib-spi.sgml | 107 ----
doc/src/sgml/filelist.sgml | 1 +
src/tools/pgindent/typedefs.list | 1 -
13 files changed, 30 insertions(+), 996 deletions(-)
delete mode 100644 contrib/spi/expected/refint.out
delete mode 100644 contrib/spi/refint--1.0.sql
delete mode 100644 contrib/spi/refint.c
delete mode 100644 contrib/spi/refint.control
delete mode 100644 contrib/spi/refint.example
delete mode 100644 contrib/spi/sql/refint.sql
create mode 100644 doc/src/sgml/appendix-obsolete-refint.sgml
diff --git a/contrib/spi/Makefile b/contrib/spi/Makefile
index 7ccbef8c926..805f7653e58 100644
--- a/contrib/spi/Makefile
+++ b/contrib/spi/Makefile
@@ -1,23 +1,18 @@
# contrib/spi/Makefile
-MODULES = autoinc insert_username moddatetime refint
+MODULES = autoinc insert_username moddatetime
-EXTENSION = autoinc insert_username moddatetime refint
+EXTENSION = autoinc insert_username moddatetime
DATA = autoinc--1.0.sql \
insert_username--1.0.sql \
- moddatetime--1.0.sql \
- refint--1.0.sql
+ moddatetime--1.0.sql
PGFILEDESC = "spi - examples of using SPI and triggers"
-REGRESS = autoinc refint
+REGRESS = autoinc
DOCS = $(addsuffix .example, $(MODULES))
-# this is needed for the regression tests;
-# comment out if you want a quieter refint package for other uses
-PG_CPPFLAGS = -DREFINT_VERBOSE
-
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/contrib/spi/expected/refint.out b/contrib/spi/expected/refint.out
deleted file mode 100644
index 79633603217..00000000000
--- a/contrib/spi/expected/refint.out
+++ /dev/null
@@ -1,113 +0,0 @@
-CREATE EXTENSION refint;
-create table pkeys (pkey1 int4 not null, pkey2 text not null);
-create table fkeys (fkey1 int4, fkey2 text, fkey3 int);
-create table fkeys2 (fkey21 int4, fkey22 text, pkey23 int not null);
-create index fkeys_i on fkeys (fkey1, fkey2);
-create index fkeys2_i on fkeys2 (fkey21, fkey22);
-create index fkeys2p_i on fkeys2 (pkey23);
-insert into pkeys values (10, '1');
-insert into pkeys values (20, '2');
-insert into pkeys values (30, '3');
-insert into pkeys values (40, '4');
-insert into pkeys values (50, '5');
-insert into pkeys values (60, '6');
-create unique index pkeys_i on pkeys (pkey1, pkey2);
---
--- For fkeys:
--- (fkey1, fkey2) --> pkeys (pkey1, pkey2)
--- (fkey3) --> fkeys2 (pkey23)
---
-create trigger check_fkeys_pkey_exist
- after insert or update on fkeys
- for each row
- execute function
- check_primary_key ('fkey1', 'fkey2', 'pkeys', 'pkey1', 'pkey2');
-create trigger check_fkeys_pkey2_exist
- after insert or update on fkeys
- for each row
- execute function check_primary_key ('fkey3', 'fkeys2', 'pkey23');
---
--- For fkeys2:
--- (fkey21, fkey22) --> pkeys (pkey1, pkey2)
---
-create trigger check_fkeys2_pkey_exist
- after insert or update on fkeys2
- for each row
- execute procedure
- check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2');
---
--- For pkeys:
--- ON DELETE/UPDATE (pkey1, pkey2) CASCADE:
--- fkeys (fkey1, fkey2) and fkeys2 (fkey21, fkey22)
---
-create trigger check_pkeys_fkey_cascade
- after delete or update on pkeys
- for each row
- execute procedure
- check_foreign_key (2, 'cascade', 'pkey1', 'pkey2',
- 'fkeys', 'fkey1', 'fkey2', 'fkeys2', 'fkey21', 'fkey22');
---
--- For fkeys2:
--- ON DELETE/UPDATE (pkey23) RESTRICT:
--- fkeys (fkey3)
---
-create trigger check_fkeys2_fkey_restrict
- after delete or update on fkeys2
- for each row
- execute procedure check_foreign_key (1, 'restrict', 'pkey23', 'fkeys', 'fkey3');
-insert into fkeys2 values (10, '1', 1);
-insert into fkeys2 values (30, '3', 2);
-insert into fkeys2 values (40, '4', 5);
-insert into fkeys2 values (50, '5', 3);
--- no key in pkeys
-insert into fkeys2 values (70, '5', 3);
-ERROR: tuple references non-existent key
-DETAIL: Trigger "check_fkeys2_pkey_exist" found tuple referencing non-existent key in "pkeys".
-insert into fkeys values (10, '1', 2);
-insert into fkeys values (30, '3', 3);
-insert into fkeys values (40, '4', 2);
-insert into fkeys values (50, '5', 2);
--- no key in pkeys
-insert into fkeys values (70, '5', 1);
-ERROR: tuple references non-existent key
-DETAIL: Trigger "check_fkeys_pkey_exist" found tuple referencing non-existent key in "pkeys".
--- no key in fkeys2
-insert into fkeys values (60, '6', 4);
-ERROR: tuple references non-existent key
-DETAIL: Trigger "check_fkeys_pkey2_exist" found tuple referencing non-existent key in "fkeys2".
-delete from pkeys where pkey1 = 30 and pkey2 = '3';
-NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
-ERROR: "check_fkeys2_fkey_restrict": tuple is referenced in "fkeys"
-CONTEXT: SQL statement "delete from fkeys2 where fkey21 = $1 and fkey22 = $2 "
-delete from pkeys where pkey1 = 40 and pkey2 = '4';
-NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
-NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys2 are deleted
-update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 50 and pkey2 = '5';
-NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are updated
-NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys2 are updated
-update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 10 and pkey2 = '1';
-ERROR: duplicate key value violates unique constraint "pkeys_i"
-DETAIL: Key (pkey1, pkey2)=(7, 70) already exists.
-SELECT trigger_name, event_manipulation, event_object_schema, event_object_table,
- action_order, action_condition, action_orientation, action_timing,
- action_reference_old_table, action_reference_new_table
- FROM information_schema.triggers
- WHERE event_object_table in ('pkeys', 'fkeys', 'fkeys2')
- ORDER BY trigger_name COLLATE "C", 2;
- trigger_name | event_manipulation | event_object_schema | event_object_table | action_order | action_condition | action_orientation | action_timing | action_reference_old_table | action_reference_new_table
-----------------------------+--------------------+---------------------+--------------------+--------------+------------------+--------------------+---------------+----------------------------+----------------------------
- check_fkeys2_fkey_restrict | DELETE | public | fkeys2 | 1 | | ROW | AFTER | |
- check_fkeys2_fkey_restrict | UPDATE | public | fkeys2 | 1 | | ROW | AFTER | |
- check_fkeys2_pkey_exist | INSERT | public | fkeys2 | 1 | | ROW | AFTER | |
- check_fkeys2_pkey_exist | UPDATE | public | fkeys2 | 2 | | ROW | AFTER | |
- check_fkeys_pkey2_exist | INSERT | public | fkeys | 1 | | ROW | AFTER | |
- check_fkeys_pkey2_exist | UPDATE | public | fkeys | 1 | | ROW | AFTER | |
- check_fkeys_pkey_exist | INSERT | public | fkeys | 2 | | ROW | AFTER | |
- check_fkeys_pkey_exist | UPDATE | public | fkeys | 2 | | ROW | AFTER | |
- check_pkeys_fkey_cascade | DELETE | public | pkeys | 1 | | ROW | AFTER | |
- check_pkeys_fkey_cascade | UPDATE | public | pkeys | 1 | | ROW | AFTER | |
-(10 rows)
-
-DROP TABLE pkeys;
-DROP TABLE fkeys;
-DROP TABLE fkeys2;
diff --git a/contrib/spi/meson.build b/contrib/spi/meson.build
index 4a9a2bef0a5..85f0b2276f7 100644
--- a/contrib/spi/meson.build
+++ b/contrib/spi/meson.build
@@ -79,35 +79,6 @@ install_data('moddatetime.example',
)
-# this is needed for the regression tests;
-# comment out if you want a quieter refint package for other uses
-refint_cflags = ['-DREFINT_VERBOSE']
-
-refint_sources = files(
- 'refint.c',
-)
-
-if host_system == 'windows'
- refint_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
- '--NAME', 'refint',
- '--FILEDESC', 'spi - examples of using SPI and triggers',])
-endif
-
-refint = shared_module('refint',
- refint_sources,
- c_args: refint_cflags,
- kwargs: contrib_mod_args,
-)
-contrib_targets += refint
-
-install_data('refint.control', 'refint--1.0.sql',
- kwargs: contrib_data_args,
-)
-
-install_data('refint.example',
- kwargs: contrib_doc_args,
-)
-
tests += {
'name': 'spi',
'sd': meson.current_source_dir(),
@@ -115,7 +86,6 @@ tests += {
'regress': {
'sql': [
'autoinc',
- 'refint',
],
},
}
diff --git a/contrib/spi/refint--1.0.sql b/contrib/spi/refint--1.0.sql
deleted file mode 100644
index faf797c2157..00000000000
--- a/contrib/spi/refint--1.0.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-/* contrib/spi/refint--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION refint" to load this file. \quit
-
-CREATE FUNCTION check_primary_key()
-RETURNS trigger
-AS 'MODULE_PATHNAME'
-LANGUAGE C;
-
-CREATE FUNCTION check_foreign_key()
-RETURNS trigger
-AS 'MODULE_PATHNAME'
-LANGUAGE C;
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
deleted file mode 100644
index 0bfd963fa83..00000000000
--- a/contrib/spi/refint.c
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * contrib/spi/refint.c
- *
- *
- * refint.c -- set of functions to define referential integrity
- * constraints using general triggers.
- */
-#include "postgres.h"
-
-#include <ctype.h>
-
-#include "commands/trigger.h"
-#include "executor/spi.h"
-#include "utils/builtins.h"
-#include "utils/rel.h"
-
-PG_MODULE_MAGIC_EXT(
- .name = "refint",
- .version = PG_VERSION
-);
-
-/*
- * check_primary_key () -- check that key in tuple being inserted/updated
- * references existing tuple in "primary" table.
- * Though it's called without args You have to specify referenced
- * table/keys while creating trigger: key field names in triggered table,
- * referenced table name, referenced key field names:
- * EXECUTE PROCEDURE
- * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2').
- */
-
-PG_FUNCTION_INFO_V1(check_primary_key);
-
-Datum
-check_primary_key(PG_FUNCTION_ARGS)
-{
- TriggerData *trigdata = (TriggerData *) fcinfo->context;
- Trigger *trigger; /* to get trigger name */
- int nargs; /* # of args specified in CREATE TRIGGER */
- char **args; /* arguments: column names and table name */
- int nkeys; /* # of key columns (= nargs / 2) */
- Datum *kvals; /* key values */
- char *relname; /* referenced relation name */
- Relation rel; /* triggered relation */
- HeapTuple tuple = NULL; /* tuple to return */
- TupleDesc tupdesc; /* tuple description */
- SPIPlanPtr pplan; /* prepared plan */
- Oid *argtypes = NULL; /* key types to prepare execution plan */
- bool isnull; /* to know is some column NULL or not */
- int ret;
- int i;
- StringInfoData sql;
-
-#ifdef DEBUG_QUERY
- elog(DEBUG4, "check_primary_key: Enter Function");
-#endif
-
- /*
- * Some checks first...
- */
-
- /* Called by trigger manager ? */
- if (!CALLED_AS_TRIGGER(fcinfo))
- /* internal error */
- elog(ERROR, "check_primary_key: not fired by trigger manager");
-
- /* Should be called for ROW trigger */
- if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_primary_key: must be fired for row");
-
- if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_primary_key: must be fired by AFTER trigger");
-
- /* If INSERTion then must check Tuple to being inserted */
- if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- tuple = trigdata->tg_trigtuple;
-
- /* Not should be called for DELETE */
- else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_primary_key: cannot process DELETE events");
-
- /* If UPDATE, then must check new Tuple, not old one */
- else
- tuple = trigdata->tg_newtuple;
-
- trigger = trigdata->tg_trigger;
- nargs = trigger->tgnargs;
- args = trigger->tgargs;
-
- if (nargs % 2 != 1) /* odd number of arguments! */
- /* internal error */
- elog(ERROR, "check_primary_key: odd number of arguments should be specified");
-
- nkeys = nargs / 2;
- relname = args[nkeys];
- rel = trigdata->tg_relation;
- tupdesc = rel->rd_att;
-
- /* Connect to SPI manager */
- SPI_connect();
-
- /*
- * We use SPI plan preparation feature, so allocate space to place key
- * values.
- */
- kvals = (Datum *) palloc(nkeys * sizeof(Datum));
-
- /* allocate argtypes for preparation */
- argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
-
- /* For each column in key ... */
- for (i = 0; i < nkeys; i++)
- {
- /* get index of column in tuple */
- int fnumber = SPI_fnumber(tupdesc, args[i]);
-
- /* Bad guys may give us un-existing column in CREATE TRIGGER */
- if (fnumber <= 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("there is no attribute \"%s\" in relation \"%s\"",
- args[i], SPI_getrelname(rel))));
-
- /* Well, get binary (in internal format) value of column */
- kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
-
- /*
- * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
- * DON'T FORGET return tuple! Executor inserts tuple you're returning!
- * If you return NULL then nothing will be inserted!
- */
- if (isnull)
- {
- SPI_finish();
- return PointerGetDatum(tuple);
- }
-
- /* Get typeId of column */
- argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
- }
-
- initStringInfo(&sql);
-
- /*
- * Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 = $1
- * [AND Pkey2 = $2 [...]]
- */
- appendStringInfo(&sql, "select 1 from %s where ", relname);
- for (i = 1; i <= nkeys; i++)
- {
- appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
- if (i < nkeys)
- appendStringInfoString(&sql, "and ");
- }
-
- /* Prepare plan for query */
- pplan = SPI_prepare(sql.data, nkeys, argtypes);
- if (pplan == NULL)
- /* internal error */
- elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
-
- pfree(sql.data);
-
- /*
- * Ok, execute prepared plan.
- */
- ret = SPI_execp(pplan, kvals, NULL, 1);
- /* we have no NULLs - so we pass ^^^^ here */
-
- if (ret < 0)
- /* internal error */
- elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
-
- /*
- * If there are no tuples returned by SELECT then ...
- */
- if (SPI_processed == 0)
- ereport(ERROR,
- (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
- errmsg("tuple references non-existent key"),
- errdetail("Trigger \"%s\" found tuple referencing non-existent key in \"%s\".", trigger->tgname, relname)));
-
- SPI_finish();
-
- return PointerGetDatum(tuple);
-}
-
-/*
- * check_foreign_key () -- check that key in tuple being deleted/updated
- * is not referenced by tuples in "foreign" table(s).
- * Though it's called without args You have to specify (while creating trigger):
- * number of references, action to do if key referenced
- * ('restrict' | 'setnull' | 'cascade'), key field names in triggered
- * ("primary") table and referencing table(s)/keys:
- * EXECUTE PROCEDURE
- * check_foreign_key (2, 'restrict', 'Pkey1', 'Pkey2',
- * 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
- */
-
-PG_FUNCTION_INFO_V1(check_foreign_key);
-
-Datum
-check_foreign_key(PG_FUNCTION_ARGS)
-{
- TriggerData *trigdata = (TriggerData *) fcinfo->context;
- Trigger *trigger; /* to get trigger name */
- int nargs; /* # of args specified in CREATE TRIGGER */
- char **args; /* arguments: as described above */
- char **args_temp;
- int nrefs; /* number of references (== # of plans) */
- char action; /* 'R'estrict | 'S'etnull | 'C'ascade */
- int nkeys; /* # of key columns */
- Datum *kvals; /* key values */
- char *relname; /* referencing relation name */
- Relation rel; /* triggered relation */
- HeapTuple trigtuple = NULL; /* tuple to being changed */
- HeapTuple newtuple = NULL; /* tuple to return */
- TupleDesc tupdesc; /* tuple description */
- SPIPlanPtr *splan; /* prepared plan(s) */
- Oid *argtypes = NULL; /* key types to prepare execution plan */
- bool isnull; /* to know is some column NULL or not */
- bool isequal = true; /* are keys in both tuples equal (in UPDATE) */
- int is_update = 0;
- int ret;
- int i,
- r;
- char **args2;
-
-#ifdef DEBUG_QUERY
- elog(DEBUG4, "check_foreign_key: Enter Function");
-#endif
-
- /*
- * Some checks first...
- */
-
- /* Called by trigger manager ? */
- if (!CALLED_AS_TRIGGER(fcinfo))
- /* internal error */
- elog(ERROR, "check_foreign_key: not fired by trigger manager");
-
- /* Should be called for ROW trigger */
- if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_foreign_key: must be fired for row");
-
- /* Not should be called for INSERT */
- if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_foreign_key: cannot process INSERT events");
-
- if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
- /* internal error */
- elog(ERROR, "check_foreign_key: must be fired by AFTER trigger");
-
- /* Have to check tg_trigtuple - tuple being deleted */
- trigtuple = trigdata->tg_trigtuple;
-
- /*
- * But if this is UPDATE then we have to return tg_newtuple. Also, if key
- * in tg_newtuple is the same as in tg_trigtuple then nothing to do.
- */
- is_update = 0;
- if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- {
- newtuple = trigdata->tg_newtuple;
- is_update = 1;
- }
- trigger = trigdata->tg_trigger;
- nargs = trigger->tgnargs;
- args = trigger->tgargs;
-
- if (nargs < 5) /* nrefs, action, key, Relation, key - at
- * least */
- /* internal error */
- elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
-
- nrefs = pg_strtoint32(args[0]);
- if (nrefs < 1)
- /* internal error */
- elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
- action = pg_ascii_tolower((unsigned char) *(args[1]));
- if (action != 'r' && action != 'c' && action != 's')
- /* internal error */
- elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
- nargs -= 2;
- args += 2;
- nkeys = (nargs - nrefs) / (nrefs + 1);
- if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
- /* internal error */
- elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
- nargs + 2, nrefs);
-
- rel = trigdata->tg_relation;
- tupdesc = rel->rd_att;
-
- /* Connect to SPI manager */
- SPI_connect();
-
- /*
- * We use SPI plan preparation feature, so allocate space to place key
- * values.
- */
- kvals = (Datum *) palloc(nkeys * sizeof(Datum));
-
- /* allocate argtypes for preparation */
- argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
-
- /* For each column in key ... */
- for (i = 0; i < nkeys; i++)
- {
- /* get index of column in tuple */
- int fnumber = SPI_fnumber(tupdesc, args[i]);
-
- /* Bad guys may give us un-existing column in CREATE TRIGGER */
- if (fnumber <= 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("there is no attribute \"%s\" in relation \"%s\"",
- args[i], SPI_getrelname(rel))));
-
- /* Well, get binary (in internal format) value of column */
- kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
-
- /*
- * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
- * DON'T FORGET return tuple! Executor inserts tuple you're returning!
- * If you return NULL then nothing will be inserted!
- */
- if (isnull)
- {
- SPI_finish();
- return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
- }
-
- /*
- * If UPDATE then get column value from new tuple being inserted and
- * compare is this the same as old one. For the moment we use string
- * presentation of values...
- */
- if (newtuple != NULL)
- {
- char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
- char *newval;
-
- /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
- if (oldval == NULL)
- /* internal error */
- elog(ERROR, "check_foreign_key: SPI_getvalue returned %s", SPI_result_code_string(SPI_result));
- newval = SPI_getvalue(newtuple, tupdesc, fnumber);
- if (newval == NULL || strcmp(oldval, newval) != 0)
- isequal = false;
- }
-
- /* Get typeId of column */
- argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
- }
- args_temp = args;
- nargs -= nkeys;
- args += nkeys;
- args2 = args;
-
- splan = (SPIPlanPtr *) palloc(nrefs * sizeof(SPIPlanPtr));
-
- for (r = 0; r < nrefs; r++)
- {
- StringInfoData sql;
- SPIPlanPtr pplan;
-
- initStringInfo(&sql);
-
- relname = args2[0];
-
- /*---------
- * For 'R'estrict action we construct SELECT query:
- *
- * SELECT 1
- * FROM _referencing_relation_
- * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
- *
- * to check is tuple referenced or not.
- *---------
- */
- if (action == 'r')
- appendStringInfo(&sql, "select 1 from %s where ", relname);
-
- /*---------
- * For 'C'ascade action we construct DELETE query
- *
- * DELETE
- * FROM _referencing_relation_
- * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]]
- *
- * to delete all referencing tuples.
- *---------
- */
-
- /*
- * Max : Cascade with UPDATE query i create update query that updates
- * new key values in referenced tables
- */
-
-
- else if (action == 'c')
- {
- if (is_update == 1)
- {
- int fn;
- char *nv;
- int k;
-
- appendStringInfo(&sql, "update %s set ", relname);
- for (k = 1; k <= nkeys; k++)
- {
- fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
- Assert(fn > 0); /* already checked above */
- nv = SPI_getvalue(newtuple, tupdesc, fn);
-
- appendStringInfo(&sql, " %s = %s ",
- args2[k],
- nv ? quote_literal_cstr(nv) : "NULL");
- if (k < nkeys)
- appendStringInfoString(&sql, ", ");
- }
- appendStringInfoString(&sql, " where ");
- }
- else
- /* DELETE */
- appendStringInfo(&sql, "delete from %s where ", relname);
- }
-
- /*
- * For 'S'etnull action we construct UPDATE query - UPDATE
- * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]] WHERE
- * Fkey1 = $1 [AND Fkey2 = $2 [...]] - to set key columns in all
- * referencing tuples to NULL.
- */
- else if (action == 's')
- {
- appendStringInfo(&sql, "update %s set ", relname);
- for (i = 1; i <= nkeys; i++)
- {
- appendStringInfo(&sql, "%s = null", args2[i]);
- if (i < nkeys)
- appendStringInfoString(&sql, ", ");
- }
- appendStringInfoString(&sql, " where ");
- }
-
- /* Construct WHERE qual */
- for (i = 1; i <= nkeys; i++)
- {
- appendStringInfo(&sql, "%s = $%d ", args2[i], i);
- if (i < nkeys)
- appendStringInfoString(&sql, "and ");
- }
-
- /* Prepare plan for query */
- pplan = SPI_prepare(sql.data, nkeys, argtypes);
- if (pplan == NULL)
- /* internal error */
- elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
-
- splan[r] = pplan;
-
- args2 += nkeys + 1; /* to the next relation */
-
-#ifdef DEBUG_QUERY
- elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
-#endif
-
- pfree(sql.data);
- }
-
- /*
- * If UPDATE and key is not changed ...
- */
- if (newtuple != NULL && isequal)
- {
- SPI_finish();
- return PointerGetDatum(newtuple);
- }
-
- /*
- * Ok, execute prepared plan(s).
- */
- for (r = 0; r < nrefs; r++)
- {
- /*
- * For 'R'estrict we may to execute plan for one tuple only, for other
- * actions - for all tuples.
- */
- int tcount = (action == 'r') ? 1 : 0;
-
- relname = args[0];
-
- ret = SPI_execp(splan[r], kvals, NULL, tcount);
- /* we have no NULLs - so we pass ^^^^ here */
-
- if (ret < 0)
- ereport(ERROR,
- (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
- errmsg("SPI_execp returned %d", ret)));
-
- /* If action is 'R'estrict ... */
- if (action == 'r')
- {
- /* If there is tuple returned by SELECT then ... */
- if (SPI_processed > 0)
- ereport(ERROR,
- (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
- errmsg("\"%s\": tuple is referenced in \"%s\"",
- trigger->tgname, relname)));
- }
- else
- {
-#ifdef REFINT_VERBOSE
- const char *operation;
-
- if (action == 'c')
- operation = is_update ? "updated" : "deleted";
- else
- operation = "set to null";
-
- elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s",
- trigger->tgname, SPI_processed, relname, operation);
-#endif
- }
- args += nkeys + 1; /* to the next relation */
- }
-
- SPI_finish();
-
- return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
-}
diff --git a/contrib/spi/refint.control b/contrib/spi/refint.control
deleted file mode 100644
index cbede45784c..00000000000
--- a/contrib/spi/refint.control
+++ /dev/null
@@ -1,5 +0,0 @@
-# refint extension
-comment = 'functions for implementing referential integrity (obsolete)'
-default_version = '1.0'
-module_pathname = '$libdir/refint'
-relocatable = true
diff --git a/contrib/spi/refint.example b/contrib/spi/refint.example
deleted file mode 100644
index 299166d5041..00000000000
--- a/contrib/spi/refint.example
+++ /dev/null
@@ -1,82 +0,0 @@
---Column ID of table A is primary key:
-
-CREATE TABLE A (
- ID int4 not null
-);
-CREATE UNIQUE INDEX AI ON A (ID);
-
---Columns REFB of table B and REFC of C are foreign keys referencing ID of A:
-
-CREATE TABLE B (
- REFB int4
-);
-CREATE INDEX BI ON B (REFB);
-
-CREATE TABLE C (
- REFC int4
-);
-CREATE INDEX CI ON C (REFC);
-
---Trigger for table A:
-
-CREATE TRIGGER AT BEFORE DELETE OR UPDATE ON A FOR EACH ROW
-EXECUTE PROCEDURE
-check_foreign_key (2, 'cascade', 'ID', 'B', 'REFB', 'C', 'REFC');
-/*
-2 - means that check must be performed for foreign keys of 2 tables.
-cascade - defines that corresponding keys must be deleted.
-ID - name of primary key column in triggered table (A). You may
- use as many columns as you need.
-B - name of (first) table with foreign keys.
-REFB - name of foreign key column in this table. You may use as many
- columns as you need, but number of key columns in referenced
- table (A) must be the same.
-C - name of second table with foreign keys.
-REFC - name of foreign key column in this table.
-*/
-
---Trigger for table B:
-
-CREATE TRIGGER BT BEFORE INSERT OR UPDATE ON B FOR EACH ROW
-EXECUTE PROCEDURE
-check_primary_key ('REFB', 'A', 'ID');
-
-/*
-REFB - name of foreign key column in triggered (B) table. You may use as
- many columns as you need, but number of key columns in referenced
- table must be the same.
-A - referenced table name.
-ID - name of primary key column in referenced table.
-*/
-
---Trigger for table C:
-
-CREATE TRIGGER CT BEFORE INSERT OR UPDATE ON C FOR EACH ROW
-EXECUTE PROCEDURE
-check_primary_key ('REFC', 'A', 'ID');
-
--- Now try
-
-INSERT INTO A VALUES (10);
-INSERT INTO A VALUES (20);
-INSERT INTO A VALUES (30);
-INSERT INTO A VALUES (40);
-INSERT INTO A VALUES (50);
-
-INSERT INTO B VALUES (1); -- invalid reference
-INSERT INTO B VALUES (10);
-INSERT INTO B VALUES (30);
-INSERT INTO B VALUES (30);
-
-INSERT INTO C VALUES (11); -- invalid reference
-INSERT INTO C VALUES (20);
-INSERT INTO C VALUES (20);
-INSERT INTO C VALUES (30);
-
-DELETE FROM A WHERE ID = 10;
-DELETE FROM A WHERE ID = 20;
-DELETE FROM A WHERE ID = 30;
-
-SELECT * FROM A;
-SELECT * FROM B;
-SELECT * FROM C;
diff --git a/contrib/spi/sql/refint.sql b/contrib/spi/sql/refint.sql
deleted file mode 100644
index 63458127917..00000000000
--- a/contrib/spi/sql/refint.sql
+++ /dev/null
@@ -1,97 +0,0 @@
-CREATE EXTENSION refint;
-
-create table pkeys (pkey1 int4 not null, pkey2 text not null);
-create table fkeys (fkey1 int4, fkey2 text, fkey3 int);
-create table fkeys2 (fkey21 int4, fkey22 text, pkey23 int not null);
-
-create index fkeys_i on fkeys (fkey1, fkey2);
-create index fkeys2_i on fkeys2 (fkey21, fkey22);
-create index fkeys2p_i on fkeys2 (pkey23);
-
-insert into pkeys values (10, '1');
-insert into pkeys values (20, '2');
-insert into pkeys values (30, '3');
-insert into pkeys values (40, '4');
-insert into pkeys values (50, '5');
-insert into pkeys values (60, '6');
-create unique index pkeys_i on pkeys (pkey1, pkey2);
-
---
--- For fkeys:
--- (fkey1, fkey2) --> pkeys (pkey1, pkey2)
--- (fkey3) --> fkeys2 (pkey23)
---
-create trigger check_fkeys_pkey_exist
- after insert or update on fkeys
- for each row
- execute function
- check_primary_key ('fkey1', 'fkey2', 'pkeys', 'pkey1', 'pkey2');
-
-create trigger check_fkeys_pkey2_exist
- after insert or update on fkeys
- for each row
- execute function check_primary_key ('fkey3', 'fkeys2', 'pkey23');
-
---
--- For fkeys2:
--- (fkey21, fkey22) --> pkeys (pkey1, pkey2)
---
-create trigger check_fkeys2_pkey_exist
- after insert or update on fkeys2
- for each row
- execute procedure
- check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2');
-
---
--- For pkeys:
--- ON DELETE/UPDATE (pkey1, pkey2) CASCADE:
--- fkeys (fkey1, fkey2) and fkeys2 (fkey21, fkey22)
---
-create trigger check_pkeys_fkey_cascade
- after delete or update on pkeys
- for each row
- execute procedure
- check_foreign_key (2, 'cascade', 'pkey1', 'pkey2',
- 'fkeys', 'fkey1', 'fkey2', 'fkeys2', 'fkey21', 'fkey22');
-
---
--- For fkeys2:
--- ON DELETE/UPDATE (pkey23) RESTRICT:
--- fkeys (fkey3)
---
-create trigger check_fkeys2_fkey_restrict
- after delete or update on fkeys2
- for each row
- execute procedure check_foreign_key (1, 'restrict', 'pkey23', 'fkeys', 'fkey3');
-
-insert into fkeys2 values (10, '1', 1);
-insert into fkeys2 values (30, '3', 2);
-insert into fkeys2 values (40, '4', 5);
-insert into fkeys2 values (50, '5', 3);
--- no key in pkeys
-insert into fkeys2 values (70, '5', 3);
-
-insert into fkeys values (10, '1', 2);
-insert into fkeys values (30, '3', 3);
-insert into fkeys values (40, '4', 2);
-insert into fkeys values (50, '5', 2);
--- no key in pkeys
-insert into fkeys values (70, '5', 1);
--- no key in fkeys2
-insert into fkeys values (60, '6', 4);
-
-delete from pkeys where pkey1 = 30 and pkey2 = '3';
-delete from pkeys where pkey1 = 40 and pkey2 = '4';
-update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 50 and pkey2 = '5';
-update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 10 and pkey2 = '1';
-
-SELECT trigger_name, event_manipulation, event_object_schema, event_object_table,
- action_order, action_condition, action_orientation, action_timing,
- action_reference_old_table, action_reference_new_table
- FROM information_schema.triggers
- WHERE event_object_table in ('pkeys', 'fkeys', 'fkeys2')
- ORDER BY trigger_name COLLATE "C", 2;
-
-DROP TABLE pkeys;
-DROP TABLE fkeys;
-DROP TABLE fkeys2;
diff --git a/doc/src/sgml/appendix-obsolete-refint.sgml b/doc/src/sgml/appendix-obsolete-refint.sgml
new file mode 100644
index 00000000000..2ec2029153f
--- /dev/null
+++ b/doc/src/sgml/appendix-obsolete-refint.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/appendix-obsolete-refint.sgml -->
+<!--
+ See doc/src/sgml/appendix-obsolete.sgml for why this file exists. Do not change the id attribute.
+-->
+
+<sect1 id="contrib-spi-refint">
+ <title>refint Module Removed</title>
+
+ <indexterm zone="contrib-spi-refint">
+ <primary>refint</primary>
+ </indexterm>
+
+ <para>
+ <productname>PostgreSQL</productname> 19 and below shipped a
+ contrib module named <literal>refint</literal> that provided the
+ trigger functions <function>check_primary_key()</function> and
+ <function>check_foreign_key()</function> as an early way to enforce
+ referential integrity. This functionality was long superseded by
+ the built-in foreign key mechanism (see
+ <xref linkend="ddl-constraints-fk"/>), and the module was removed
+ in <productname>PostgreSQL</productname> 20.
+ </para>
+
+</sect1>
diff --git a/doc/src/sgml/appendix-obsolete.sgml b/doc/src/sgml/appendix-obsolete.sgml
index cc002653052..759a65cb5e0 100644
--- a/doc/src/sgml/appendix-obsolete.sgml
+++ b/doc/src/sgml/appendix-obsolete.sgml
@@ -39,5 +39,6 @@
&obsolete-pgresetxlog;
&obsolete-pgreceivexlog;
&obsolete-auth-radius;
+ &obsolete-refint;
</appendix>
diff --git a/doc/src/sgml/contrib-spi.sgml b/doc/src/sgml/contrib-spi.sgml
index 7e4e580bc74..b585083c6fb 100644
--- a/doc/src/sgml/contrib-spi.sgml
+++ b/doc/src/sgml/contrib-spi.sgml
@@ -24,113 +24,6 @@
separately-installable extension.
</para>
- <sect2 id="contrib-spi-refint">
- <title>refint — Functions for Implementing Referential Integrity</title>
-
- <para>
- <function>check_primary_key()</function> and
- <function>check_foreign_key()</function> are used to check foreign key constraints.
- (This functionality is long since superseded by the built-in foreign
- key mechanism, of course, but the module is still useful as an example.)
- </para>
-
- <note>
- <para>
- <filename>refint</filename> requires a
- <link linkend="ddl-schemas-patterns">secure schema usage pattern</link> and
- data types where the equality operator is named <literal>=</literal>.
- </para>
- </note>
-
- <para>
- <function>check_primary_key()</function> checks the referencing table.
- To use, create an <literal>AFTER INSERT OR UPDATE</literal> trigger using this
- function on a table referencing another table. Specify as the trigger
- arguments: the referencing table's column name(s) which form the foreign
- key, the referenced table name, and the column names in the referenced table
- which form the primary/unique key. To handle multiple foreign
- keys, create a trigger for each reference.
- </para>
-
- <note>
- <para>
- The <emphasis>referenced</emphasis> table name and column name arguments to
- <function>check_primary_key()</function> are copied as-is into internally
- generated SQL statements and therefore must be double-quoted by the user as
- necessary in the <command>CREATE TRIGGER</command> command. See
- <xref linkend="sql-syntax-identifiers"/> for more information about quoting
- SQL identifiers. Conversely, the <emphasis>referencing</emphasis> table
- column name arguments should not be double quoted. See the following mock
- example of proper use of <function>check_primary_key()</function>:
-<programlisting>
-CREATE TRIGGER mytrigger
-AFTER INSERT OR UPDATE ON referencing_table
-FOR EACH ROW EXECUTE PROCEDURE
-check_primary_key (
- 'column A', 'column B', -- referencing table columns
- 'myschema."referenced table"', -- referenced table
- '"column A"', '"column B"' -- referenced table columns
-);
-</programlisting>
- </para>
- </note>
-
- <para>
- <function>check_foreign_key()</function> checks the referenced table.
- To use, create an <literal>AFTER DELETE OR UPDATE</literal> trigger using this
- function on a table referenced by other table(s). Specify as the trigger
- arguments: the number of referencing tables for which the function has to
- perform checking, the action if a referencing key is found
- (<literal>cascade</literal> — to delete the referencing row,
- <literal>restrict</literal> — to abort transaction if referencing keys
- exist, <literal>setnull</literal> — to set referencing key fields to null),
- the referenced table's column names which form the primary/unique key, then
- the referencing table name and column names (repeated for as many
- referencing tables as were specified by first argument). Note that the
- primary/unique key columns should be marked NOT NULL and should have a
- unique index.
- </para>
-
- <note>
- <para>
- The <emphasis>referencing</emphasis> table name and column name arguments
- to <function>check_foreign_key()</function> are copied as-is into
- internally generated SQL statements and therefore must be double-quoted by
- the user as necessary in the <command>CREATE TRIGGER</command> command.
- See <xref linkend="sql-syntax-identifiers"/> for more information about
- quoting SQL identifiers. Conversely, the <emphasis>referenced</emphasis>
- table column name arguments should not be double quoted. See the following
- mock example of proper use of <function>check_foreign_key()</function>:
-<programlisting>
-CREATE TRIGGER mytrigger
-AFTER DELETE OR UPDATE ON referenced_table
-FOR EACH ROW EXECUTE PROCEDURE
-check_foreign_key (
- 1, -- number of referencing tables
- 'cascade', -- action
- 'column A', 'column B', -- referenced table columns
- 'myschema."referencing table"', -- referencing table
- '"column A"', '"column B"' -- referencing table columns
-);
-</programlisting>
- </para>
- </note>
-
- <para>
- Note that if these triggers are executed from
- another <literal>BEFORE</literal> trigger, they can fail unexpectedly. For
- example, if a user inserts row1 and then the <literal>BEFORE</literal>
- trigger inserts row2 and calls a trigger with the
- <function>check_foreign_key()</function>,
- the <function>check_foreign_key()</function>
- function will not see row1 and will fail.
- </para>
-
- <para>
- There are examples in <filename>refint.example</filename>.
- </para>
- </sect2>
-
<sect2 id="contrib-spi-autoinc">
<title>autoinc — Functions for Autoincrementing Fields</title>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 25a85082759..082d5565215 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -209,3 +209,4 @@
<!ENTITY obsolete-pgresetxlog SYSTEM "appendix-obsolete-pgresetxlog.sgml">
<!ENTITY obsolete-pgreceivexlog SYSTEM "appendix-obsolete-pgreceivexlog.sgml">
<!ENTITY obsolete-auth-radius SYSTEM "appendix-obsolete-auth-radius.sgml">
+<!ENTITY obsolete-refint SYSTEM "appendix-obsolete-refint.sgml">
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8cf40c87043..7903f1640c1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -723,7 +723,6 @@ ENGINE
EOM_flatten_into_method
EOM_get_flat_size_method
EPQState
-EPlan
EState
EStatus
EVP_CIPHER
--
2.43.0
view thread (15+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: Remove the refint contrib module (for v20)
In-Reply-To: <CAJTYsWWm+67r=yf2bCY9bbriYKaK3q79sWJpgqbHQ3endHe9gA@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox