/ Check-in [8a5b121f]
Login

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

Overview
Comment:Add tests (and fixes) for the virtual table transaction interface. (CVS 3265)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8a5b121f2f26bebe3f1164bc2f504d29b74400f4
User & Date: danielk1977 2006-06-17 09:39:56
Context
2006-06-17
10:44
Clear a compiler warning by adding a prototype to sqliteInt.h. (CVS 3266) check-in: ca541ef3 user: drh tags: trunk
09:39
Add tests (and fixes) for the virtual table transaction interface. (CVS 3265) check-in: 8a5b121f user: danielk1977 tags: trunk
06:31
When updating a view, invoke the authorization callback for reading the view before setting the authorization-context to the view name. (CVS 3264) check-in: 48d297c5 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.179 2006/06/16 08:01:04 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
  int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
  int (*xRowid)(sqlite3_vtab_cursor*, sqlite_int64 *pRowid);
  int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite_int64 *);
  int (*xBegin)(sqlite3_vtab *pVTab);
  int (*xSync)(sqlite3_vtab *pVTab);
  int (*xCommit)(sqlite3_vtab *pVTab);
  int (*xRollback)(sqlite3_vtab *pVTab);
  int (*xIsInTrans)(sqlite3_vtab *pVTab);
};

/*
** The sqlite3_index_info structure and its substructures is used to
** pass information into and receive the reply from the xBestIndex
** method of an sqlite3_module.  The fields under **Inputs** are the
** inputs to xBestIndex and are read-only.  xBestIndex inserts its







|







 







<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1554
1555
1556
1557
1558
1559
1560

1561
1562
1563
1564
1565
1566
1567
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.180 2006/06/17 09:39:56 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
  int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
  int (*xRowid)(sqlite3_vtab_cursor*, sqlite_int64 *pRowid);
  int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite_int64 *);
  int (*xBegin)(sqlite3_vtab *pVTab);
  int (*xSync)(sqlite3_vtab *pVTab);
  int (*xCommit)(sqlite3_vtab *pVTab);
  int (*xRollback)(sqlite3_vtab *pVTab);

};

/*
** The sqlite3_index_info structure and its substructures is used to
** pass information into and receive the reply from the xBestIndex
** method of an sqlite3_module.  The fields under **Inputs** are the
** inputs to xBestIndex and are read-only.  xBestIndex inserts its

Changes to src/test8.c.

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
...
609
610
611
612
613
614
615









































616
617
618
619
620
621
622
...
628
629
630
631
632
633
634
635




636
637
638
639
640
641
642
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the virtual table interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test8.c,v 1.24 2006/06/16 06:17:47 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

typedef struct echo_vtab echo_vtab;
typedef struct echo_cursor echo_cursor;









/* 
** An echo virtual-table object.
**
** echo.vtab.aIndex is an array of booleans. The nth entry is true if 
** the nth column of the real table is the left-most column of an index
** (implicit or otherwise). In other words, if SQLite can optimize
** a query like "SELECT * FROM real_table WHERE col = ?".
**
** Member variable contains copies of the column names of the real table.

*/
struct echo_vtab {
  sqlite3_vtab base;
  Tcl_Interp *interp;
  sqlite3 *db;

  char *zTableName;       /* Name of the real table */
................................................................................

  if( pRowid && rc==SQLITE_OK ){
    *pRowid = sqlite3_last_insert_rowid(db);
  }

  return rc;
}










































/*
** A virtual table module that merely echos method calls into TCL
** variables.
*/
static sqlite3_module echoModule = {
  0,                         /* iVersion */
................................................................................
  echoDestroy,
  echoOpen,                  /* xOpen - open a cursor */
  echoClose,                 /* xClose - close a cursor */
  echoFilter,                /* xFilter - configure scan constraints */
  echoNext,                  /* xNext - advance a cursor */
  echoColumn,                /* xColumn - read data */
  echoRowid,                 /* xRowid - read data */
  echoUpdate                 /* xUpdate - write data */




};

/*
** Decode a pointer to an sqlite3 object.
*/
static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
  *ppDb = (sqlite3*)sqlite3TextToPtr(zA);







|









>
>
>
>
>
>
>
>









|
>







 







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







 







|
>
>
>
>







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
...
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the virtual table interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test8.c,v 1.25 2006/06/17 09:39:56 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

typedef struct echo_vtab echo_vtab;
typedef struct echo_cursor echo_cursor;

