SQLite

Check-in [6997e00c32]
Login

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

Overview
Comment:Test case for writing to a WITHOUT ROWID virtual table. The TCLVAR virtual table is modified to add a "fullname" column which is the primary key, and to accept update operations against the primary key.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | writable-vtab-without-rowid
Files: files | file ages | folders
SHA3-256: 6997e00c3221f266f4d9187501d8a9e5bafb85551e88a744cdc8ffe3b75ec2a4
User & Date: drh 2017-08-10 17:53:11.866
Context
2017-08-10
19:10
Add extra tests to this branch. (check-in: b0e3b88a79 user: dan tags: writable-vtab-without-rowid)
17:53
Test case for writing to a WITHOUT ROWID virtual table. The TCLVAR virtual table is modified to add a "fullname" column which is the primary key, and to accept update operations against the primary key. (check-in: 6997e00c32 user: drh tags: writable-vtab-without-rowid)
15:19
Experimental changes that allow a WITHOUT ROWID virtual table to be writable as long as it has only a single-column PRIMARY KEY. (check-in: ab9ee4c1e6 user: drh tags: writable-vtab-without-rowid)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/test_tclvar.c.
11
12
13
14
15
16
17



















18
19
20
21
22
23
24
*************************************************************************
** 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.
**
** The emphasis of this file is a virtual table that provides
** access to TCL variables.



















*/
#include "sqliteInt.h"
#if defined(INCLUDE_SQLITE_TCL_H)
#  include "sqlite_tcl.h"
#else
#  include "tcl.h"
#endif







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







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
*************************************************************************
** 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.
**
** The emphasis of this file is a virtual table that provides
** access to TCL variables.
**
** The TCLVAR eponymous virtual table has a schema like this:
**
**    CREATE TABLE tclvar(
**       name TEXT,       -- base name of the variable:  "x" in "$x(y)"
**       arrayname TEXT,  -- array index name: "y" in "$x(y)"
**       value TEXT,      -- the value of the variable 
**       fullname TEXT,   -- the full name of the variable
**       PRIMARY KEY(fullname)
**    ) WITHOUT ROWID;
**
** DELETE, INSERT, and UPDATE operations use the "fullname" field to
** determine the variable to be modified.  Changing "value" to NULL
** deletes the variable.
**
** For SELECT operations, the "name" and "arrayname" fields will always
** match the "fullname" field.  For DELETE, INSERT, and UPDATE, the
** "name" and "arrayname" fields are ignored and the variable is modified
** according to "fullname" and "value" only.
*/
#include "sqliteInt.h"
#if defined(INCLUDE_SQLITE_TCL_H)
#  include "sqlite_tcl.h"
#else
#  include "tcl.h"
#endif
63
64
65
66
67
68
69
70





71
72
73
74
75
76
77
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  tclvar_vtab *pVtab;
  static const char zSchema[] = 
     "CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";





  pVtab = sqlite3MallocZero( sizeof(*pVtab) );
  if( pVtab==0 ) return SQLITE_NOMEM;
  *ppVtab = &pVtab->base;
  pVtab->interp = (Tcl_Interp *)pAux;
  sqlite3_declare_vtab(db, zSchema);
  return SQLITE_OK;
}







|
>
>
>
>
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  tclvar_vtab *pVtab;
  static const char zSchema[] = 
     "CREATE TABLE x("
     "  name TEXT,"                       /* Base name */
     "  arrayname TEXT,"                  /* Array index */
     "  value TEXT,"                      /* Value */
     "  fullname TEXT PRIMARY KEY"        /* base(index) name */
     ") WITHOUT ROWID";
  pVtab = sqlite3MallocZero( sizeof(*pVtab) );
  if( pVtab==0 ) return SQLITE_NOMEM;
  *ppVtab = &pVtab->base;
  pVtab->interp = (Tcl_Interp *)pAux;
  sqlite3_declare_vtab(db, zSchema);
  return SQLITE_OK;
}
247
248
249
250
251
252
253










254
255
256
257
258
259
260
      break;
    }
    case 2: {
      Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
      sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
      break;
    }










  }
  return SQLITE_OK;
}

static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  *pRowid = 0;
  return SQLITE_OK;







>
>
>
>
>
>
>
>
>
>







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
      break;
    }
    case 2: {
      Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
      sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
      break;
    }
    case 3: {
      char *z3;
      if( p2 ){
        z3 = sqlite3_mprintf("%s(%s)", z1, z2);
        sqlite3_result_text(ctx, z3, -1, sqlite3_free);
      }else{
        sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
      }
      break;
    }
  }
  return SQLITE_OK;
}

