Allow the INTO clause of VACUUM to be a text-valued expression.

FossilOrigin-Name: af172b53b46759f491f522356e14c5e2374d3f25ec70fbc1e100cadded8f9b22
This commit is contained in:
drh
2018-12-08 00:43:08 +00:00
parent 7464f578ed
commit 2f6239ed4e
7 changed files with 70 additions and 42 deletions
+6 -5
View File
@@ -1367,11 +1367,12 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
//
%ifndef SQLITE_OMIT_VACUUM
%ifndef SQLITE_OMIT_ATTACH
%type vinto {Token}
cmd ::= VACUUM vinto(Y). {sqlite3Vacuum(pParse,0,&Y);}
cmd ::= VACUUM nm(X) vinto(Y). {sqlite3Vacuum(pParse,&X,&Y);}
vinto(A) ::= INTO nm(X). {A = X;}
vinto(A) ::= . {A.z = 0;}
%type vinto {Expr*}
%destructor vinto {sqlite3ExprDelete(pParse->db, $$);}
cmd ::= VACUUM vinto(Y). {sqlite3Vacuum(pParse,0,Y);}
cmd ::= VACUUM nm(X) vinto(Y). {sqlite3Vacuum(pParse,&X,Y);}
vinto(A) ::= INTO expr(X). {A = X;}
vinto(A) ::= . {A = 0;}
%endif SQLITE_OMIT_ATTACH
%endif SQLITE_OMIT_VACUUM
+2 -2
View File
@@ -3985,8 +3985,8 @@ Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
void sqlite3Vacuum(Parse*,Token*,Token*);
int sqlite3RunVacuum(char**, sqlite3*, int, const char*);
void sqlite3Vacuum(Parse*,Token*,Expr*);
int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*);
char *sqlite3NameFromToken(sqlite3*, Token*);
int sqlite3ExprCompare(Parse*,Expr*, Expr*, int);
int sqlite3ExprCompareSkip(Expr*, Expr*, int);
+34 -19
View File
@@ -102,17 +102,16 @@ static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
** transient would cause the database file to appear to be deleted
** following reboot.
*/
void sqlite3Vacuum(Parse *pParse, Token *pNm, Token *pInto){
void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
Vdbe *v = sqlite3GetVdbe(pParse);
int iDb = 0;
assert( pInto!=0 );
if( v==0 ) return;
if( v==0 ) goto build_vacuum_end;
if( pNm ){
#ifndef SQLITE_BUG_COMPATIBLE_20160819
/* Default behavior: Report an error if the argument to VACUUM is
** not recognized */
iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm);
if( iDb<0 ) return;
if( iDb<0 ) goto build_vacuum_end;
#else
/* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments
** to VACUUM are silently ignored. This is a back-out of a bug fix that
@@ -124,20 +123,28 @@ void sqlite3Vacuum(Parse *pParse, Token *pNm, Token *pInto){
#endif
}
if( iDb!=1 ){
sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
sqlite3VdbeUsesBtree(v, iDb);
if( pInto->z ){
char *zName = sqlite3NameFromToken(pParse->db, pInto);
sqlite3VdbeChangeP4(v, -1, zName, P4_DYNAMIC);
int iIntoReg = 0;
if( pInto ){
iIntoReg = ++pParse->nMem;
sqlite3ExprCode(pParse, pInto, iIntoReg);
}
sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg);
sqlite3VdbeUsesBtree(v, iDb);
}
build_vacuum_end:
sqlite3ExprDelete(pParse->db, pInto);
return;
}
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
int sqlite3RunVacuum(
char **pzErrMsg, /* Write error message here */
sqlite3 *db, /* Database connection */
int iDb, /* Which attached DB to vacuum */
sqlite3_value *pOut /* Write results here, if not NULL */
){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
@@ -151,6 +158,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
int nRes; /* Bytes of reserved space at the end of each page */
int nDb; /* Number of attached databases */
const char *zDbMain; /* Schema name of database to vacuum */
const char *zOut; /* Name of output file */
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
@@ -160,6 +168,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
if( pOut ){
if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){
sqlite3SetString(pzErrMsg, db, "non-text filename");
return SQLITE_ERROR;
}
zOut = (const char*)sqlite3_value_text(pOut);
}else{
zOut = "";
}
/* Save the current value of the database flags so that it can be
** restored before returning. Then set the writable-schema flag, and
@@ -194,13 +211,13 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
** to write the journal header file.
*/
nDb = db->nDb;
rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut ? zOut : "");
rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
assert( (db->nDb-1)==nDb );
pDb = &db->aDb[nDb];
assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
pTemp = pDb->pBt;
if( zOut!=0 ){
if( pOut ){
sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp));
i64 sz = 0;
if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){
@@ -232,7 +249,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
*/
rc = execSql(db, pzErrMsg, "BEGIN");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeBeginTrans(pMain, zOut==0 ? 2 : 0, 0);
rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Do not attempt to change the page size for a WAL database */
@@ -327,7 +344,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
};
assert( 1==sqlite3BtreeIsInTrans(pTemp) );
assert( zOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) );
assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) );
/* Copy Btree meta values */
for(i=0; i<ArraySize(aCopy); i+=2){
@@ -338,23 +355,21 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb, const char *zOut){
if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
}
if( zOut==0 ){
if( pOut==0 ){
rc = sqlite3BtreeCopyFile(pMain, pTemp);
}else{
rc = sqlite3BtreeCommit(pMain);
}
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeCommit(pTemp);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( zOut==0 ){
if( pOut==0 ){
sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
}
#endif
}
assert( rc==SQLITE_OK );
if( zOut==0 ){
if( pOut==0 ){
rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
}
+6 -5
View File
@@ -6684,18 +6684,19 @@ case OP_JournalMode: { /* out2 */
#endif /* SQLITE_OMIT_PRAGMA */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/* Opcode: Vacuum P1 * * P4 *
/* Opcode: Vacuum P1 P2 * * *
**
** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
** for an attached database. The "temp" database may not be vacuumed.
**
** If P4 is not a NULL pointer, then it is a UTF8 string which is the
** name of a file into which to write the result of the vacuum. When
** P4 is NULL, the result of vacuum overwrites the original database.
** If P2 is not zero, then it is a register holding a string which is
** the file into which the result of vacuum should be written. When
** P2 is zero, the vacuum overwrites the original database.
*/
case OP_Vacuum: {
assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, pOp->p4.z);
rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1,
pOp->p2 ? &aMem[pOp->p2] : 0);
if( rc ) goto abort_due_to_error;
break;
}