/*
** The test module defined in this file uses two global Tcl variables to
** commicate with test-scripts:
**
**     $::echo_module
**     $::echo_module_sync_fail
*/

/* 
** An echo virtual-table object.
**
** echo.vtab.aIndex is an array of booleans. The nth entry is true if 
** the nth column of the real table is the left-most column of an index
** (implicit or otherwise). In other words, if SQLite can optimize
** a query like "SELECT * FROM real_table WHERE col = ?".
**
** Member variable aCol[] contains copies of the column names of the real
** table.
*/
struct echo_vtab {
  sqlite3_vtab base;
  Tcl_Interp *interp;
  sqlite3 *db;

  char *zTableName;       /* Name of the real table */
................................................................................

  if( pRowid && rc==SQLITE_OK ){
    *pRowid = sqlite3_last_insert_rowid(db);
  }

  return rc;
}

/*
** xBegin, xSync, xCommit and xRollback callbacks for echo module
** virtual tables. Do nothing other than add the name of the callback
** to the $::echo_module Tcl variable.
*/
static int echoTransactionCall(sqlite3_vtab *tab, const char *zCall){
  char *z;
  echo_vtab *pVtab = (echo_vtab *)tab;
  z = sqlite3_mprintf("echo(%s)", pVtab->zTableName);
  appendToEchoModule(pVtab->interp, zCall);
  appendToEchoModule(pVtab->interp, z);
  sqlite3_free(z);
  return SQLITE_OK;
}
static int echoBegin(sqlite3_vtab *tab){
  return echoTransactionCall(tab, "xBegin");
}
static int echoSync(sqlite3_vtab *tab){
  echo_vtab *pVtab = (echo_vtab *)tab;
  Tcl_Interp *interp = pVtab->interp;
  const char *zVal; 

  echoTransactionCall(tab, "xSync");

  /* Check if the $::echo_module_sync_fail variable is defined. If it is,
  ** and it is set to the name of the real table underlying this virtual
  ** echo module table, then cause this xSync operation to fail.
  */
  zVal = Tcl_GetVar(interp, "echo_module_sync_fail", TCL_GLOBAL_ONLY);
  if( zVal && 0==strcmp(zVal, pVtab->zTableName) ){
    return -1;
  }
  return SQLITE_OK;
}
static int echoCommit(sqlite3_vtab *tab){
  return echoTransactionCall(tab, "xCommit");
}
static int echoRollback(sqlite3_vtab *tab){
  return echoTransactionCall(tab, "xRollback");
}

/*
** A virtual table module that merely echos method calls into TCL
** variables.
*/
static sqlite3_module echoModule = {
  0,                         /* iVersion */
................................................................................
  echoDestroy,
  echoOpen,                  /* xOpen - open a cursor */
  echoClose,                 /* xClose - close a cursor */
  echoFilter,                /* xFilter - configure scan constraints */
  echoNext,                  /* xNext - advance a cursor */
  echoColumn,                /* xColumn - read data */
  echoRowid,                 /* xRowid - read data */
  echoUpdate,                /* xUpdate - write data */
  echoBegin,                 /* xBegin - begin transaction */
  echoSync,                  /* xSync - sync transaction */
  echoCommit,                /* xCommit - commit transaction */
  echoRollback               /* xRollback - rollback transaction */
};

/*
** Decode a pointer to an sqlite3 object.
*/
static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
  *ppDb = (sqlite3*)sqlite3TextToPtr(zA);

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
607
608
609
610
611
612
613

614
615
616
617
618
619
620
621
622
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.129 2006/06/17 06:31:19 danielk1977 Exp $
*/
#include "sqliteInt.h"

/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
  SrcList *pSrc,       /* The virtual table to be modified */
................................................................................
    sqlite3VdbeAddOp(v, OP_Column, ephemTab, 1);
  }else{
    sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
  }
  for(i=0; i<pTab->nCol; i++){
    sqlite3VdbeAddOp(v, OP_Column, ephemTab, i+1+(pRowid!=0));
  }

  sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2, 
                     (const char*)pTab->pVtab, P3_VTAB);
  sqlite3VdbeAddOp(v, OP_Next, ephemTab, addr);
  sqlite3VdbeAddOp(v, OP_Close, ephemTab, 0);

  /* Cleanup */
  sqlite3SelectDelete(pSelect);  
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */







|







 







>









8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.130 2006/06/17 09:39:56 danielk1977 Exp $
*/
#include "sqliteInt.h"

