/ Check-in [77f150b8]
Login

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

Overview
Comment:Add support for the VACUUM INTO command.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:77f150b8b46761f4f62f9d8926c10a95a70589a4525393fc16b321bd98c083a7
User & Date: drh 2018-12-10 01:48:29
Context
2018-12-10
16:00
Refactor the sqlite3_normalized_sql() implementation. This is a work-in-progress. There are still issues. check-in: a4c890b0 user: drh tags: normalize-refactor
08:41
Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum. check-in: c878d741 user: dan tags: trunk
02:00
Merge enhancements from trunk. check-in: b1bbc718 user: drh tags: apple-osx
01:48
Add support for the VACUUM INTO command. check-in: 77f150b8 user: drh tags: trunk
00:41
Fix the shell1.test test for the new format of the .backup command. Closed-Leaf check-in: 9748d799 user: drh tags: vacuum-into
2018-12-09
18:55
New test case for ticket [1d958d90596593a77420e59]. check-in: b7bf3c98 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/parse.y.

  1363   1363   //
  1364   1364   cmd ::= DROP INDEX ifexists(E) fullname(X).   {sqlite3DropIndex(pParse, X, E);}
  1365   1365   
  1366   1366   ///////////////////////////// The VACUUM command /////////////////////////////
  1367   1367   //
  1368   1368   %ifndef SQLITE_OMIT_VACUUM
  1369   1369   %ifndef SQLITE_OMIT_ATTACH
  1370         -cmd ::= VACUUM.                {sqlite3Vacuum(pParse,0);}
  1371         -cmd ::= VACUUM nm(X).          {sqlite3Vacuum(pParse,&X);}
         1370  +%type vinto {Expr*}
         1371  +%destructor vinto {sqlite3ExprDelete(pParse->db, $$);}
         1372  +cmd ::= VACUUM vinto(Y).                {sqlite3Vacuum(pParse,0,Y);}
         1373  +cmd ::= VACUUM nm(X) vinto(Y).          {sqlite3Vacuum(pParse,&X,Y);}
         1374  +vinto(A) ::= INTO expr(X).              {A = X;}
         1375  +vinto(A) ::= .                          {A = 0;}
  1372   1376   %endif  SQLITE_OMIT_ATTACH
  1373   1377   %endif  SQLITE_OMIT_VACUUM
  1374   1378   
  1375   1379   ///////////////////////////// The PRAGMA command /////////////////////////////
  1376   1380   //
  1377   1381   %ifndef SQLITE_OMIT_PRAGMA
  1378   1382   cmd ::= PRAGMA nm(X) dbnm(Z).                {sqlite3Pragma(pParse,&X,&Z,0,0);}

Changes to src/shell.c.in.

  3373   3373     "      http://sqlite.org/cli.html#sqlar_archive_support",
  3374   3374   #endif
  3375   3375   #ifndef SQLITE_OMIT_AUTHORIZATION
  3376   3376     ".auth ON|OFF             Show authorizer callbacks",
  3377   3377   #endif
  3378   3378     ".backup ?DB? FILE        Backup DB (default \"main\") to FILE",
  3379   3379     "       --append            Use the appendvfs",
         3380  +  "       --async             Write to FILE without a journal and without fsync()",
  3380   3381     ".bail on|off             Stop after hitting an error.  Default OFF",
  3381   3382     ".binary on|off           Turn binary output on or off.  Default OFF",
  3382   3383     ".cd DIRECTORY            Change the working directory to DIRECTORY",
  3383   3384     ".changes on|off          Show number of rows changed by SQL",
  3384   3385     ".check GLOB              Fail if output since .testcase does not match",
  3385   3386     ".clone NEWDB             Clone data into NEWDB from the existing database",
  3386   3387     ".databases               List names and files of attached databases",
