/ Check-in [81be7290]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 81be7290a4db7b74a533aaf95c7389eb4bde6a88
User & Date: shess 2007-04-09 20:45:41
Context
2007-04-10
13:51
Be careful not to use the result of sqlite3_value_blob() after changing the representation of an object. Ticket #2290. (CVS 3834) check-in: e14374e4 user: drh tags: trunk
2007-04-09
20:45
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) check-in: 81be7290 user: shess tags: trunk
20:30
Fix stack buffer overrun problem in the test harness. (CVS 3832) check-in: cad9faf3 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/fts1/fts1.c.

1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282

1283
1284
1285
1286
1287
1288
1289
....
1299
1300
1301
1302
1303
1304
1305



1306

1307
1308
1309
1310
1311
1312
1313
  return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
}

static void freeStringArray(int nString, const char **pString){
  int i;

  for (i=0 ; i < nString ; ++i) {
    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.

 *
 * TODO: Perhaps we should return pointer/length strings here for consistency
 * with other code which uses pointer/length. */
static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
                          const char ***pValues){
  sqlite3_stmt *s;
  const char **values;
................................................................................
  if( rc!=SQLITE_OK ) return rc;

  rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
  if( rc!=SQLITE_ROW ) return rc;

  values = (const char **) malloc(v->nColumn * sizeof(const char *));
  for(i=0; i<v->nColumn; ++i){



    values[i] = string_dup((char*)sqlite3_column_text(s, i));

  }

  /* We expect only one row.  We must execute another sqlite3_step()
   * to complete the iteration; otherwise the table will remain locked. */
  rc = sqlite3_step(s);
  if( rc==SQLITE_DONE ){
    *pValues = values;







|






>







 







>
>
>
|
>







1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
....
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
  return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
}

static void freeStringArray(int nString, const char **pString){
  int i;

  for (i=0 ; i < nString ; ++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. */
static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
                          const char ***pValues){
  sqlite3_stmt *s;
  const char **values;
................................................................................
  if( rc!=SQLITE_OK ) return rc;

  rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
  if( rc!=SQLITE_ROW ) return rc;

  values = (const char **) malloc(v->nColumn * sizeof(const char *));
  for(i=0; i<v->nColumn; ++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()
   * to complete the iteration; otherwise the table will remain locked. */
  rc = sqlite3_step(s);
  if( rc==SQLITE_DONE ){
    *pValues = values;

Changes to ext/fts2/fts2.c.

1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933

1934
1935
1936
1937
1938
1939
1940
....
1950
1951
1952
1953
1954
1955
1956



1957

1958
1959
1960
1961
1962
1963
1964
  return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
}

static void freeStringArray(int nString, const char **pString){
  int i;

  for (i=0 ; i < nString ; ++i) {
    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.

 *
 * TODO: Perhaps we should return pointer/length strings here for consistency
 * with other code which uses pointer/length. */
static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
                          const char ***pValues){
  sqlite3_stmt *s;
  const char **values;
................................................................................
  if( rc!=SQLITE_OK ) return rc;

  rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
  if( rc!=SQLITE_ROW ) return rc;

  values = (const char **) malloc(v->nColumn * sizeof(const char *));
  for(i=0; i<v->nColumn; ++i){



    values[i] = string_dup((char*)sqlite3_column_text(s, i));

  }

  /* We expect only one row.  We must execute another sqlite3_step()
   * to complete the iteration; otherwise the table will remain locked. */
  rc = sqlite3_step(s);
  if( rc==SQLITE_DONE ){
    *pValues = values;







|






>







 







>
>
>
|
>







1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
....
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
  return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
}

static void freeStringArray(int nString, const char **pString){
  int i;

  for (i=0 ; i < nString ; ++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. */
static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
                          const char ***pValues){
  sqlite3_stmt *s;
  const char **values;
................................................................................
  if( rc!=SQLITE_OK ) return rc;

  rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
  if( rc!=SQLITE_ROW ) return rc;

  values = (const char **) malloc(v->nColumn * sizeof(const char *));
  for(i=0; i<v->nColumn; ++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()
   * to complete the iteration; otherwise the table will remain locked. */
  rc = sqlite3_step(s);
  if( rc==SQLITE_DONE ){
    *pValues = values;

Added test/fts1l.test.



































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

Added test/fts2m.test.



































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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