/ Check-in [e705d232]
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:Disallow writing to, creating or dropping virtual tables from within xSync() callbacks. (CVS 3339)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e705d232324c71738b8b25713a134617dd1fc525
User & Date: danielk1977 2006-07-26 16:22:15
Context
2006-07-28
20:16
Report any error which occurs in closing the database at exit. Free a couple of strings which were leaking. (CVS 3340) check-in: 47353f62 user: adamd tags: trunk
2006-07-26
16:22
Disallow writing to, creating or dropping virtual tables from within xSync() callbacks. (CVS 3339) check-in: e705d232 user: danielk1977 tags: trunk
14:57
Fix an overzealous assert() in the UTF-16 to UTF-8 converter. (CVS 3338) check-in: 096bd821 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
842
843
844
845
846
847
848






849
850
851
852
853
854
855
....
1949
1950
1951
1952
1953
1954
1955









1956
1957
1958
1959
1960
1961
1962
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.408 2006/07/11 14:17:52 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
................................................................................
  ** indices.  Hence, the record number for the table must be allocated
  ** now.
  */
  if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
    int lbl;
    int fileFormat;
    sqlite3BeginWriteOperation(pParse, 0, iDb);







    /* If the file format and encoding in the database have not been set, 
    ** set them now.
    */
    sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1);   /* file_format */
    lbl = sqlite3VdbeMakeLabel(v);
    sqlite3VdbeAddOp(v, OP_If, 0, lbl);
................................................................................
  ** on disk.
  */
  v = sqlite3GetVdbe(pParse);
  if( v ){
    Trigger *pTrigger;
    Db *pDb = &db->aDb[iDb];
    sqlite3BeginWriteOperation(pParse, 0, iDb);










    /* Drop all triggers associated with the table being dropped. Code
    ** is generated to remove entries from sqlite_master and/or
    ** sqlite_temp_master if required.
    */
    pTrigger = pTab->pTrigger;
    while( pTrigger ){







|







 







>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
....
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.409 2006/07/26 16:22:15 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
................................................................................
  ** indices.  Hence, the record number for the table must be allocated
  ** now.
  */
  if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
    int lbl;
    int fileFormat;
    sqlite3BeginWriteOperation(pParse, 0, iDb);

#ifndef SQLITE_OMIT_VIRTUALTABLE
    if( isVirtual ){
      sqlite3VdbeAddOp(v, OP_VBegin, 0, 0);
    }
#endif

    /* If the file format and encoding in the database have not been set, 
    ** set them now.
    */
    sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1);   /* file_format */
    lbl = sqlite3VdbeMakeLabel(v);
    sqlite3VdbeAddOp(v, OP_If, 0, lbl);
................................................................................
  ** on disk.
  */
  v = sqlite3GetVdbe(pParse);
  if( v ){
    Trigger *pTrigger;
    Db *pDb = &db->aDb[iDb];
    sqlite3BeginWriteOperation(pParse, 0, iDb);

#ifndef SQLITE_OMIT_VIRTUALTABLE
    if( IsVirtual(pTab) ){
      Vdbe *v = sqlite3GetVdbe(pParse);
      if( v ){
        sqlite3VdbeAddOp(v, OP_VBegin, 0, 0);
      }
    }
#endif

    /* Drop all triggers associated with the table being dropped. Code
    ** is generated to remove entries from sqlite_master and/or
    ** sqlite_temp_master if required.
    */
    pTrigger = pTab->pTrigger;
    while( pTrigger ){

Changes to src/vtab.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
508
509
510
511
512
513
514

515

516

517
518
519
520
521
522
523
524

525

526
527
528
529
530
531
532
...
553
554
555
556
557
558
559
560














561
562

563
564
565
566
567
568
569
**    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.
**
*************************************************************************
** This file contains code used to help implement virtual tables.
**
** $Id: vtab.c,v 1.28 2006/07/25 15:14:53 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** External API function used to create a new virtual-table module.
*/
................................................................................
    *pzErr = sqlite3MPrintf("no such module: %s", zModule);
    rc = SQLITE_ERROR;
  }else{
    rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
  }

  if( rc==SQLITE_OK && pTab->pVtab ){
    rc = addToVTrans(db, pTab->pVtab);
  }

  return rc;
}

/*
** This function is used to set the schema of a virtual table.  It is only
................................................................................
** sqlite3.aVTrans array. Return the error code for the first error 
** that occurs, or SQLITE_OK if all xSync operations are successful.
*/
int sqlite3VtabSync(sqlite3 *db, int rc2){
  int i;
  int rc = SQLITE_OK;
  int rcsafety;

  if( rc2!=SQLITE_OK ) return rc2;

  rc = sqlite3SafetyOff(db);

  for(i=0; rc==SQLITE_OK && i<db->nVTrans && db->aVTrans[i]; i++){
    sqlite3_vtab *pVtab = db->aVTrans[i];
    int (*x)(sqlite3_vtab *);
    x = pVtab->pModule->xSync;
    if( x ){
      rc = x(pVtab);
    }
  }

  rcsafety = sqlite3SafetyOn(db);

  if( rc==SQLITE_OK ){
    rc = rcsafety;
  }
  return rc;
}

/*
................................................................................
** not currently open, invoke the xBegin method now.
**
** If the xBegin call is successful, place the sqlite3_vtab pointer
** in the sqlite3.aVTrans array.
*/
int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
  int rc = SQLITE_OK;
  const sqlite3_module *pModule = pVtab->pModule;














  if( pModule->xBegin ){
    int i;


    /* If pVtab is already in the aVTrans array, return early */
    for(i=0; (i<db->nVTrans) && 0!=db->aVTrans[i]; i++){
      if( db->aVTrans[i]==pVtab ){
        return SQLITE_OK;
      }
    }







|







 







|







 







>

>

>
|
|






>

>







 







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


>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
...
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
**    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.
**
*************************************************************************
** This file contains code used to help implement virtual tables.
**
** $Id: vtab.c,v 1.29 2006/07/26 16:22:15 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** External API function used to create a new virtual-table module.
*/
................................................................................
    *pzErr = sqlite3MPrintf("no such module: %s", zModule);
    rc = SQLITE_ERROR;
  }else{
    rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
  }

  if( rc==SQLITE_OK && pTab->pVtab ){
      rc = addToVTrans(db, pTab->pVtab);
  }

  return rc;
}