................................................................................
  5836   5837      || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
  5837   5838     ){
  5838   5839       const char *zDestFile = 0;
  5839   5840       const char *zDb = 0;
  5840   5841       sqlite3 *pDest;
  5841   5842       sqlite3_backup *pBackup;
  5842   5843       int j;
         5844  +    int bAsync = 0;
  5843   5845       const char *zVfs = 0;
  5844   5846       for(j=1; j<nArg; j++){
  5845   5847         const char *z = azArg[j];
  5846   5848         if( z[0]=='-' ){
  5847   5849           if( z[1]=='-' ) z++;
  5848   5850           if( strcmp(z, "-append")==0 ){
  5849   5851             zVfs = "apndvfs";
         5852  +        }else
         5853  +        if( strcmp(z, "-async")==0 ){
         5854  +          bAsync = 1;
  5850   5855           }else
  5851   5856           {
  5852   5857             utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
  5853   5858             return 1;
  5854   5859           }
  5855   5860         }else if( zDestFile==0 ){
  5856   5861           zDestFile = azArg[j];
  5857   5862         }else if( zDb==0 ){
  5858   5863           zDb = zDestFile;
  5859   5864           zDestFile = azArg[j];
  5860   5865         }else{
  5861         -        raw_printf(stderr, "Usage: .backup ?DB? ?--append? FILENAME\n");
         5866  +        raw_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
  5862   5867           return 1;
  5863   5868         }
  5864   5869       }
  5865   5870       if( zDestFile==0 ){
  5866   5871         raw_printf(stderr, "missing FILENAME argument on .backup\n");
  5867   5872         return 1;
  5868   5873       }
................................................................................
  5870   5875       rc = sqlite3_open_v2(zDestFile, &pDest, 
  5871   5876                     SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
  5872   5877       if( rc!=SQLITE_OK ){
  5873   5878         utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
  5874   5879         close_db(pDest);
  5875   5880         return 1;
  5876   5881       }
         5882  +    if( bAsync ){
         5883  +      sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
         5884  +                   0, 0, 0);
         5885  +    }
  5877   5886       open_db(p, 0);
  5878   5887       pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
  5879   5888       if( pBackup==0 ){
  5880   5889         utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
  5881   5890         close_db(pDest);
  5882   5891         return 1;
  5883   5892       }

Changes to src/sqliteInt.h.

  3981   3981   #define LOCATE_VIEW    0x01
  3982   3982   #define LOCATE_NOERR   0x02
  3983   3983   Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*);
  3984   3984   Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
  3985   3985   Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
  3986   3986   void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
  3987   3987   void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
  3988         -void sqlite3Vacuum(Parse*,Token*);
  3989         -int sqlite3RunVacuum(char**, sqlite3*, int);
         3988  +void sqlite3Vacuum(Parse*,Token*,Expr*);
         3989  +int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*);
  3990   3990   char *sqlite3NameFromToken(sqlite3*, Token*);
  3991   3991   int sqlite3ExprCompare(Parse*,Expr*, Expr*, int);
  3992   3992   int sqlite3ExprCompareSkip(Expr*, Expr*, int);
  3993   3993   int sqlite3ExprListCompare(ExprList*, ExprList*, int);
  3994   3994   int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int);
  3995   3995   int sqlite3ExprImpliesNonNullRow(Expr*,int);
  3996   3996   void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);