static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  *pRowid = 0;
  return SQLITE_OK;
371
372
373
374
375
376
377




















































378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
    }
  }
  pIdxInfo->idxStr = zStr;
  pIdxInfo->needToFreeIdxStr = 1;

  return SQLITE_OK;
}





















































/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module tclvarModule = {
  0,                         /* iVersion */
  tclvarConnect,
  tclvarConnect,
  tclvarBestIndex,
  tclvarDisconnect, 
  tclvarDisconnect,
  tclvarOpen,                  /* xOpen - open a cursor */
  tclvarClose,                 /* xClose - close a cursor */
  tclvarFilter,                /* xFilter - configure scan constraints */
  tclvarNext,                  /* xNext - advance a cursor */
  tclvarEof,                   /* xEof - check for end of scan */
  tclvarColumn,                /* xColumn - read data */
  tclvarRowid,                 /* xRowid - read data */
  0,                           /* xUpdate */
  0,                           /* xBegin */
  0,                           /* xSync */
  0,                           /* xCommit */
  0,                           /* xRollback */
  0,                           /* xFindMethod */
  0,                           /* xRename */
};







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



















|







405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
    }
  }
  pIdxInfo->idxStr = zStr;
  pIdxInfo->needToFreeIdxStr = 1;

  return SQLITE_OK;
}

/*
** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table
*/
static int tclvarUpdate(
  sqlite3_vtab *tab,
  int argc,
  sqlite3_value **argv,
  sqlite_int64 *pRowid
){
  tclvar_vtab *pTab = (tclvar_vtab*)tab;
  if( argc==1 ){
    /* A DELETE operation.  The variable to be deleted is stored in argv[0] */
    const char *zVar = (const char*)sqlite3_value_text(argv[0]);
    Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY);
    return SQLITE_OK;
  }
  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
    /* An INSERT operation */
    const char *zValue = (const char*)sqlite3_value_text(argv[4]);
    const char *zName;
    if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){
      tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT");
      return SQLITE_ERROR;
    }
    zName = (const char*)sqlite3_value_text(argv[5]);
    if( zValue ){
      Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY);
    }else{
      Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY);
    }
    return SQLITE_OK;
  }
  if( sqlite3_value_type(argv[0])==SQLITE_TEXT
   && sqlite3_value_type(argv[1])==SQLITE_TEXT
  ){
    /* An UPDATE operation */
    const char *zOldName = (const char*)sqlite3_value_text(argv[0]);
    const char *zNewName = (const char*)sqlite3_value_text(argv[1]);
    const char *zValue = (const char*)sqlite3_value_text(argv[4]);

    if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){
      Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY);
    }
    if( zValue!=0 ){
      Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY);
    }
    return SQLITE_OK;
  }
  tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change");
  return SQLITE_ERROR;
}

/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module tclvarModule = {
  0,                         /* iVersion */
  tclvarConnect,
  tclvarConnect,
  tclvarBestIndex,
  tclvarDisconnect, 
  tclvarDisconnect,
  tclvarOpen,                  /* xOpen - open a cursor */
  tclvarClose,                 /* xClose - close a cursor */
  tclvarFilter,                /* xFilter - configure scan constraints */
  tclvarNext,                  /* xNext - advance a cursor */
  tclvarEof,                   /* xEof - check for end of scan */
  tclvarColumn,                /* xColumn - read data */
  tclvarRowid,                 /* xRowid - read data */
  tclvarUpdate,                /* xUpdate */
  0,                           /* xBegin */
  0,                           /* xSync */
  0,                           /* xCommit */
  0,                           /* xRollback */
  0,                           /* xFindMethod */
  0,                           /* xRename */
};
Changes to test/vtab2.test.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
} {6}

register_tclvar_module [sqlite3_connection_pointer db]
do_test vtab2-2.1 {
  set ::abc 123
  execsql {
    CREATE VIRTUAL TABLE vars USING tclvar;
    SELECT * FROM vars WHERE name='abc';
  }
} [list abc "" 123]
do_test vtab2-2.2 {
  set A(1) 1
  set A(2) 4
  set A(3) 9
  execsql {
    SELECT * FROM vars WHERE name='A';
  }
} [list A 1 1 A 2 4 A 3 9]
unset -nocomplain result
unset -nocomplain var
set result {}
foreach var [lsort [info vars tcl_*]] {
  catch {lappend result $var [set $var]}







|







|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
} {6}

