From: dan Date: Wed, 8 Jul 2026 17:40:04 +0000 (+0000) Subject: Allow "ALTER TABLE ... DROP CONSTRAINT" to drop foreign key constraints. X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=47a4302042e4bcb8695ecc44f3553822b1c225a2;p=thirdparty%2Fsqlite.git Allow "ALTER TABLE ... DROP CONSTRAINT" to drop foreign key constraints. FossilOrigin-Name: 17c08963812d6458783720c511806727aeae263a243a11d49b463d2c7867cd54 --- diff --git a/manifest b/manifest index 4fa0339a15..3fc0eb9bec 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C For\sdisplay\smodes\sin\sthe\sCLI\sfor\swhich\scolumn\sheaders\sare\senabled,\nmake\sthe\smode\s"always"\sinstead\sof\s"on.\s\sImproved\scomments\sand\stest\ncases. -D 2026-07-08T15:43:42.959 +C Allow\s"ALTER\sTABLE\s...\sDROP\sCONSTRAINT"\sto\sdrop\sforeign\skey\sconstraints. +D 2026-07-08T17:40:04.496 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -675,7 +675,7 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366 F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398 -F src/alter.c da59ac700b52ba5d0e4dd099fb1818975cf8a79a546594da586b4e1eba3ae405 +F src/alter.c 0c6e7f9d2ddd1ee55e5e9dd903ea00a13ba728b7be2fbbaa2e83725dafdadbd9 F src/analyze.c 73162482c656187823217f4c00758c9ee13a420c8745bc542129e0279b792287 F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338 F src/auth.c b5ece4e1edccad082c0332fa0087df225473bae0feea9269f824312201377185 @@ -846,7 +846,8 @@ F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256b F test/alterauth2.test 4b74fa8f184f4736497317feb587b65759eb87d87acfe3a8ef433d4d18bb002b F test/altercol.test 3661c432aacb42bc2198dd4611bbb9c3b09fc73251b59edda046109103b8ac00 F test/altercons.test 9b036a956268eccd64761a6af3ab6f7b313c738392c7460b747d8aacd32589a2 -F test/altercons2.test 4933eadb035fc0ccfc34ed879f69a09051c53907f9dae9c3d36d25f93519ccc7 +F test/altercons2.test 264c1cc31c16b319de423af9565eb7129d7e9f55b21bb4eda6fdae02841265ae +F test/altercons3.test d0bd7dde66a4a4f0b432b3ae3520e989b52b9bdfadddb9bd80be94554fecf803 F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12 F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41 @@ -2213,9 +2214,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 4edba31af4ef18350cb415079fb92cae0c53767fc0e74d63d404296974e75e5c f94259fc07cd7856e9c6f81a46689f4f96bbf464fda7f1d11b51a308c296aca0 -R 688db872c29fb684e76bc6c6acf8cc9b -T +closed f94259fc07cd7856e9c6f81a46689f4f96bbf464fda7f1d11b51a308c296aca0 -U drh -Z aa42c4c981256f3d4f6c42fc99d72364 +P c8aa208bf5b00f3ab93c5edd510dfd0d7edd271c064f159616ee409b308f7eb9 +R 4bd3a46fbadb8b3a12b89cadb35de70a +T *branch * drop-constraint-fk +T *sym-drop-constraint-fk * +T -sym-trunk * +U dan +Z 7b2661190251d9be0a0996978db71a70 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..d37ec80d47 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch drop-constraint-fk +tag drop-constraint-fk diff --git a/manifest.uuid b/manifest.uuid index 9eba18e427..767d1600e9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c8aa208bf5b00f3ab93c5edd510dfd0d7edd271c064f159616ee409b308f7eb9 +17c08963812d6458783720c511806727aeae263a243a11d49b463d2c7867cd54 diff --git a/src/alter.c b/src/alter.c index 7d3c85b5ae..5d39087e6f 100644 --- a/src/alter.c +++ b/src/alter.c @@ -2409,16 +2409,38 @@ static int getWhitespace(const u8 *z){ /* ** Argument z points into the body of a constraint - specifically the ** second token of the constraint definition. For a named constraint, -** z points to the first token past the CONSTRAINT keyword. For an -** unnamed NOT NULL constraint, z points to the first byte past the NOT +** z points to the second token of the constraint definition. For an +** unnamed NOT NULL constraint, z points to the first byte past the NOT ** keyword. ** +** Argument eTok may be the token value of the first token of the constraint +** (e.g. TK_CHECK or TK_REFERENCES) or zero. If it is either TK_REFERENCES +** or TK_FOREIGN, special parsing is enabled to find the end of the foreign-key +** constraint definition. +** ** Return the number of bytes until the end of the constraint. */ -static int getConstraint(const u8 *z){ +static int getConstraint(const u8 *z, int eTok){ int iOff = 0; int t = 0; +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( eTok==TK_FOREIGN ){ + /* For a FOREIGN KEY constraint, use getConstraint() to parse everything + ** up to the REFERENCES keyword. Then getConstraintToken() to consume + ** the TK_REFERENCES token itself. Then fall through to the special + ** handling for TK_REFERENCES below. */ + iOff = getConstraint(z, 0); + iOff += getConstraintToken(&z[iOff], &eTok); + } + + if( eTok==TK_REFERENCES ){ + /* REFERENCES is followed by a table name. Gobble this up here in + ** case the table name is a fallback token like TK_GENERATED. */ + iOff += getConstraintToken(&z[iOff], &t); + } +#endif + /* Now, the current constraint proceeds until the next occurence of one ** of the following tokens: ** @@ -2590,11 +2612,11 @@ static void dropConstraintFunc( t = TK_CHECK; }else{ iOff += nTok; - iOff += getConstraint(&zSql[iOff]); + iOff += getConstraint(&zSql[iOff], t); } if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){ - if( t!=TK_NOT && t!=TK_CHECK ){ + if( t!=TK_NOT && t!=TK_CHECK && t!=TK_REFERENCES && t!=TK_FOREIGN ){ errorMPrintf(ctx, "constraint may not be dropped: %s", zCons); return; } @@ -2603,7 +2625,7 @@ static void dropConstraintFunc( } }else if( t==TK_NOT && iNotNull==ii ){ - iEnd = iOff + getConstraint(&zSql[iOff]); + iEnd = iOff + getConstraint(&zSql[iOff], 0); break; }else if( t==TK_RP || t==TK_ILLEGAL ){ iEnd = -1; diff --git a/test/altercons2.test b/test/altercons2.test index 1e6aa1acc3..916ae27077 100644 --- a/test/altercons2.test +++ b/test/altercons2.test @@ -172,7 +172,10 @@ do_execsql_test 8.0 { } do_catchsql_test 8.1 { ALTER TABLE abc DROP CONSTRAINT one -} {1 {constraint may not be dropped: one}} +} {0 {}} +do_execsql_test 8.2 { + SELECT sql FROM sqlite_schema +} {{CREATE TABLE abc(a, b, c)}} #------------------------------------------------------------------------- reset_db diff --git a/test/altercons3.test b/test/altercons3.test new file mode 100644 index 0000000000..5fe8a868cd --- /dev/null +++ b/test/altercons3.test @@ -0,0 +1,101 @@ +# 2026 July 8 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix altercons3 + +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + +#------------------------------------------------------------------------- +do_execsql_test 1.0 { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b CONSTRAINT fk REFERENCES p1(a)); +} +do_execsql_test 1.1 { + ALTER TABLE c1 DROP CONSTRAINT fk; + SELECT sql FROM sqlite_schema WHERE name='c1'; +} {{CREATE TABLE c1(b)}} + + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.0 { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b, CONSTRAINT fk FOREIGN KEY (b) REFERENCES p1(a)); +} +do_execsql_test 2.1 { + ALTER TABLE c1 DROP CONSTRAINT fk; + SELECT sql FROM sqlite_schema WHERE name='c1'; +} {{CREATE TABLE c1(b)}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 3.0 { + CREATE TABLE generated(a PRIMARY KEY); + CREATE TABLE c1(b CONSTRAINT fk REFERENCES generated(a)); +} +do_execsql_test 3.1 { + ALTER TABLE c1 DROP CONSTRAINT fk; + SELECT sql FROM sqlite_schema WHERE name='c1'; +} {{CREATE TABLE c1(b)}} + +#-------------------------------------------------------------------------- +reset_db + +foreach {tn before after} { + 1 "CREATE TABLE c1(b CONSTRAINT fk REFERENCES p1(a))" + "CREATE TABLE c1(b)" + + 2 "CREATE TABLE c1(b, c, CONSTRAINT fk FOREIGN key (c, b) REFERENCES p1(o,t))" + "CREATE TABLE c1(b, c)" + + 3 "CREATE TABLE c1(b, generated, + CONSTRAINT fk FOREIGN key (generated) REFERENCES p1(o))" + "CREATE TABLE c1(b, generated)" + + 4 "CREATE TABLE c1(b, generated CONSTRAINT fk REFERENCES p1(o) NOT NULL)" + "CREATE TABLE c1(b, generated NOT NULL)" + + 5 "CREATE TABLE c1(b, generated CONSTRAINT fk REFERENCES x1)" + "CREATE TABLE c1(b, generated)" +} { + do_execsql_test 4.$tn.1 { DROP TABLE IF EXISTS c1 } + do_execsql_test 4.$tn.2 $before + do_execsql_test 4.$tn.3 { ALTER TABLE c1 DROP CONSTRAINT fk } + do_execsql_test 4.$tn.4 { + SELECT sql FROM sqlite_schema WHERE name='c1' + } [list $after] +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 5.0 { + CREATE TABLE x1(a, b CONSTRAINT ott REFERENCES x2); + PRAGMA writable_schema = 1; + UPDATE sqlite_schema SET sql='CREATE TABLE x1(a, b CONSTRAINT ott REFERENCES'; +} + +do_execsql_test 5.1 { + ALTER TABLE x1 DROP CONSTRAINT ott; +} + +do_execsql_test 5.2 { + SELECT sql FROM sqlite_schema +} {{CREATE TABLE x1(a, b }} + +finish_test +