Changes to src/vacuum.c.

    98     98   ** the copy of step (3) were replaced by deleting the original database
    99     99   ** and renaming the transient database as the original.  But that will
   100    100   ** not work if other processes are attached to the original database.
   101    101   ** And a power loss in between deleting the original and renaming the
   102    102   ** transient would cause the database file to appear to be deleted
   103    103   ** following reboot.
   104    104   */
   105         -void sqlite3Vacuum(Parse *pParse, Token *pNm){
          105  +void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
   106    106     Vdbe *v = sqlite3GetVdbe(pParse);
   107    107     int iDb = 0;
   108         -  if( v==0 ) return;
          108  +  if( v==0 ) goto build_vacuum_end;
   109    109     if( pNm ){
   110    110   #ifndef SQLITE_BUG_COMPATIBLE_20160819
   111    111       /* Default behavior:  Report an error if the argument to VACUUM is
   112    112       ** not recognized */
   113    113       iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm);
   114         -    if( iDb<0 ) return;
          114  +    if( iDb<0 ) goto build_vacuum_end;
   115    115   #else
   116    116       /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments
   117    117       ** to VACUUM are silently ignored.  This is a back-out of a bug fix that
   118    118       ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270).
   119    119       ** The buggy behavior is required for binary compatibility with some
   120    120       ** legacy applications. */
   121    121       iDb = sqlite3FindDb(pParse->db, pNm);
   122    122       if( iDb<0 ) iDb = 0;
   123    123   #endif
   124    124     }
   125    125     if( iDb!=1 ){
   126         -    sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
          126  +    int iIntoReg = 0;
          127  +    if( pInto ){
          128  +      iIntoReg = ++pParse->nMem;
          129  +      sqlite3ExprCode(pParse, pInto, iIntoReg);
          130  +    }
          131  +    sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg);
   127    132       sqlite3VdbeUsesBtree(v, iDb);
   128    133     }
          134  +build_vacuum_end:
          135  +  sqlite3ExprDelete(pParse->db, pInto);
   129    136     return;
   130    137   }
   131    138   
   132    139   /*
   133    140   ** This routine implements the OP_Vacuum opcode of the VDBE.
   134    141   */
   135         -int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
          142  +int sqlite3RunVacuum(
          143  +  char **pzErrMsg,        /* Write error message here */
          144  +  sqlite3 *db,            /* Database connection */
          145  +  int iDb,                /* Which attached DB to vacuum */
          146  +  sqlite3_value *pOut     /* Write results here, if not NULL */
          147  +){
   136    148     int rc = SQLITE_OK;     /* Return code from service routines */
   137    149     Btree *pMain;           /* The database being vacuumed */
   138    150     Btree *pTemp;           /* The temporary database we vacuum into */
   139    151     u32 saved_mDbFlags;     /* Saved value of db->mDbFlags */
   140    152     u64 saved_flags;        /* Saved value of db->flags */
   141    153     int saved_nChange;      /* Saved value of db->nChange */
   142    154     int saved_nTotalChange; /* Saved value of db->nTotalChange */
   143    155     u8 saved_mTrace;        /* Saved trace settings */
   144    156     Db *pDb = 0;            /* Database to detach at end of vacuum */
   145    157     int isMemDb;            /* True if vacuuming a :memory: database */
   146    158     int nRes;               /* Bytes of reserved space at the end of each page */
   147    159     int nDb;                /* Number of attached databases */
   148    160     const char *zDbMain;    /* Schema name of database to vacuum */
          161  +  const char *zOut;       /* Name of output file */
   149    162   
   150    163     if( !db->autoCommit ){
   151    164       sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
   152    165       return SQLITE_ERROR;
   153    166     }
   154    167     if( db->nVdbeActive>1 ){
   155    168       sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
   156    169       return SQLITE_ERROR;
   157    170     }
          171  +  if( pOut ){
          172  +    if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){
          173  +      sqlite3SetString(pzErrMsg, db, "non-text filename");
          174  +      return SQLITE_ERROR;
          175  +    }
          176  +    zOut = (const char*)sqlite3_value_text(pOut);
          177  +  }else{
          178  +    zOut = "";
          179  +  }
   158    180   
   159    181     /* Save the current value of the database flags so that it can be 
   160    182     ** restored before returning. Then set the writable-schema flag, and
   161    183     ** disable CHECK and foreign key constraints.  */
   162    184     saved_flags = db->flags;
   163    185     saved_mDbFlags = db->mDbFlags;
   164    186     saved_nChange = db->nChange;