register_tclvar_module [sqlite3_connection_pointer db]
do_test vtab2-2.1 {
  set ::abc 123
  execsql {
    CREATE VIRTUAL TABLE vars USING tclvar;
    SELECT name, arrayname, value FROM vars WHERE name='abc';
  }
} [list abc "" 123]
do_test vtab2-2.2 {
  set A(1) 1
  set A(2) 4
  set A(3) 9
  execsql {
    SELECT name, arrayname, value FROM vars WHERE name='A';
  }
} [list A 1 1 A 2 4 A 3 9]
unset -nocomplain result
unset -nocomplain var
set result {}
foreach var [lsort [info vars tcl_*]] {
  catch {lappend result $var [set $var]}
Changes to test/vtabE.test.
35
36
37
38
39
40
41


42
43
44
45
46
47
48
49
set vtabE2(c) d

do_test vtabE-1 {
  db eval {
    CREATE VIRTUAL TABLE t1 USING tclvar;
    CREATE VIRTUAL TABLE t2 USING tclvar;
    CREATE TABLE t3(a INTEGER PRIMARY KEY, b);


    SELECT t1.*, t2.*, abs(t3.b + abs(t2.value + abs(t1.value)))
      FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname
           LEFT JOIN t3 ON t3.a=t2.value
     WHERE t1.name = 'vtabE'
     ORDER BY t1.value, t2.value;
  }
} {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}}








>
>
|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
set vtabE2(c) d

do_test vtabE-1 {
  db eval {
    CREATE VIRTUAL TABLE t1 USING tclvar;
    CREATE VIRTUAL TABLE t2 USING tclvar;
    CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
    SELECT t1.name, t1.arrayname, t1.value,
           t2.name, t2.arrayname, t2.value,
           abs(t3.b + abs(t2.value + abs(t1.value)))
      FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname
           LEFT JOIN t3 ON t3.a=t2.value
     WHERE t1.name = 'vtabE'
     ORDER BY t1.value, t2.value;
  }
} {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}}

Changes to test/vtabH.test.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#--------------------------------------------------------------------------

register_tclvar_module db
set ::xyz 10
do_execsql_test 2.0 {
  CREATE VIRTUAL TABLE vars USING tclvar;
  SELECT * FROM vars WHERE name = 'xyz';
} {xyz {} 10}

set x1 aback
set x2 abaft
set x3 abandon
set x4 abandonint
set x5 babble







|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#--------------------------------------------------------------------------

register_tclvar_module db
set ::xyz 10
do_execsql_test 2.0 {
  CREATE VIRTUAL TABLE vars USING tclvar;
  SELECT name, arrayname, value FROM vars WHERE name = 'xyz';
} {xyz {} 10}

set x1 aback
set x2 abaft
set x3 abandon
set x4 abandonint
set x5 babble
Added test/vtabJ.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
# 2017-08-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 tests of writing to WITHOUT ROWID virtual tables
# using the tclvar eponymous virtual table.
#

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

ifcapable !vtab {
  finish_test
  return
}

register_tclvar_module db

unset -nocomplain vtabJ
do_test 100 {
  set vtabJ(1) this
  set vtabJ(two) is
  set vtabJ(3) {a test}
  db eval {
    SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
  }
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(two) is}

do_execsql_test 110 {
  INSERT INTO tclvar(fullname, value)
    VALUES('vtabJ(4)',4),('vtabJ(five)',555);
  SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}
do_test 111 {
  set res {}
  foreach vname [lsort [array names vtabJ]] {
    lappend res vtabJ($vname) $vtabJ($vname)
  }
  set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}

do_test 120 {
  db eval {
    INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',444);
  }
  set vtabJ(4)
} {444}

do_test 130 {
  db eval {
    INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',NULL);
  }
  info exists vtabJ(4)
} {0}

do_test 140 {
  db eval {
    UPDATE tclvar SET value=55 WHERE fullname='vtabJ(five)';
  }
  set vtabJ(five)
} {55}

do_test 150 {
  db eval {
    UPDATE tclvar SET fullname='vtabJ(5)' WHERE fullname='vtabJ(five)';
  }
  set vtabJ(5)
} {55}
do_test 151 {
  info exists vtabJ(five)
} {0}
do_test 152 {
  set res {}
  foreach vname [lsort [array names vtabJ]] {
    lappend res vtabJ($vname) $vtabJ($vname)
  }
  set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55 vtabJ(two) is}

do_execsql_test 160 {
  SELECT fullname FROM tclvar WHERE arrayname='two'
} {vtabJ(two)}
do_execsql_test 161 {
  DELETE FROM tclvar WHERE arrayname='two';
  SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}
do_test 162 {
  set res {}
  foreach vname [lsort [array names vtabJ]] {
    lappend res vtabJ($vname) $vtabJ($vname)
  }
  set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}

finish_test