|
7 | 7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | 8 | * |
9 | 9 | * IDENTIFICATION |
10 | | - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.125 2002/08/13 17:22:08 petere Exp $ |
| 10 | + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.126 2002/08/17 12:15:48 momjian Exp $ |
11 | 11 | * |
12 | 12 | *------------------------------------------------------------------------- |
13 | 13 | */ |
@@ -1266,12 +1266,10 @@ static MemoryContext deftrig_cxt = NULL; |
1266 | 1266 | * state IMMEDIATE or DEFERRED. |
1267 | 1267 | * ---------- |
1268 | 1268 | */ |
1269 | | -staticbooldeftrig_dfl_all_isset= false; |
1270 | | -staticbooldeftrig_dfl_all_isdeferred= false; |
1271 | 1269 | staticList*deftrig_dfl_trigstates=NIL; |
1272 | 1270 |
|
1273 | | -staticbooldeftrig_all_isset; |
1274 | | -staticbooldeftrig_all_isdeferred; |
| 1271 | +staticbooldeftrig_all_isset= false; |
| 1272 | +staticbooldeftrig_all_isdeferred= false; |
1275 | 1273 | staticList*deftrig_trigstates; |
1276 | 1274 |
|
1277 | 1275 | /* ---------- |
@@ -1702,8 +1700,11 @@ DeferredTriggerBeginXact(void) |
1702 | 1700 | ALLOCSET_DEFAULT_MAXSIZE); |
1703 | 1701 | oldcxt=MemoryContextSwitchTo(deftrig_cxt); |
1704 | 1702 |
|
1705 | | -deftrig_all_isset=deftrig_dfl_all_isset; |
1706 | | -deftrig_all_isdeferred=deftrig_dfl_all_isdeferred; |
| 1703 | +deftrig_all_isset= false; |
| 1704 | +/* |
| 1705 | + * If unspecified, constraints default to IMMEDIATE, per SQL |
| 1706 | + */ |
| 1707 | +deftrig_all_isdeferred= false; |
1707 | 1708 |
|
1708 | 1709 | deftrig_trigstates=NIL; |
1709 | 1710 | foreach(l,deftrig_dfl_trigstates) |
@@ -1793,189 +1794,125 @@ DeferredTriggerAbortXact(void) |
1793 | 1794 | /* ---------- |
1794 | 1795 | * DeferredTriggerSetState() |
1795 | 1796 | * |
1796 | | - *Called for theusersSET CONSTRAINTS ... utility command. |
| 1797 | + *Called for the SET CONSTRAINTS ... utility command. |
1797 | 1798 | * ---------- |
1798 | 1799 | */ |
1799 | 1800 | void |
1800 | 1801 | DeferredTriggerSetState(ConstraintsSetStmt*stmt) |
1801 | 1802 | { |
1802 | | -Relationtgrel; |
1803 | 1803 | List*l; |
1804 | | -List*ls; |
1805 | | -List*loid=NIL; |
1806 | | -MemoryContextoldcxt; |
1807 | | -boolfound; |
1808 | | -DeferredTriggerStatusstate; |
| 1804 | + |
| 1805 | +/* |
| 1806 | + * If called outside a transaction block, we can safely return: this |
| 1807 | + * command cannot effect any subsequent transactions, and there |
| 1808 | + * are no "session-level" trigger settings. |
| 1809 | + */ |
| 1810 | +if (!IsTransactionBlock()) |
| 1811 | +return; |
1809 | 1812 |
|
1810 | 1813 | /* |
1811 | 1814 | * Handle SET CONSTRAINTS ALL ... |
1812 | 1815 | */ |
1813 | 1816 | if (stmt->constraints==NIL) |
1814 | 1817 | { |
1815 | | -if (!IsTransactionBlock()) |
1816 | | -{ |
1817 | | -/* |
1818 | | - * ... outside of a transaction block |
1819 | | - * |
1820 | | - * Drop all information about individual trigger states per |
1821 | | - * session. |
1822 | | - */ |
1823 | | -l=deftrig_dfl_trigstates; |
1824 | | -while (l!=NIL) |
1825 | | -{ |
1826 | | -List*next=lnext(l); |
1827 | | - |
1828 | | -pfree(lfirst(l)); |
1829 | | -pfree(l); |
1830 | | -l=next; |
1831 | | -} |
1832 | | -deftrig_dfl_trigstates=NIL; |
1833 | | - |
1834 | | -/* |
1835 | | - * Set the session ALL state to known. |
1836 | | - */ |
1837 | | -deftrig_dfl_all_isset= true; |
1838 | | -deftrig_dfl_all_isdeferred=stmt->deferred; |
1839 | | - |
1840 | | -return; |
1841 | | -} |
1842 | | -else |
| 1818 | +/* |
| 1819 | + * Drop all per-transaction information about individual trigger |
| 1820 | + * states. |
| 1821 | + */ |
| 1822 | +l=deftrig_trigstates; |
| 1823 | +while (l!=NIL) |
1843 | 1824 | { |
1844 | | -/* |
1845 | | - * ... inside of a transaction block |
1846 | | - * |
1847 | | - * Drop all information about individual trigger states per |
1848 | | - * transaction. |
1849 | | - */ |
1850 | | -l=deftrig_trigstates; |
1851 | | -while (l!=NIL) |
1852 | | -{ |
1853 | | -List*next=lnext(l); |
1854 | | - |
1855 | | -pfree(lfirst(l)); |
1856 | | -pfree(l); |
1857 | | -l=next; |
1858 | | -} |
1859 | | -deftrig_trigstates=NIL; |
1860 | | - |
1861 | | -/* |
1862 | | - * Set the per transaction ALL state to known. |
1863 | | - */ |
1864 | | -deftrig_all_isset= true; |
1865 | | -deftrig_all_isdeferred=stmt->deferred; |
| 1825 | +List*next=lnext(l); |
1866 | 1826 |
|
1867 | | -return; |
| 1827 | +pfree(lfirst(l)); |
| 1828 | +pfree(l); |
| 1829 | +l=next; |
1868 | 1830 | } |
1869 | | -} |
1870 | | - |
1871 | | -/* ---------- |
1872 | | - * Handle SET CONSTRAINTS constraint-name [, ...] |
1873 | | - * First lookup all trigger Oid's for the constraint names. |
1874 | | - * ---------- |
1875 | | - */ |
1876 | | -tgrel=heap_openr(TriggerRelationName,AccessShareLock); |
1877 | | - |
1878 | | -foreach(l,stmt->constraints) |
1879 | | -{ |
1880 | | -char*cname=strVal(lfirst(l)); |
1881 | | -ScanKeyDataskey; |
1882 | | -SysScanDesctgscan; |
1883 | | -HeapTuplehtup; |
1884 | | - |
1885 | | -/* |
1886 | | - * Check that only named constraints are set explicitly |
1887 | | - */ |
1888 | | -if (strlen(cname)==0) |
1889 | | -elog(ERROR,"unnamed constraints cannot be set explicitly"); |
| 1831 | +deftrig_trigstates=NIL; |
1890 | 1832 |
|
1891 | 1833 | /* |
1892 | | - *Setup to scan pg_trigger by tgconstrname ... |
| 1834 | + *Set the per-transaction ALL state to known. |
1893 | 1835 | */ |
1894 | | -ScanKeyEntryInitialize(&skey, |
1895 | | - (bits16)0x0, |
1896 | | - (AttrNumber)Anum_pg_trigger_tgconstrname, |
1897 | | - (RegProcedure)F_NAMEEQ, |
1898 | | -PointerGetDatum(cname)); |
1899 | | - |
1900 | | -tgscan=systable_beginscan(tgrel,TriggerConstrNameIndex, true, |
1901 | | -SnapshotNow,1,&skey); |
1902 | | - |
1903 | | -/* |
1904 | | - * ... and search for the constraint trigger row |
| 1836 | +deftrig_all_isset= true; |
| 1837 | +deftrig_all_isdeferred=stmt->deferred; |
| 1838 | +} |
| 1839 | +else |
| 1840 | +{ |
| 1841 | +Relationtgrel; |
| 1842 | +MemoryContextoldcxt; |
| 1843 | +boolfound; |
| 1844 | +DeferredTriggerStatusstate; |
| 1845 | +List*ls; |
| 1846 | +List*loid=NIL; |
| 1847 | + |
| 1848 | +/* ---------- |
| 1849 | + * Handle SET CONSTRAINTS constraint-name [, ...] |
| 1850 | + * First lookup all trigger Oid's for the constraint names. |
| 1851 | + * ---------- |
1905 | 1852 | */ |
1906 | | -found=false; |
| 1853 | +tgrel=heap_openr(TriggerRelationName,AccessShareLock); |
1907 | 1854 |
|
1908 | | -while (HeapTupleIsValid(htup=systable_getnext(tgscan))) |
| 1855 | +foreach(l,stmt->constraints) |
1909 | 1856 | { |
1910 | | -Form_pg_triggerpg_trigger= (Form_pg_trigger)GETSTRUCT(htup); |
1911 | | -Oidconstr_oid; |
| 1857 | +char*cname=strVal(lfirst(l)); |
| 1858 | +ScanKeyDataskey; |
| 1859 | +SysScanDesctgscan; |
| 1860 | +HeapTuplehtup; |
1912 | 1861 |
|
1913 | 1862 | /* |
1914 | | - * If we found some, check that they fit the deferrability but |
1915 | | - * skip ON <event> RESTRICT ones, since they are silently |
1916 | | - * never deferrable. |
| 1863 | + * Check that only named constraints are set explicitly |
1917 | 1864 | */ |
1918 | | -if (stmt->deferred&& !pg_trigger->tgdeferrable&& |
1919 | | -pg_trigger->tgfoid!=F_RI_FKEY_RESTRICT_UPD&& |
1920 | | -pg_trigger->tgfoid!=F_RI_FKEY_RESTRICT_DEL) |
1921 | | -elog(ERROR,"Constraint '%s' is not deferrable", |
1922 | | -cname); |
1923 | | - |
1924 | | -AssertTupleDescHasOid(tgrel->rd_att); |
1925 | | -constr_oid=HeapTupleGetOid(htup); |
1926 | | -loid=lappendi(loid,constr_oid); |
1927 | | -found= true; |
1928 | | -} |
1929 | | - |
1930 | | -systable_endscan(tgscan); |
| 1865 | +if (strlen(cname)==0) |
| 1866 | +elog(ERROR,"unnamed constraints cannot be set explicitly"); |
1931 | 1867 |
|
1932 | | -/* |
1933 | | - *Not found ? |
1934 | | - */ |
1935 | | -if (!found) |
1936 | | -elog(ERROR,"Constraint '%s' does not exist",cname); |
1937 | | -} |
1938 | | -heap_close(tgrel,AccessShareLock); |
| 1868 | +/* |
| 1869 | + *Setup to scan pg_trigger by tgconstrname ... |
| 1870 | + */ |
| 1871 | +ScanKeyEntryInitialize(&skey, (bits16)0x0, |
| 1872 | + (AttrNumber)Anum_pg_trigger_tgconstrname, |
| 1873 | + (RegProcedure)F_NAMEEQ, |
| 1874 | +PointerGetDatum(cname)); |
1939 | 1875 |
|
1940 | | -if (!IsTransactionBlock()) |
1941 | | -{ |
1942 | | -/* |
1943 | | - * Outside of a transaction block set the trigger states of |
1944 | | - * individual triggers on session level. |
1945 | | - */ |
1946 | | -oldcxt=MemoryContextSwitchTo(deftrig_gcxt); |
| 1876 | +tgscan=systable_beginscan(tgrel,TriggerConstrNameIndex, true, |
| 1877 | +SnapshotNow,1,&skey); |
1947 | 1878 |
|
1948 | | -foreach(l,loid) |
1949 | | -{ |
| 1879 | +/* |
| 1880 | + * ... and search for the constraint trigger row |
| 1881 | + */ |
1950 | 1882 | found= false; |
1951 | | -foreach(ls,deftrig_dfl_trigstates) |
1952 | | -{ |
1953 | | -state= (DeferredTriggerStatus)lfirst(ls); |
1954 | | -if (state->dts_tgoid== (Oid)lfirsti(l)) |
1955 | | -{ |
1956 | | -state->dts_tgisdeferred=stmt->deferred; |
1957 | | -found= true; |
1958 | | -break; |
1959 | | -} |
1960 | | -} |
1961 | | -if (!found) |
| 1883 | + |
| 1884 | +while (HeapTupleIsValid(htup=systable_getnext(tgscan))) |
1962 | 1885 | { |
1963 | | -state= (DeferredTriggerStatus) |
1964 | | -palloc(sizeof(DeferredTriggerStatusData)); |
1965 | | -state->dts_tgoid= (Oid)lfirsti(l); |
1966 | | -state->dts_tgisdeferred=stmt->deferred; |
| 1886 | +Form_pg_triggerpg_trigger= (Form_pg_trigger)GETSTRUCT(htup); |
| 1887 | +Oidconstr_oid; |
1967 | 1888 |
|
1968 | | -deftrig_dfl_trigstates= |
1969 | | -lappend(deftrig_dfl_trigstates,state); |
| 1889 | +/* |
| 1890 | + * If we found some, check that they fit the deferrability but |
| 1891 | + * skip ON <event> RESTRICT ones, since they are silently |
| 1892 | + * never deferrable. |
| 1893 | + */ |
| 1894 | +if (stmt->deferred&& !pg_trigger->tgdeferrable&& |
| 1895 | +pg_trigger->tgfoid!=F_RI_FKEY_RESTRICT_UPD&& |
| 1896 | +pg_trigger->tgfoid!=F_RI_FKEY_RESTRICT_DEL) |
| 1897 | +elog(ERROR,"Constraint '%s' is not deferrable", |
| 1898 | +cname); |
| 1899 | + |
| 1900 | +AssertTupleDescHasOid(tgrel->rd_att); |
| 1901 | +constr_oid=HeapTupleGetOid(htup); |
| 1902 | +loid=lappendi(loid,constr_oid); |
| 1903 | +found= true; |
1970 | 1904 | } |
1971 | | -} |
1972 | 1905 |
|
1973 | | -MemoryContextSwitchTo(oldcxt); |
| 1906 | +systable_endscan(tgscan); |
| 1907 | + |
| 1908 | +/* |
| 1909 | + * Not found ? |
| 1910 | + */ |
| 1911 | +if (!found) |
| 1912 | +elog(ERROR,"Constraint '%s' does not exist",cname); |
| 1913 | +} |
| 1914 | +heap_close(tgrel,AccessShareLock); |
1974 | 1915 |
|
1975 | | -return; |
1976 | | -} |
1977 | | -else |
1978 | | -{ |
1979 | 1916 | /* |
1980 | 1917 | * Inside of a transaction block set the trigger states of |
1981 | 1918 | * individual triggers on transaction level. |
@@ -2008,9 +1945,17 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) |
2008 | 1945 | } |
2009 | 1946 |
|
2010 | 1947 | MemoryContextSwitchTo(oldcxt); |
2011 | | - |
2012 | | -return; |
2013 | 1948 | } |
| 1949 | + |
| 1950 | +/* |
| 1951 | + * SQL99 requires that when a constraint is set to IMMEDIATE, any |
| 1952 | + * deferred checks against that constraint must be made when the |
| 1953 | + * SET CONSTRAINTS command is executed -- i.e. the effects of the |
| 1954 | + * SET CONSTRAINTS command applies retroactively. This happens "for |
| 1955 | + * free" since we have already made the necessary modifications to |
| 1956 | + * the constraints, and deferredTriggerEndQuery() is called by |
| 1957 | + * finish_xact_command(). |
| 1958 | + */ |
2014 | 1959 | } |
2015 | 1960 |
|
2016 | 1961 |
|
|