................................................................................
   185    207     ** that actually made the VACUUM run slower.  Very little journalling
   186    208     ** actually occurs when doing a vacuum since the vacuum_db is initially
   187    209     ** empty.  Only the journal header is written.  Apparently it takes more
   188    210     ** time to parse and run the PRAGMA to turn journalling off than it does
   189    211     ** to write the journal header file.
   190    212     */
   191    213     nDb = db->nDb;
   192         -  rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
          214  +  rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut);
   193    215     if( rc!=SQLITE_OK ) goto end_of_vacuum;
   194    216     assert( (db->nDb-1)==nDb );
   195    217     pDb = &db->aDb[nDb];
   196    218     assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
   197    219     pTemp = pDb->pBt;
          220  +  if( pOut ){
          221  +    sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp));
          222  +    i64 sz = 0;
          223  +    if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){
          224  +      rc = SQLITE_ERROR;
          225  +      sqlite3SetString(pzErrMsg, db, "output file already exists");
          226  +      goto end_of_vacuum;
          227  +    }
          228  +  }
   198    229     nRes = sqlite3BtreeGetOptimalReserve(pMain);
   199    230   
   200    231     /* A VACUUM cannot change the pagesize of an encrypted database. */
   201    232   #ifdef SQLITE_HAS_CODEC
   202    233     if( db->nextPagesize ){
   203    234       extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
   204    235       int nKey;
................................................................................
   214    245   
   215    246     /* Begin a transaction and take an exclusive lock on the main database
   216    247     ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
   217    248     ** to ensure that we do not try to change the page-size on a WAL database.
   218    249     */
   219    250     rc = execSql(db, pzErrMsg, "BEGIN");
   220    251     if( rc!=SQLITE_OK ) goto end_of_vacuum;
   221         -  rc = sqlite3BtreeBeginTrans(pMain, 2, 0);
          252  +  rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0);
   222    253     if( rc!=SQLITE_OK ) goto end_of_vacuum;
   223    254   
   224    255     /* Do not attempt to change the page size for a WAL database */
   225    256     if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
   226    257                                                  ==PAGER_JOURNALMODE_WAL ){
   227    258       db->nextPagesize = 0;
   228    259     }
................................................................................
   309    340          BTREE_DEFAULT_CACHE_SIZE, 0,  /* Preserve the default page cache size */
   310    341          BTREE_TEXT_ENCODING,      0,  /* Preserve the text encoding */
   311    342          BTREE_USER_VERSION,       0,  /* Preserve the user version */
   312    343          BTREE_APPLICATION_ID,     0,  /* Preserve the application id */
   313    344       };
   314    345   
   315    346       assert( 1==sqlite3BtreeIsInTrans(pTemp) );
   316         -    assert( 1==sqlite3BtreeIsInTrans(pMain) );
          347  +    assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) );
   317    348   
   318    349       /* Copy Btree meta values */
   319    350       for(i=0; i<ArraySize(aCopy); i+=2){
   320    351         /* GetMeta() and UpdateMeta() cannot fail in this context because
   321    352         ** we already have page 1 loaded into cache and marked dirty. */
   322    353         sqlite3BtreeGetMeta(pMain, aCopy[i], &meta);
   323    354         rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]);
   324    355         if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
   325    356       }
   326    357   
   327         -    rc = sqlite3BtreeCopyFile(pMain, pTemp);
          358  +    if( pOut==0 ){
          359  +      rc = sqlite3BtreeCopyFile(pMain, pTemp);
          360  +    }
   328    361       if( rc!=SQLITE_OK ) goto end_of_vacuum;
   329    362       rc = sqlite3BtreeCommit(pTemp);
   330    363       if( rc!=SQLITE_OK ) goto end_of_vacuum;
   331    364   #ifndef SQLITE_OMIT_AUTOVACUUM
   332         -    sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
          365  +    if( pOut==0 ){
          366  +      sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
          367  +    }
   333    368   #endif
   334    369     }
   335    370   
   336    371     assert( rc==SQLITE_OK );
   337         -  rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
          372  +  if( pOut==0 ){
          373  +    rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
          374  +  }
   338    375   
   339    376   end_of_vacuum:
   340    377     /* Restore the original value of db->flags */
   341    378     db->init.iDb = 0;
   342    379     db->mDbFlags = saved_mDbFlags;
   343    380     db->flags = saved_flags;
   344    381     db->nChange = saved_nChange;