/*
** This function is used to set the schema of a virtual table.  It is only
................................................................................
** sqlite3.aVTrans array. Return the error code for the first error 
** that occurs, or SQLITE_OK if all xSync operations are successful.
*/
int sqlite3VtabSync(sqlite3 *db, int rc2){
  int i;
  int rc = SQLITE_OK;
  int rcsafety;
  sqlite3_vtab **aVTrans = db->aVTrans;
  if( rc2!=SQLITE_OK ) return rc2;

  rc = sqlite3SafetyOff(db);
  db->aVTrans = 0;
  for(i=0; rc==SQLITE_OK && i<db->nVTrans && aVTrans[i]; i++){
    sqlite3_vtab *pVtab = aVTrans[i];
    int (*x)(sqlite3_vtab *);
    x = pVtab->pModule->xSync;
    if( x ){
      rc = x(pVtab);
    }
  }
  db->aVTrans = aVTrans;
  rcsafety = sqlite3SafetyOn(db);

  if( rc==SQLITE_OK ){
    rc = rcsafety;
  }
  return rc;
}

/*
................................................................................
** not currently open, invoke the xBegin method now.
**
** If the xBegin call is successful, place the sqlite3_vtab pointer
** in the sqlite3.aVTrans array.
*/
int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
  int rc = SQLITE_OK;
  const sqlite3_module *pModule;

  /* Special case: If db->aVTrans is NULL and db->nVTrans is greater
  ** than zero, then this function is being called from within a
  ** virtual module xSync() callback. It is illegal to write to 
  ** virtual module tables in this case, so return SQLITE_LOCKED.
  */
  if( 0==db->aVTrans && db->nVTrans>0 ){
    return SQLITE_LOCKED;
  }
  if( !pVtab ){
    return SQLITE_OK;
  } 
  pModule = pVtab->pModule;

  if( pModule->xBegin ){
    int i;


    /* If pVtab is already in the aVTrans array, return early */
    for(i=0; (i<db->nVTrans) && 0!=db->aVTrans[i]; i++){
      if( db->aVTrans[i]==pVtab ){
        return SQLITE_OK;
      }
    }

Changes to test/vtab7.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
151
152
153
154
155
156
157




































158
159
160
161
162
163
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this test is reading and writing to the database from within a
# virtual table xSync() callback.
#
# $Id: vtab7.test,v 1.1 2006/07/25 15:14:53 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !vtab {
  finish_test
  return
................................................................................
#     execsql { INSERT INTO log VALUES('hello') }
#   }
#   catchsql {
#     INSERT INTO abc2 VALUES(1, 2, 3);
#   }
# } {1 {library routine called out of sequence}}






































trace remove variable ::echo_module write echo_module_trace
unset -nocomplain ::callbacks

finish_test








|







 







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






8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this test is reading and writing to the database from within a
# virtual table xSync() callback.
#
# $Id: vtab7.test,v 1.2 2006/07/26 16:22:16 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !vtab {
  finish_test
  return
................................................................................
#     execsql { INSERT INTO log VALUES('hello') }
#   }
#   catchsql {
#     INSERT INTO abc2 VALUES(1, 2, 3);
#   }
# } {1 {library routine called out of sequence}}

# These tests, vtab7-4.*, test that an SQLITE_LOCKED error is returned
# if an attempt to write to a virtual module table or create a new 
# virtual table from within an xSync() callback.
do_test vtab7-4.1 {
  execsql {
    CREATE TABLE def(d, e, f);
    CREATE VIRTUAL TABLE def2 USING echo(def);
  }
  set ::callbacks(xSync,abc) {
    set ::error [catchsql { INSERT INTO def2 VALUES(1, 2, 3) }]
  }
  execsql {
    INSERT INTO abc2 VALUES(1, 2, 3);
  }
  set ::error
} {1 {database table is locked}}
do_test vtab7-4.2 {
  set ::callbacks(xSync,abc) {
    set ::error [catchsql { CREATE VIRTUAL TABLE def3 USING echo(def) }]
  }
  execsql {
    INSERT INTO abc2 VALUES(1, 2, 3);
  }
  set ::error
} {1 {database table is locked}}

do_test vtab7-4.3 {
  set ::callbacks(xSync,abc) {
    set ::error [catchsql { DROP TABLE def2 }]
  }
  execsql {
    INSERT INTO abc2 VALUES(1, 2, 3);
    SELECT name FROM sqlite_master ORDER BY name;
  }
  set ::error
} {1 {database table is locked}}

trace remove variable ::echo_module write echo_module_trace
unset -nocomplain ::callbacks

finish_test