SQLite

Check-in [badf3014bd]
Login

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

Overview
Comment:Fix a problem with UPDATEs on fts5 tables that contain blob values.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: badf3014bd1620fd3d4b8013f641fd820b249649fb93cc75b7b8df9dfd6f32a6
User & Date: dan 2025-06-11 15:03:53.627
Context
2025-06-12
07:35
Have sqlite3_setlk_timeout() take the database handle mutex. This fixes an assert() failure that could occur if sqlite3_setlk_timeout() were called on a threadsafe handle. (check-in: a95d126e13 user: dan tags: trunk)
2025-06-11
17:24
Fix a problem with UPDATEs on fts5 tables that contain blob values. (check-in: 2b4577d83c user: drh tags: branch-3.50)
15:03
Fix a problem with UPDATEs on fts5 tables that contain blob values. (check-in: badf3014bd user: dan tags: trunk)
00:01
Fix the concat_ws() SQL function so that it includes empty strings in the concatenation. Forum post 52503ac21d. (check-in: 80a78987da user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/fts5/fts5_storage.c.
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557










558
559
560
561
562

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577

578
579
580
581
582
583
584
  }

  ctx.pStorage = p;
  ctx.iCol = -1;
  for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
    if( pConfig->abUnindexed[iCol-1]==0 ){
      sqlite3_value *pVal = 0;

      const char *pText = 0;
      int nText = 0;
      const char *pLoc = 0;
      int nLoc = 0;

      assert( pSeek==0 || apVal==0 );
      assert( pSeek!=0 || apVal!=0 );
      if( pSeek ){
        pVal = sqlite3_column_value(pSeek, iCol);
      }else{
        pVal = apVal[iCol-1];
      }

      if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
        rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
      }else{










        pText = (const char*)sqlite3_value_text(pVal);
        nText = sqlite3_value_bytes(pVal);
        if( pConfig->bLocale && pSeek ){
          pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol);
          nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);

        }
      }

      if( rc==SQLITE_OK ){
        sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
        ctx.szCol = 0;
        rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, 
            pText, nText, (void*)&ctx, fts5StorageInsertCallback
        );
        p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
        if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){
          rc = FTS5_CORRUPT;
        }
        sqlite3Fts5ClearLocale(pConfig);
      }

    }
  }
  if( rc==SQLITE_OK && p->nTotalRow<1 ){
    rc = FTS5_CORRUPT;
  }else{
    p->nTotalRow--;
  }







>
















>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
>















>







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
  }

  ctx.pStorage = p;
  ctx.iCol = -1;
  for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
    if( pConfig->abUnindexed[iCol-1]==0 ){
      sqlite3_value *pVal = 0;
      sqlite3_value *pFree = 0;
      const char *pText = 0;
      int nText = 0;
      const char *pLoc = 0;
      int nLoc = 0;

      assert( pSeek==0 || apVal==0 );
      assert( pSeek!=0 || apVal!=0 );
      if( pSeek ){
        pVal = sqlite3_column_value(pSeek, iCol);
      }else{
        pVal = apVal[iCol-1];
      }

      if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
        rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
      }else{
        if( sqlite3_value_type(pVal)!=SQLITE_TEXT ){
          /* Make a copy of the value to work with. This is because the call
          ** to sqlite3_value_text() below forces the type of the value to
          ** SQLITE_TEXT, and we may need to use it again later. */
          pFree = pVal = sqlite3_value_dup(pVal);
          if( pVal==0 ){
            rc = SQLITE_NOMEM;
          }
        }
        if( rc==SQLITE_OK ){
          pText = (const char*)sqlite3_value_text(pVal);
          nText = sqlite3_value_bytes(pVal);
          if( pConfig->bLocale && pSeek ){
            pLoc = (const char*)sqlite3_column_text(pSeek, iCol+pConfig->nCol);
            nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);
          }
        }
      }

      if( rc==SQLITE_OK ){
        sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
        ctx.szCol = 0;
        rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, 
            pText, nText, (void*)&ctx, fts5StorageInsertCallback
        );
        p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
        if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){
          rc = FTS5_CORRUPT;
        }
        sqlite3Fts5ClearLocale(pConfig);
      }
      sqlite3_value_free(pFree);
    }
  }
  if( rc==SQLITE_OK && p->nTotalRow<1 ){
    rc = FTS5_CORRUPT;
  }else{
    p->nTotalRow--;
  }
Changes to ext/fts5/test/fts5faultI.test.
320
321
322
323
324
325
326




















327
328
329
    execsql { SELECT 123 }
    faultsim_test_result \
      {1 {FOREIGN KEY constraint failed}} \
      {1 {out of memory}} \
      {1 {constraint failed}}
  }
}





















finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    execsql { SELECT 123 }
    faultsim_test_result \
      {1 {FOREIGN KEY constraint failed}} \
      {1 {out of memory}} \
      {1 {constraint failed}}
  }
}

#-------------------------------------------------------------------------
reset_db

do_execsql_test 13.0 {
  CREATE VIRTUAL TABLE t1 USING fts5(a, b);
  INSERT INTO t1 VALUES('abc def', X'123456');
}
faultsim_save_and_close


do_faultsim_test 13 -faults oom* -prep {
  faultsim_restore_and_reopen
} -body {
  execsql {
    UPDATE t1 SET a='def abc'
  }
} -test {
  faultsim_test_result {0 {}}
}

finish_test

Changes to ext/fts5/test/fts5unicode4.test.
23
24
25
26
27
28
29






























30
31
do_execsql_test 1.0 {
  CREATE VIRTUAL TABLE sss USING fts5(a, prefix=3); 
}

do_execsql_test 1.1 {
  INSERT INTO sss VALUES('まりや');
}































finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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
do_execsql_test 1.0 {
  CREATE VIRTUAL TABLE sss USING fts5(a, prefix=3); 
}

do_execsql_test 1.1 {
  INSERT INTO sss VALUES('まりや');
}

foreach {tn enc tok} {
  1   utf-8    ascii
  2   utf-16   ascii
  3   utf-8    unicode61
  4   utf-16   unicode61
} {
  reset_db

  do_execsql_test 1.$tn.0 " 
    PRAGMA encoding = '$enc'; 
    CREATE VIRTUAL TABLE vt2 USING fts5(c0, c1, tokenize=$tok);
  "

  do_execsql_test 1.$tn.1 {
    INSERT INTO vt2(c0, c1) VALUES ('bhal', x'17db');
  }

  do_execsql_test 1.$tn.2 {
    UPDATE vt2 SET c0='bhal';
  }

  do_execsql_test 1.$tn.3 {
    INSERT INTO vt2(vt2) VALUES('integrity-check')
  }

  do_execsql_test 1.$tn.4 {
    SELECT quote(c1) FROM vt2
  } {X'17DB'}
}

finish_test