/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
  SrcList *pSrc,       /* The virtual table to be modified */
................................................................................
    sqlite3VdbeAddOp(v, OP_Column, ephemTab, 1);
  }else{
    sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
  }
  for(i=0; i<pTab->nCol; i++){
    sqlite3VdbeAddOp(v, OP_Column, ephemTab, i+1+(pRowid!=0));
  }
  pParse->pVirtualLock = pTab;
  sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2, 
                     (const char*)pTab->pVtab, P3_VTAB);
  sqlite3VdbeAddOp(v, OP_Next, ephemTab, addr);
  sqlite3VdbeAddOp(v, OP_Close, ephemTab, 0);

  /* Cleanup */
  sqlite3SelectDelete(pSelect);  
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */

Changes to src/vtab.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
**    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.16 2006/06/17 03:27:23 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** External API function used to create a new virtual-table module.
*/
................................................................................

static int callFinaliser(sqlite3 *db, int offset, int doDelete){
  int rc = SQLITE_OK;
  int i;
  for(i=0; rc==SQLITE_OK && i<db->nVTrans && db->aVTrans[i]; i++){
    sqlite3_vtab *pVtab = db->aVTrans[i];
    int (*x)(sqlite3_vtab *);
    x = (int (*)(sqlite3_vtab *))((char *)pVtab->pModule + offset);
    if( x ){
      rc = x(pVtab);
    }
  }
  if( doDelete ){
    sqliteFree(db->aVTrans);
    db->nVTrans = 0;







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
**    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.17 2006/06/17 09:39:56 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"

/*
** External API function used to create a new virtual-table module.
*/
................................................................................

static int callFinaliser(sqlite3 *db, int offset, int doDelete){
  int rc = SQLITE_OK;
  int i;
  for(i=0; rc==SQLITE_OK && i<db->nVTrans && db->aVTrans[i]; i++){
    sqlite3_vtab *pVtab = db->aVTrans[i];
    int (*x)(sqlite3_vtab *);
    x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset);
    if( x ){
      rc = x(pVtab);
    }
  }
  if( doDelete ){
    sqliteFree(db->aVTrans);
    db->nVTrans = 0;

Changes to test/auth.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: auth.test,v 1.35 2006/06/17 06:31:19 danielk1977 Exp $
#

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

# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
# defined during compilation.
................................................................................
      INSERT INTO v1chng VALUES(OLD.x,NEW.x);
    END;
    SELECT * FROM v1;
  }
} {115 117}
do_test auth-4.3 {
  set authargs {}
breakpoint
  execsql {
    UPDATE v1 SET x=1 WHERE x=117
  }
  set authargs
} [list \
  SQLITE_UPDATE v1     x  main {} \
  SQLITE_READ   v1     x  main {} \







|







 







<







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
2221
2222
2223
2224
2225
2226
2227

2228
2229
2230
2231
2232
2233
2234
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: auth.test,v 1.36 2006/06/17 09:39:56 danielk1977 Exp $
#

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

# disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
# defined during compilation.
................................................................................
      INSERT INTO v1chng VALUES(OLD.x,NEW.x);
    END;
    SELECT * FROM v1;
  }
} {115 117}
do_test auth-4.3 {
  set authargs {}

  execsql {
    UPDATE v1 SET x=1 WHERE x=117
  }
  set authargs
} [list \
  SQLITE_UPDATE v1     x  main {} \
  SQLITE_READ   v1     x  main {} \

Added test/vtab4.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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
# 2006 June 10
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus is on testing the following virtual table methods:
#
#     xBegin
#     xSync
#     xCommit
#     xRollback
#
# $Id: vtab4.test,v 1.1 2006/06/17 09:39:56 danielk1977 Exp $

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

ifcapable !vtab {
  finish_test
  return
}

# Register the echo module
db cache size 0
register_echo_module [sqlite3_connection_pointer db]

do_test vtab4-1.1 {
  execsql {
    CREATE TABLE treal(a PRIMARY KEY, b, c);
    CREATE VIRTUAL TABLE techo USING echo(treal);
  }
} {}

# Test an INSERT, UPDATE and DELETE statement on the virtual table
# in an implicit transaction. Each should result in a single call
# to xBegin, xSync and xCommit.
#
do_test vtab4-1.2 {
  set echo_module [list]
  execsql {
    INSERT INTO techo VALUES(1, 2, 3);
  }
  set echo_module
} {xBegin echo(treal) xSync echo(treal) xCommit echo(treal)}
do_test vtab4-1.3 {
  set echo_module [list]
  execsql {
    UPDATE techo SET a = 2;
  }
  set echo_module
} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
        xBegin     echo(treal)                    \
        xFilter    {SELECT rowid, * FROM 'treal'} \
        xSync      echo(treal)                    \
        xCommit    echo(treal)                    \
]
do_test vtab4-1.4 {
  set echo_module [list]
  execsql {
    DELETE FROM techo;
  }
  set echo_module
} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
        xBegin     echo(treal)                    \
        xFilter    {SELECT rowid, * FROM 'treal'} \
        xSync      echo(treal)                    \
        xCommit    echo(treal)                    \
]