Changes to src/vdbe.c.

  6680   6680     sqlite3VdbeChangeEncoding(pOut, encoding);
  6681   6681     if( rc ) goto abort_due_to_error;
  6682   6682     break;
  6683   6683   };
  6684   6684   #endif /* SQLITE_OMIT_PRAGMA */
  6685   6685   
  6686   6686   #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
  6687         -/* Opcode: Vacuum P1 * * * *
         6687  +/* Opcode: Vacuum P1 P2 * * *
  6688   6688   **
  6689   6689   ** Vacuum the entire database P1.  P1 is 0 for "main", and 2 or more
  6690   6690   ** for an attached database.  The "temp" database may not be vacuumed.
         6691  +**
         6692  +** If P2 is not zero, then it is a register holding a string which is
         6693  +** the file into which the result of vacuum should be written.  When
         6694  +** P2 is zero, the vacuum overwrites the original database.
  6691   6695   */
  6692   6696   case OP_Vacuum: {
  6693   6697     assert( p->readOnly==0 );
  6694         -  rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
         6698  +  rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1,
         6699  +                        pOp->p2 ? &aMem[pOp->p2] : 0);
  6695   6700     if( rc ) goto abort_due_to_error;
  6696   6701     break;
  6697   6702   }
  6698   6703   #endif
  6699   6704   
  6700   6705   #if !defined(SQLITE_OMIT_AUTOVACUUM)
  6701   6706   /* Opcode: IncrVacuum P1 P2 * * *

Changes to test/shell1.test.

   252    252   } {0 {}}
   253    253   do_test shell1-3.1.3 {
   254    254     catchcmd "test.db" ".backup FOO BAR"
   255    255   } {1 {Error: unknown database FOO}}
   256    256   do_test shell1-3.1.4 {
   257    257     # too many arguments
   258    258     catchcmd "test.db" ".backup FOO BAR BAD"
   259         -} {1 {Usage: .backup ?DB? ?--append? FILENAME}}
          259  +} {1 {Usage: .backup ?DB? ?OPTIONS? FILENAME}}
   260    260   
   261    261   # .bail ON|OFF           Stop after hitting an error.  Default OFF
   262    262   do_test shell1-3.2.1 {
   263    263     catchcmd "test.db" ".bail"
   264    264   } {1 {Usage: .bail on|off}}
   265    265   do_test shell1-3.2.2 {
   266    266     catchcmd "test.db" ".bail ON"

Added test/vacuum-into.test.

            1  +# 2018-12-07
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the VACUUM INTO statement.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +
           18  +# If the VACUUM statement is disabled in the current build, skip all
           19  +# the tests in this file.
           20  +#
           21  +ifcapable {!vacuum} {
           22  +  omit_test vacuum.test {Compiled with SQLITE_OMIT_VACUUM}
           23  +  finish_test
           24  +  return
           25  +}
           26  +
           27  +forcedelete out.db
           28  +do_execsql_test vacuum-into-100 {
           29  +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
           30  +  WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
           31  +  INSERT INTO t1(a,b) SELECT x, randomblob(600) FROM c;
           32  +  CREATE INDEX t1b ON t1(b);
           33  +  DELETE FROM t1 WHERE a%2;
           34  +  SELECT count(*), sum(a), sum(length(b)) FROM t1;
           35  +} {50 2550 30000}
           36  +do_execsql_test vacuum-into-110 {
           37  +  VACUUM main INTO 'out.db';
           38  +} {}
           39  +sqlite3 db2 out.db
           40  +do_test vacuum-into-120 {
           41  +  db2 eval {SELECT count(*), sum(a), sum(length(b)) FROM t1}
           42  +} {50 2550 30000}
           43  +do_catchsql_test vacuum-into-130 {
           44  +  VACUUM INTO 'out.db';
           45  +} {1 {output file already exists}}
           46  +forcedelete out2.db
           47  +do_catchsql_test vacuum-into-140 {
           48  +  VACUUM INTO 'out2.db';
           49  +} {0 {}}
           50  +do_catchsql_test vacuum-into-150 {
           51  +  VACUUM INTO 'out2.db';
           52  +} {1 {output file already exists}}
           53  +
           54  +do_catchsql_test vacuum-into-200 {
           55  +  VACUUM main INTO ':memory:';
           56  +} {0 {}}
           57  +
           58  +# The INTO argument can be an arbitrary expression.
           59  +#
           60  +do_execsql_test vacuum-into-300 {
           61  +  CREATE TABLE t2(name TEXT);
           62  +  INSERT INTO t2 VALUES(':memory:');
           63  +  VACUUM main INTO (SELECT name FROM t2);
           64  +} {}
           65  +do_catchsql_test vacuum-into-310 {
           66  +  VACUUM INTO null;
           67  +} {1 {non-text filename}}
           68  +
           69  +finish_test