From 6b6ab133533de12eca702cbf891ae92a4b8fce1a Mon Sep 17 00:00:00 2001 From: shess Date: Mon, 9 Apr 2007 20:45:40 +0000 Subject: [PATCH] Fix crash in delete when existing row has null fields. Previous code assumed that the row had values in all columns, sigh. Fixes bug http://www.sqlite.org/cvstrac/tktview?tn=2289 . (CVS 3833) FossilOrigin-Name: 81be7290a4db7b74a533aaf95c7389eb4bde6a88 --- ext/fts1/fts1.c | 9 +++++-- ext/fts2/fts2.c | 9 +++++-- manifest | 18 ++++++++------ manifest.uuid | 2 +- test/fts1l.test | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ test/fts2m.test | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 test/fts1l.test create mode 100644 test/fts2m.test diff --git a/ext/fts1/fts1.c b/ext/fts1/fts1.c index ddd4529c7e..0d931eca19 100644 --- a/ext/fts1/fts1.c +++ b/ext/fts1/fts1.c @@ -1273,13 +1273,14 @@ static void freeStringArray(int nString, const char **pString){ int i; for (i=0 ; i < nString ; ++i) { - free((void *) pString[i]); + if( pString[i]!=NULL ) free((void *) pString[i]); } free((void *) pString); } /* select * from %_content where rowid = [iRow] * The caller must delete the returned array and all strings in it. + * null fields will be NULL in the returned array. * * TODO: Perhaps we should return pointer/length strings here for consistency * with other code which uses pointer/length. */ @@ -1303,7 +1304,11 @@ static int content_select(fulltext_vtab *v, sqlite_int64 iRow, values = (const char **) malloc(v->nColumn * sizeof(const char *)); for(i=0; inColumn; ++i){ - values[i] = string_dup((char*)sqlite3_column_text(s, i)); + if( sqlite3_column_type(s, i)==SQLITE_NULL ){ + values[i] = NULL; + }else{ + values[i] = string_dup((char*)sqlite3_column_text(s, i)); + } } /* We expect only one row. We must execute another sqlite3_step() diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c index 2a52737d70..e734d32f81 100644 --- a/ext/fts2/fts2.c +++ b/ext/fts2/fts2.c @@ -1924,13 +1924,14 @@ static void freeStringArray(int nString, const char **pString){ int i; for (i=0 ; i < nString ; ++i) { - free((void *) pString[i]); + if( pString[i]!=NULL ) free((void *) pString[i]); } free((void *) pString); } /* select * from %_content where rowid = [iRow] * The caller must delete the returned array and all strings in it. + * null fields will be NULL in the returned array. * * TODO: Perhaps we should return pointer/length strings here for consistency * with other code which uses pointer/length. */ @@ -1954,7 +1955,11 @@ static int content_select(fulltext_vtab *v, sqlite_int64 iRow, values = (const char **) malloc(v->nColumn * sizeof(const char *)); for(i=0; inColumn; ++i){ - values[i] = string_dup((char*)sqlite3_column_text(s, i)); + if( sqlite3_column_type(s, i)==SQLITE_NULL ){ + values[i] = NULL; + }else{ + values[i] = string_dup((char*)sqlite3_column_text(s, i)); + } } /* We expect only one row. We must execute another sqlite3_step() diff --git a/manifest b/manifest index 0e6d6af4a1..a1bb28e120 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sstack\sbuffer\soverrun\sproblem\sin\sthe\stest\sharness.\s(CVS\s3832) -D 2007-04-09T20:30:11 +C Fix\scrash\sin\sdelete\swhen\sexisting\srow\shas\snull\sfields.\s\sPrevious\scode\nassumed\sthat\sthe\srow\shad\svalues\sin\sall\scolumns,\ssigh.\s\sFixes\sbug\nhttp://www.sqlite.org/cvstrac/tktview?tn=2289\s.\s(CVS\s3833) +D 2007-04-09T20:45:41 F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -22,7 +22,7 @@ F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5 -F ext/fts1/fts1.c 7585d9cb7ad7bcdf162936ab1fd64868f2f55ea5 +F ext/fts1/fts1.c 1ee986c31a7080d509602fa182738c5a8862fbff F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6 F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114 F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089 @@ -34,7 +34,7 @@ F ext/fts1/fulltext.h 08525a47852d1d62a0be81d3fc3fe2d23b094efd F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9 F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts2/fts2.c 8d69d6e4b48b0de4350e10c9c76649c638e35997 +F ext/fts2/fts2.c acfce1c936d50615f21b63b71f6e86730da64a8c F ext/fts2/fts2.h bbdab26d34f91974d5b9ade8b7836c140a7c4ce1 F ext/fts2/fts2_hash.c b3f22116d4ef0bc8f2da6e3fdc435c86d0951a9b F ext/fts2/fts2_hash.h e283308156018329f042816eb09334df714e105e @@ -219,6 +219,7 @@ F test/fts1f.test 2d6cb10d8b7a4e6edc321bbdb3982f1f48774714 F test/fts1i.test 6bfe08cdfdced063a39a50c8601da65e6274d879 F test/fts1j.test e4c0ffcd0ba2adce09c6b7b43ffd0749b5fda5c7 F test/fts1k.test fdf295cb797ba6a2ef81ec41cb98df0ceb2e572c +F test/fts1l.test 15c119ed2362b2b28d5300c0540a6a43eab66c36 F test/fts1porter.test d86e9c3e0c7f8ff95add6582b4b585fb4e02b96d F test/fts2a.test 103fc178d134c54c44c1938a4331e9e2030792d9 F test/fts2b.test 964abc0236c849c07ca1ae496bb25c268ae94816 @@ -232,6 +233,7 @@ F test/fts2i.test 1b22451d1f13f7c509baec620dc3a4a754885dd6 F test/fts2j.test f68d7611f76309bc8b94170f3740d9fbbc061d9b F test/fts2k.test 222d0b3bc8667753f18406aaea9906a6098ea016 F test/fts2l.test 4c53c89ce3919003765ff4fd8d98ecf724d97dd3 +F test/fts2m.test 4b30142ead6f3ed076e880a2a464064c5ad58c51 F test/func.test 019d706b2458dfdf239c74cc31143446de1ee44a F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d @@ -455,7 +457,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P ba5f4a55fad183a1698555b256f43f63451f5fc6 -R 5ef4b02d2117db2ca987c4c827be07e2 -U drh -Z aefa6d34865394c1dd78203eae52ac99 +P cad9faf3ad99b68be4618dff4b3497b15b9e6d9d +R 66cd54d39dd95145a99973d21d5eff33 +U shess +Z 50d829f2eb908722f9e9fda676f48500 diff --git a/manifest.uuid b/manifest.uuid index 70d4a8b850..051d46b047 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cad9faf3ad99b68be4618dff4b3497b15b9e6d9d \ No newline at end of file +81be7290a4db7b74a533aaf95c7389eb4bde6a88 \ No newline at end of file diff --git a/test/fts1l.test b/test/fts1l.test new file mode 100644 index 0000000000..924be33801 --- /dev/null +++ b/test/fts1l.test @@ -0,0 +1,65 @@ +# 2007 April 9 +# +# The author disclaims copyright to this source code. +# +#************************************************************************* +# This file implements regression tests for SQLite library. fts1 +# DELETE handling assumed all fields were non-null. This was not +# the intention at all. +# +# $Id: fts1l.test,v 1.1 2007/04/09 20:45:42 shess Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If SQLITE_ENABLE_FTS1 is defined, omit this file. +ifcapable !fts1 { + finish_test + return +} + +db eval { + CREATE VIRTUAL TABLE t1 USING fts1(col_a, col_b); + + INSERT INTO t1(rowid, col_a, col_b) VALUES(1, 'testing', 'testing'); + INSERT INTO t1(rowid, col_a, col_b) VALUES(2, 'only a', null); + INSERT INTO t1(rowid, col_a, col_b) VALUES(3, null, 'only b'); + INSERT INTO t1(rowid, col_a, col_b) VALUES(4, null, null); +} + +do_test fts1m-1.0 { + execsql { + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {2 2 4} + +do_test fts1m-1.1 { + execsql { + DELETE FROM t1 WHERE rowid = 1; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {1 1 3} + +do_test fts1m-1.2 { + execsql { + DELETE FROM t1 WHERE rowid = 2; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 1 2} + +do_test fts1m-1.3 { + execsql { + DELETE FROM t1 WHERE rowid = 3; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 0 1} + +do_test fts1m-1.4 { + execsql { + DELETE FROM t1 WHERE rowid = 4; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 0 0} + +finish_test diff --git a/test/fts2m.test b/test/fts2m.test new file mode 100644 index 0000000000..6552637a62 --- /dev/null +++ b/test/fts2m.test @@ -0,0 +1,65 @@ +# 2007 April 9 +# +# The author disclaims copyright to this source code. +# +#************************************************************************* +# This file implements regression tests for SQLite library. fts2 +# DELETE handling assumed all fields were non-null. This was not +# the intention at all. +# +# $Id: fts2m.test,v 1.1 2007/04/09 20:45:42 shess Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If SQLITE_ENABLE_FTS2 is defined, omit this file. +ifcapable !fts2 { + finish_test + return +} + +db eval { + CREATE VIRTUAL TABLE t1 USING fts2(col_a, col_b); + + INSERT INTO t1(rowid, col_a, col_b) VALUES(1, 'testing', 'testing'); + INSERT INTO t1(rowid, col_a, col_b) VALUES(2, 'only a', null); + INSERT INTO t1(rowid, col_a, col_b) VALUES(3, null, 'only b'); + INSERT INTO t1(rowid, col_a, col_b) VALUES(4, null, null); +} + +do_test fts2m-1.0 { + execsql { + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {2 2 4} + +do_test fts2m-1.1 { + execsql { + DELETE FROM t1 WHERE rowid = 1; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {1 1 3} + +do_test fts2m-1.2 { + execsql { + DELETE FROM t1 WHERE rowid = 2; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 1 2} + +do_test fts2m-1.3 { + execsql { + DELETE FROM t1 WHERE rowid = 3; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 0 1} + +do_test fts2m-1.4 { + execsql { + DELETE FROM t1 WHERE rowid = 4; + SELECT COUNT(col_a), COUNT(col_b), COUNT(*) FROM t1; + } +} {0 0 0} + +finish_test