# Ensure xBegin is not called more than once in a single transaction.
#
do_test vtab4-2.1 {
  set echo_module [list]
  execsql {
    BEGIN;
    INSERT INTO techo VALUES(1, 2, 3);
    INSERT INTO techo VALUES(4, 5, 6);
    INSERT INTO techo VALUES(7, 8, 9);
    COMMIT;
  }
  set echo_module
} {xBegin echo(treal) xSync echo(treal) xCommit echo(treal)}

# Try a transaction with two virtual tables.
#
do_test vtab4-2.2 {
  execsql {
    CREATE TABLE sreal(a, b, c UNIQUE);
    CREATE VIRTUAL TABLE secho USING echo(sreal);
  }
  set echo_module [list]
  execsql {
    BEGIN;
    INSERT INTO secho SELECT * FROM techo;
    DELETE FROM techo;
    COMMIT;
  }
  set echo_module
} [list xBestIndex {SELECT rowid, * FROM 'treal'} \
        xBegin     echo(sreal)                    \
        xFilter    {SELECT rowid, * FROM 'treal'} \
        xBestIndex {SELECT rowid, * FROM 'treal'} \
        xBegin     echo(treal)                    \
        xFilter    {SELECT rowid, * FROM 'treal'} \
        xSync   echo(sreal)                       \
        xSync   echo(treal)                       \
        xCommit echo(sreal)                       \
        xCommit echo(treal)                       \
]
do_test vtab4-2.3 {
  execsql {
    SELECT * FROM secho;
  }
} {1 2 3 4 5 6 7 8 9}
do_test vtab4-2.4 {
  execsql {
    SELECT * FROM techo;
  }
} {}

# Try an explicit ROLLBACK on a transaction with two open virtual tables.
do_test vtab4-2.5 {
  set echo_module [list]
  execsql {
    BEGIN;
    INSERT INTO techo SELECT * FROM secho;
    DELETE FROM secho;
    ROLLBACK;
  }
  set echo_module
} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \
        xBegin     echo(treal)                    \
        xFilter    {SELECT rowid, * FROM 'sreal'} \
        xBestIndex {SELECT rowid, * FROM 'sreal'} \
        xBegin     echo(sreal)                    \
        xFilter    {SELECT rowid, * FROM 'sreal'} \
        xRollback  echo(treal)                    \
        xRollback  echo(sreal)                    \
]
do_test vtab4-2.6 {
  execsql {
    SELECT * FROM secho;
  }
} {1 2 3 4 5 6 7 8 9}
do_test vtab4-2.7 {
  execsql {
    SELECT * FROM techo;
  }
} {}

do_test vtab4-3.1 {
  set echo_module [list]
  set echo_module_sync_fail treal
  catchsql {
    INSERT INTO techo VALUES(1, 2, 3);
  }
} {1 {unknown error}}
do_test vtab4-3.2 {
  set echo_module
} {xBegin echo(treal) xSync echo(treal) xRollback echo(treal)}

breakpoint
do_test vtab4-3.3 {
  set echo_module [list]
  set echo_module_sync_fail sreal
  catchsql {
    BEGIN;
    INSERT INTO techo SELECT * FROM secho;
    DELETE FROM secho;
    COMMIT;
  }
  set echo_module
} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \
        xBegin     echo(treal)                    \
        xFilter    {SELECT rowid, * FROM 'sreal'} \
        xBestIndex {SELECT rowid, * FROM 'sreal'} \
        xBegin     echo(sreal)                    \
        xFilter    {SELECT rowid, * FROM 'sreal'} \
        xSync      echo(treal)                    \
        xSync      echo(sreal)                    \
        xRollback  echo(treal)                    \
        xRollback  echo(sreal)                    \
]

finish_test