/ Check-in [4feb4b9a]
Login

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

Overview
Comment:Fix vacuum so that it works with blobs. (CVS 1490)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:4feb4b9a71ce7a92924d2358a7ccecb4cca19223
User & Date: danielk1977 2004-05-29 10:23:19
Context
2004-05-29
10:43
Ensure vacuum handles table names with spaces in them. (CVS 1491) check-in: 0a6689be user: danielk1977 tags: trunk
10:23
Fix vacuum so that it works with blobs. (CVS 1490) check-in: 4feb4b9a user: danielk1977 tags: trunk
02:44
Avoid arithmetic on void pointers. (CVS 1489) check-in: 3d68703e user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.147 2004/05/26 00:01:54 drh Exp $
           12  +** $Id: btree.c,v 1.148 2004/05/29 10:23:19 danielk1977 Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** For a detailed discussion of BTrees, refer to
    16     16   **
    17     17   **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
    18     18   **     "Sorting And Searching", pages 473-480. Addison-Wesley
    19     19   **     Publishing Company, Reading, Massachusetts.
................................................................................
  4146   4146   }
  4147   4147   
  4148   4148   /*
  4149   4149   ** Copy the complete content of pBtFrom into pBtTo.  A transaction
  4150   4150   ** must be active for both files.
  4151   4151   **
  4152   4152   ** The size of file pBtFrom may be reduced by this operation.
  4153         -** If anything goes wrong, the transaction on pBtFrom is rolled back.
         4153  +** If anything goes wrong, the transaction on pBtTo is rolled back.
  4154   4154   */
  4155   4155   int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
  4156   4156     int rc = SQLITE_OK;
  4157   4157     Pgno i, nPage, nToPage;
  4158   4158   
  4159   4159     if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
  4160   4160     if( pBtTo->pCursor ) return SQLITE_BUSY;

Changes to src/build.c.

    19     19   **     DROP INDEX
    20     20   **     creating ID lists
    21     21   **     BEGIN TRANSACTION
    22     22   **     COMMIT
    23     23   **     ROLLBACK
    24     24   **     PRAGMA
    25     25   **
    26         -** $Id: build.c,v 1.198 2004/05/29 02:37:19 danielk1977 Exp $
           26  +** $Id: build.c,v 1.199 2004/05/29 10:23:19 danielk1977 Exp $
    27     27   */
    28     28   #include "sqliteInt.h"
    29     29   #include <ctype.h>
    30     30   
    31     31   /*
    32     32   ** This routine is called when a new SQL statement is beginning to
    33     33   ** be parsed.  Check to see if the schema for the database needs
................................................................................
   562    562             "the temporary database file");
   563    563           return;
   564    564         }
   565    565       }
   566    566     }
   567    567   
   568    568     /* Make sure the new table name does not collide with an existing
   569         -  ** index or table name.  Issue an error message if it does.
   570         -  **
   571         -  ** If we are re-reading the sqlite_master table because of a schema
   572         -  ** change and a new permanent table is found whose name collides with
   573         -  ** an existing temporary table, that is not an error.
          569  +  ** index or table name in the same database.  Issue an error message if
          570  +  ** it does.
   574    571     */
   575         -  pTable = sqlite3FindTable(db, zName, 0);
   576         -  if( pTable!=0 && (pTable->iDb==iDb || !db->init.busy) ){
          572  +  pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
          573  +  if( pTable ){
   577    574       sqlite3ErrorMsg(pParse, "table %T already exists", pName);
   578    575       sqliteFree(zName);
   579    576       return;
   580    577     }
   581    578     if( (pIdx = sqlite3FindIndex(db, zName, 0))!=0 &&
   582    579             (pIdx->iDb==0 || !db->init.busy) ){
   583    580       sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
................................................................................
  1687   1684     ** own name.
  1688   1685     */
  1689   1686     if( pName && !db->init.busy ){
  1690   1687       Index *pISameName;    /* Another index with the same name */
  1691   1688       Table *pTSameName;    /* A table with same name as the index */
  1692   1689       zName = sqliteStrNDup(pName->z, pName->n);
  1693   1690       if( zName==0 ) goto exit_create_index;
  1694         -    if( (pISameName = sqlite3FindIndex(db, zName, 0))!=0 ){
         1691  +    if( (pISameName = sqlite3FindIndex(db, zName, db->aDb[iDb].zName))!=0 ){
  1695   1692         sqlite3ErrorMsg(pParse, "index %s already exists", zName);
  1696   1693         goto exit_create_index;
  1697   1694       }
  1698   1695       if( (pTSameName = sqlite3FindTable(db, zName, 0))!=0 ){
  1699   1696         sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
  1700   1697         goto exit_create_index;
  1701   1698       }

Changes to src/main.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** Main file for the SQLite library.  The routines in this file
    13     13   ** implement the programmer interface to the library.  Routines in
    14     14   ** other files are for internal use by SQLite and should not be
    15     15   ** accessed by users of the library.
    16     16   **
    17         -** $Id: main.c,v 1.195 2004/05/27 23:56:16 danielk1977 Exp $
           17  +** $Id: main.c,v 1.196 2004/05/29 10:23:19 danielk1977 Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include "os.h"
    21     21   #include <ctype.h>
    22     22   
    23     23   /*
    24     24   ** A pointer to this structure is used to communicate information
................................................................................
   243    243         sqlite3BtreeCloseCursor(curMain);
   244    244         return rc;
   245    245       }
   246    246     }else{
   247    247       memset(meta, 0, sizeof(meta));
   248    248     }
   249    249     db->aDb[iDb].schema_cookie = meta[0];
   250         -  if( iDb==0 ){
   251         -    db->next_cookie = meta[0];
   252         -    db->file_format = meta[1];
   253         -    if( meta[4] ){
   254         -      /* If meta[4] is still zero, then we are opening a previously empty
   255         -      ** file. Leave db->enc to the default value set by the sqlite3_open()
   256         -      ** call in this case.
   257         -      */
          250  +
          251  +  /* If opening a non-empty database, check the text encoding. For the
          252  +  ** main database, set sqlite3.enc to the encoding of the main database.
          253  +  ** For an attached db, it is an error if the encoding is not the same
          254  +  ** as sqlite3.enc.
          255  +  */
          256  +  if( meta[4] ){  /* text encoding */
          257  +    if( iDb==0 ){
          258  +      /* If opening the main database, set db->enc. */
   258    259         db->enc = (u8)meta[4];
          260  +    }else{
          261  +      /* If opening an attached database, the encoding much match db->enc */
          262  +      if( meta[4]!=db->enc ){
          263  +        sqlite3BtreeCloseCursor(curMain);
          264  +        sqlite3SetString(pzErrMsg, "attached databases must use the same"
          265  +            " text encoding as main database", (char*)0);
          266  +        return SQLITE_ERROR;
          267  +      }
   259    268       }
          269  +  }
          270  +
          271  +  if( iDb==0 ){
   260    272       size = meta[2];
   261    273       if( size==0 ){ size = MAX_PAGES; }
   262    274       db->cache_size = size;
   263    275       db->safety_level = meta[3];
   264    276       if( meta[5]>0 && meta[5]<=2 && db->temp_store==0 ){
   265    277         db->temp_store = meta[5];
   266    278       }
   267    279       if( db->safety_level==0 ) db->safety_level = 2;
   268    280   
   269         -    /*
   270         -    **  file_format==1    Version 3.0.0.
   271         -    */
          281  +    /* FIX ME: Every struct Db will need a next_cookie */
          282  +    db->next_cookie = meta[0];
          283  +    db->file_format = meta[1];
   272    284       if( db->file_format==0 ){
   273    285         /* This happens if the database was initially empty */
   274    286         db->file_format = 1;
   275         -    }else if( db->file_format>1 ){
   276         -      sqlite3BtreeCloseCursor(curMain);
   277         -      sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
   278         -      return SQLITE_ERROR;
   279    287       }
   280         -  }else if( db->file_format!=meta[1] ){
   281         -    if( meta[1]==0 ){
   282         -      sqlite3SetString(pzErrMsg, "cannot attach empty database: ",
   283         -         db->aDb[iDb].zName, (char*)0);
   284         -    }else{
   285         -      sqlite3SetString(pzErrMsg, "incompatible file format in auxiliary "
   286         -         "database: ", db->aDb[iDb].zName, (char*)0);
   287         -    }
   288         -    sqlite3BtreeClose(db->aDb[iDb].pBt);
   289         -    db->aDb[iDb].pBt = 0;
   290         -    return SQLITE_FORMAT;
   291    288     }
          289  +
          290  +  /*
          291  +  **  file_format==1    Version 3.0.0.
          292  +  */
          293  +  if( meta[1]>1 ){
          294  +    sqlite3BtreeCloseCursor(curMain);
          295  +    sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
          296  +    return SQLITE_ERROR;
          297  +  }
          298  +
   292    299     sqlite3BtreeSetCacheSize(db->aDb[iDb].pBt, db->cache_size);
   293    300     sqlite3BtreeSetSafetyLevel(db->aDb[iDb].pBt, meta[3]==0 ? 2 : meta[3]);
   294    301   
   295    302     /* Read the schema information out of the schema tables
   296    303     */
   297    304     assert( db->init.busy );
   298         -  sqlite3SafetyOff(db);
   299    305     if( rc==SQLITE_EMPTY ){
   300    306       /* For an empty database, there is nothing to read */
   301    307       rc = SQLITE_OK;
   302    308     }else{
          309  +    sqlite3SafetyOff(db);
   303    310       if( iDb==0 ){
   304    311         /* This SQL statement tries to read the temp.* schema from the
   305    312         ** sqlite_temp_master table. It might return SQLITE_EMPTY. 
   306    313         */
   307    314         rc = sqlite3_exec(db, init_script1, sqlite3InitCallback, &initData, 0);
   308    315         if( rc==SQLITE_OK || rc==SQLITE_EMPTY ){
   309    316           rc = sqlite3_exec(db, init_script2, sqlite3InitCallback, &initData, 0);

Changes to src/parse.y.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains SQLite's grammar for SQL.  Process this file
    13     13   ** using the lemon parser generator to generate C code that runs
    14     14   ** the parser.  Lemon will also generate a header file containing
    15     15   ** numeric codes for all of the tokens.
    16     16   **
    17         -** @(#) $Id: parse.y,v 1.123 2004/05/29 02:37:19 danielk1977 Exp $
           17  +** @(#) $Id: parse.y,v 1.124 2004/05/29 10:23:20 danielk1977 Exp $
    18     18   */
    19     19   %token_prefix TK_
    20     20   %token_type {Token}
    21     21   %default_type {Token}
    22     22   %extra_argument {Parse *pParse}
    23     23   %syntax_error {
    24     24     if( pParse->zErrMsg==0 ){
................................................................................
   778    778   number(A) ::= INTEGER(X).  {A = X;}
   779    779   number(A) ::= FLOAT(X).    {A = X;}
   780    780   plus_opt ::= PLUS.
   781    781   plus_opt ::= .
   782    782   
   783    783   //////////////////////////// The CREATE TRIGGER command /////////////////////
   784    784   
   785         -cmd ::= CREATE(A) trigger_decl BEGIN trigger_cmd_list(S) END(Z). {
          785  +cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
   786    786     Token all;
   787    787     all.z = A.z;
   788    788     all.n = (Z.z - A.z) + Z.n;
   789    789     sqlite3FinishTrigger(pParse, S, &all);
   790    790   }
   791    791   
   792         -trigger_decl ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D)
          792  +trigger_decl(A) ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D)
   793    793                    ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G). {
   794    794     SrcList *pTab = sqlite3SrcListAppend(0, &E, &DB);
   795    795     sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, pTab, F, G, T);
          796  +  A = (Z.n==0?B:Z);
   796    797   }
   797    798   
   798    799   %type trigger_time  {int}
   799    800   trigger_time(A) ::= BEFORE.      { A = TK_BEFORE; }
   800    801   trigger_time(A) ::= AFTER.       { A = TK_AFTER;  }
   801    802   trigger_time(A) ::= INSTEAD OF.  { A = TK_INSTEAD;}
   802    803   trigger_time(A) ::= .            { A = TK_BEFORE; }

Changes to src/trigger.c.

   209    209     if( !db->init.busy ){
   210    210       static VdbeOpList insertTrig[] = {
   211    211         { OP_NewRecno,   0, 0,  0          },
   212    212         { OP_String,     0, 0,  "trigger"  },
   213    213         { OP_String,     0, 0,  0          },  /* 2: trigger name */
   214    214         { OP_String,     0, 0,  0          },  /* 3: table name */
   215    215         { OP_Integer,    0, 0,  0          },
   216         -      { OP_String,     0, 0,  0          },  /* 5: SQL */
          216  +      { OP_String,     0, 0,  "CREATE TRIGGER "},
          217  +      { OP_String,     0, 0,  0          },  /* 6: SQL */
          218  +      { OP_Concat,     2, 0,  0          }, 
   217    219         { OP_MakeRecord, 5, 0,  "tttit"    },
   218    220         { OP_PutIntKey,  0, 0,  0          },
   219    221       };
   220    222       int addr;
   221    223       Vdbe *v;
   222    224   
   223    225       /* Make an entry in the sqlite_master table */
................................................................................
   224    226       v = sqlite3GetVdbe(pParse);
   225    227       if( v==0 ) goto triggerfinish_cleanup;
   226    228       sqlite3BeginWriteOperation(pParse, 0, nt->iDb);
   227    229       sqlite3OpenMasterTable(v, nt->iDb);
   228    230       addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
   229    231       sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); 
   230    232       sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); 
   231         -    sqlite3VdbeChangeP3(v, addr+5, pAll->z, pAll->n);
          233  +    sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n);
   232    234       if( nt->iDb==0 ){
   233    235         sqlite3ChangeCookie(db, v, 0);
   234    236       }
   235    237       sqlite3VdbeAddOp(v, OP_Close, 0, 0);
   236    238       sqlite3EndWriteOperation(pParse);
   237    239     }
   238    240   

Changes to src/vacuum.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains code used to implement the VACUUM command.
    13     13   **
    14     14   ** Most of the code in this file may be omitted by defining the
    15     15   ** SQLITE_OMIT_VACUUM macro.
    16     16   **
    17         -** $Id: vacuum.c,v 1.16 2004/05/22 09:21:21 danielk1977 Exp $
           17  +** $Id: vacuum.c,v 1.17 2004/05/29 10:23:20 danielk1977 Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include "os.h"
    21     21   
    22         -/*
    23         -** A structure for holding a dynamic string - a string that can grow
    24         -** without bound. 
    25         -*/
    26         -typedef struct dynStr dynStr;
    27         -struct dynStr {
    28         -  char *z;        /* Text of the string in space obtained from sqliteMalloc() */
    29         -  int nAlloc;     /* Amount of space allocated to z[] */
    30         -  int nUsed;      /* Next unused slot in z[] */
    31         -};
    32         -
    33         -/*
    34         -** A structure that holds the vacuum context
    35         -*/
    36         -typedef struct vacuumStruct vacuumStruct;
    37         -struct vacuumStruct {
    38         -  sqlite *dbOld;       /* Original database */
    39         -  sqlite *dbNew;       /* New database */
    40         -  char **pzErrMsg;     /* Write errors here */
    41         -  int rc;              /* Set to non-zero on an error */
    42         -  const char *zTable;  /* Name of a table being copied */
    43         -  const char *zPragma; /* Pragma to execute with results */
    44         -  dynStr s1, s2;       /* Two dynamic strings */
    45         -};
    46         -
    47     22   #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
    48         -/*
    49         -** Append text to a dynamic string
    50         -*/
    51         -static void appendText(dynStr *p, const char *zText, int nText){
    52         -  if( nText<0 ) nText = strlen(zText);
    53         -  if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
    54         -    char *zNew;
    55         -    p->nAlloc = p->nUsed + nText + 1000;
    56         -    zNew = sqliteRealloc(p->z, p->nAlloc);
    57         -    if( zNew==0 ){
    58         -      sqliteFree(p->z);
    59         -      memset(p, 0, sizeof(*p));
    60         -      return;
    61         -    }
    62         -    p->z = zNew;
    63         -  }
    64         -  memcpy(&p->z[p->nUsed], zText, nText+1);
    65         -  p->nUsed += nText;
    66         -}
    67         -
    68         -/*
    69         -** Append text to a dynamic string, having first put the text in quotes.
    70         -*/
    71         -static void appendQuoted(dynStr *p, const char *zText){
    72         -  int i, j;
    73         -  appendText(p, "'", 1);
    74         -  for(i=j=0; zText[i]; i++){
    75         -    if( zText[i]=='\'' ){
    76         -      appendText(p, &zText[j], i-j+1);
    77         -      j = i + 1;
    78         -      appendText(p, "'", 1);
    79         -    }
    80         -  }
    81         -  if( j<i ){
    82         -    appendText(p, &zText[j], i-j);
    83         -  }
    84         -  appendText(p, "'", 1);
    85         -}
    86         -
    87         -/*
    88         -** Execute statements of SQL.  If an error occurs, write the error
    89         -** message into *pzErrMsg and return non-zero.
    90         -*/
    91         -static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){ 
    92         -  char *zErrMsg = 0;
    93         -  int rc;
    94         -
    95         -  /* printf("***** executing *****\n%s\n", zSql); */
    96         -  rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
    97         -  if( zErrMsg ){
    98         -    sqlite3SetString(pzErrMsg, zErrMsg, (char*)0);
    99         -    sqlite3_freemem(zErrMsg);
   100         -  }
   101         -  return rc;
   102         -}
   103         -
   104         -/*
   105         -** This is the second stage callback.  Each invocation contains all the
   106         -** data for a single row of a single table in the original database.  This
   107         -** routine must write that information into the new database.
   108         -*/
   109         -static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
   110         -  vacuumStruct *p = (vacuumStruct*)pArg;
   111         -  const char *zSep = "(";
   112         -  int i;
   113         -
   114         -  if( argv==0 ) return 0;
   115         -  p->s2.nUsed = 0;
   116         -  appendText(&p->s2, "INSERT INTO ", -1);
   117         -  appendQuoted(&p->s2, p->zTable);
   118         -  appendText(&p->s2, " VALUES", -1);
   119         -  for(i=0; i<argc; i++){
   120         -    appendText(&p->s2, zSep, 1);
   121         -    zSep = ",";
   122         -    if( argv[i]==0 ){
   123         -      appendText(&p->s2, "NULL", 4);
   124         -    }else{
   125         -      appendQuoted(&p->s2, argv[i]);
   126         -    }
   127         -  }
   128         -  appendText(&p->s2,")", 1);
   129         -  p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
   130         -  return p->rc;
   131         -}
   132         -
   133         -/*
   134         -** This is the first stage callback.  Each invocation contains three
   135         -** arguments where are taken from the SQLITE_MASTER table of the original
   136         -** database:  (1) the entry type, (2) the entry name, and (3) the SQL for
   137         -** the entry.  In all cases, execute the SQL of the third argument.
   138         -** For tables, run a query to select all entries in that table and 
   139         -** transfer them to the second-stage callback.
   140         -*/
   141         -static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
   142         -  vacuumStruct *p = (vacuumStruct*)pArg;
   143         -  int rc = 0;
   144         -  assert( argc==3 );
   145         -  if( argv==0 ) return 0;
   146         -  assert( argv[0]!=0 );
   147         -  assert( argv[1]!=0 );
   148         -  assert( argv[2]!=0 );
   149         -  rc = execsql(p->pzErrMsg, p->dbNew, argv[2]);
   150         -  if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
   151         -    char *zErrMsg = 0;
   152         -    p->s1.nUsed = 0;
   153         -    appendText(&p->s1, "SELECT * FROM ", -1);
   154         -    appendQuoted(&p->s1, argv[1]);
   155         -    p->zTable = argv[1];
   156         -    rc = sqlite3_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
   157         -    if( zErrMsg ){
   158         -      sqlite3SetString(p->pzErrMsg, zErrMsg, (char*)0);
   159         -      sqlite3_freemem(zErrMsg);
   160         -    }
   161         -  }
   162         -  if( rc!=SQLITE_ABORT ) p->rc = rc;
   163         -  return rc;
   164         -}
   165         -
   166         -/*
   167         -** This callback is used to transfer PRAGMA settings from one database
   168         -** to the other.  The value in argv[0] should be passed to a pragma
   169         -** identified by ((vacuumStruct*)pArg)->zPragma.
   170         -*/
   171         -static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
   172         -  vacuumStruct *p = (vacuumStruct*)pArg;
   173         -  char zBuf[200];
   174         -  assert( argc==1 );
   175         -  if( argv==0 ) return 0;
   176         -  assert( argv[0]!=0 );
   177         -  assert( strlen(p->zPragma)<100 );
   178         -  assert( strlen(argv[0])<30 );
   179         -  sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
   180         -  p->rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
   181         -  return p->rc;
   182         -}
   183         -
   184     23   /*
   185     24   ** Generate a random name of 20 character in length.
   186     25   */
   187     26   static void randomName(unsigned char *zBuf){
   188     27     static const unsigned char zChars[] =
   189     28       "abcdefghijklmnopqrstuvwxyz"
   190     29       "0123456789";
   191     30     int i;
   192     31     sqlite3Randomness(20, zBuf);
   193     32     for(i=0; i<20; i++){
   194     33       zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
   195     34     }
   196     35   }
           36  +
           37  +/*
           38  +** Execute zSql on database db. Return an error code.
           39  +*/
           40  +static int execSql(sqlite3 *db, const char *zSql){
           41  +  sqlite3_stmt *pStmt;
           42  +  if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
           43  +    return sqlite3_errcode(db);
           44  +  }
           45  +  while( SQLITE_ROW==sqlite3_step(pStmt) );
           46  +  return sqlite3_finalize(pStmt);
           47  +}
           48  +
           49  +/*
           50  +** Execute zSql on database db. The statement returns exactly
           51  +** one column. Execute this as SQL on the same database.
           52  +*/
           53  +static int execExecSql(sqlite3 *db, const char *zSql){
           54  +  sqlite3_stmt *pStmt;
           55  +  int rc;
           56  +
           57  +  rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
           58  +  if( rc!=SQLITE_OK ) return rc;
           59  +
           60  +  while( SQLITE_ROW==sqlite3_step(pStmt) ){
           61  +    rc = execSql(db, sqlite3_column_text(pStmt, 0));
           62  +    if( rc!=SQLITE_OK ){
           63  +      sqlite3_finalize(pStmt);
           64  +      return rc;
           65  +    }
           66  +  }
           67  +
           68  +  return sqlite3_finalize(pStmt);
           69  +}
           70  +
   197     71   #endif
   198     72   
   199     73   /*
   200     74   ** The non-standard VACUUM command is used to clean up the database,
   201     75   ** collapse free space, etc.  It is modelled after the VACUUM command
   202     76   ** in PostgreSQL.
   203     77   **
................................................................................
   212     86     return;
   213     87   }
   214     88   
   215     89   /*
   216     90   ** This routine implements the OP_Vacuum opcode of the VDBE.
   217     91   */
   218     92   int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){
           93  +  int rc = SQLITE_OK;     /* Return code from service routines */
   219     94   #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
   220     95     const char *zFilename;  /* full pathname of the database file */
   221     96     int nFilename;          /* number of characters  in zFilename[] */
   222     97     char *zTemp = 0;        /* a temporary file in same directory as zFilename */
   223         -  sqlite *dbNew = 0;      /* The new vacuumed database */
   224         -  int rc = SQLITE_OK;     /* Return code from service routines */
   225     98     int i;                  /* Loop counter */
   226         -  char *zErrMsg;          /* Error message */
   227         -  vacuumStruct sVac;      /* Information passed to callbacks */
   228     99   
   229         -  /* These are all of the pragmas that need to be transferred over
   230         -  ** to the new database */
   231         -  static const char *zPragma[] = {
   232         -     "default_synchronous",
   233         -     "default_cache_size",
   234         -     /* "default_temp_store", */
   235         -  };
          100  +  char *zSql = 0;
          101  +  sqlite3_stmt *pStmt = 0;
   236    102   
   237    103     if( db->flags & SQLITE_InTrans ){
   238    104       sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", 
   239    105          (char*)0);
   240         -    return SQLITE_ERROR;
          106  +    rc = SQLITE_ERROR;
          107  +    goto end_of_vacuum;
   241    108     }
   242         -  memset(&sVac, 0, sizeof(sVac));
   243    109   
   244         -  /* Get the full pathname of the database file and create two
   245         -  ** temporary filenames in the same directory as the original file.
          110  +  /* Get the full pathname of the database file and create a
          111  +  ** temporary filename in the same directory as the original file.
   246    112     */
   247    113     zFilename = sqlite3BtreeGetFilename(db->aDb[0].pBt);
   248    114     if( zFilename==0 ){
   249         -    /* This only happens with the in-memory database.  VACUUM is a no-op
   250         -    ** there, so just return */
   251         -    return SQLITE_OK;
          115  +    /* The in-memory database. Do nothing. */
          116  +    goto end_of_vacuum;
   252    117     }
   253    118     nFilename = strlen(zFilename);
   254    119     zTemp = sqliteMalloc( nFilename+100 );
   255         -  if( zTemp==0 ) return SQLITE_NOMEM;
          120  +  if( zTemp==0 ){
          121  +    rc = SQLITE_NOMEM;
          122  +    goto end_of_vacuum;
          123  +  }
   256    124     strcpy(zTemp, zFilename);
   257    125     for(i=0; i<10; i++){
   258    126       zTemp[nFilename] = '-';
   259    127       randomName((unsigned char*)&zTemp[nFilename+1]);
   260    128       if( !sqlite3OsFileExists(zTemp) ) break;
   261    129     }
   262         -  if( i>=10 ){
   263         -    sqlite3SetString(pzErrMsg, "unable to create a temporary database file "
   264         -       "in the same directory as the original database", (char*)0);
   265         -    goto end_of_vacuum;
   266         -  }
   267    130   
   268         -  
   269         -  if( SQLITE_OK!=sqlite3_open(zTemp, &dbNew, 0) ){
   270         -    sqlite3SetString(pzErrMsg, "unable to open a temporary database at ",
   271         -       zTemp, " - ", sqlite3_errmsg(dbNew), (char*)0);
          131  +  /* Attach the temporary database as 'vacuum' */
          132  +  zSql = sqlite3MPrintf("ATTACH '%s' AS vacuum_db;", zTemp);
          133  +  if( !zSql ){
          134  +    rc = SQLITE_NOMEM;
   272    135       goto end_of_vacuum;
   273    136     }
   274         -  if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
   275         -  if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
   276         -    goto end_of_vacuum;
   277         -  }
   278         -  
   279         -  sVac.dbOld = db;
   280         -  sVac.dbNew = dbNew;
   281         -  sVac.pzErrMsg = pzErrMsg;
   282         -  for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
   283         -    char zBuf[200];
   284         -    assert( strlen(zPragma[i])<100 );
   285         -    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
   286         -    sVac.zPragma = zPragma[i];
   287         -    rc = sqlite3_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
   288         -  }
   289         -  if( rc==SQLITE_OK ){
   290         -    rc = sqlite3_exec(db, 
   291         -      "SELECT type, name, sql FROM sqlite_master "
   292         -      "WHERE sql NOT NULL AND type!='view' "
   293         -      "UNION ALL "
   294         -      "SELECT type, name, sql FROM sqlite_master "
   295         -      "WHERE sql NOT NULL AND type=='view'",
   296         -      vacuumCallback1, &sVac, &zErrMsg);
   297         -  }
   298         -  if( rc==SQLITE_OK ){
   299         -    rc = sqlite3BtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
   300         -    sqlite3_exec(db, "COMMIT", 0, 0, 0);
   301         -    sqlite3ResetInternalSchema(db, 0);
          137  +  rc = execSql(db, zSql);
          138  +  sqliteFree(zSql);
          139  +  zSql = 0;
          140  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          141  +
          142  +  /* Begin a transaction */
          143  +  rc = execSql(db, "BEGIN;");
          144  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          145  +
          146  +  /* Query the schema of the main database. Create a mirror schema
          147  +  ** in the temporary database.
          148  +  */
          149  +  rc = execExecSql(db, 
          150  +      "SELECT 'CREATE ' || type || ' vacuum_db.' || "
          151  +      "substr(sql, length(type)+9, 1000000) "
          152  +      "FROM sqlite_master "
          153  +      "WHERE type != 'trigger' AND sql IS NOT NULL "
          154  +      "ORDER BY (type != 'table');" 
          155  +  );
          156  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          157  +
          158  +  /* Loop through the tables in the main database. For each, do
          159  +  ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy
          160  +  ** the contents to the temporary database.
          161  +  */
          162  +  rc = execExecSql(db, 
          163  +      "SELECT 'INSERT INTO vacuum_db.' || name "
          164  +      "|| ' SELECT * FROM ' || name || ';'"
          165  +      "FROM sqlite_master "
          166  +      "WHERE type = 'table';"
          167  +  );
          168  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          169  +
          170  +  /* Copy the triggers from the main database to the temporary database.
          171  +  ** This was deferred before in case the triggers interfered with copying
          172  +  ** the data. It's possible the indices should be deferred until this
          173  +  ** point also.
          174  +  */
          175  +  rc = execExecSql(db, 
          176  +      "SELECT 'CREATE ' || type || ' vacuum_db.' || "
          177  +      "substr(sql, length(type)+9, 1000000) "
          178  +      "FROM sqlite_master "
          179  +      "WHERE type = 'trigger' AND sql IS NOT NULL;"
          180  +  );
          181  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          182  +
          183  +
          184  +  /* At this point, unless the main db was completely empty, there is now a
          185  +  ** transaction open on the vacuum database, but not on the main database.
          186  +  ** Open a btree level transaction on the main database. This allows a
          187  +  ** call to sqlite3BtreeCopyFile(). The main database btree level
          188  +  ** transaction is then committed, so the SQL level never knows it was
          189  +  ** opened for writing. This way, the SQL transaction used to create the
          190  +  ** temporary database never needs to be committed.
          191  +  */
          192  +
          193  +  /* FIX ME: The above will be the case shortly. But for now, a transaction
          194  +  ** will have been started on the main database file by the 'BEGIN'.
          195  +  */
          196  +/*
          197  +  rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt);
          198  +  if( rc!=SQLITE_OK ) goto end_of_vacuum;
          199  +*/
          200  +
          201  +  if( db->aDb[db->nDb-1].inTrans ){
          202  +    Btree *pTemp = db->aDb[db->nDb-1].pBt;
          203  +    Btree *pMain = db->aDb[0].pBt;
          204  +    u32 meta;
          205  +
          206  +    /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta 
          207  +    ** values 2 and 3, the default values of a couple of pragmas.
          208  +    */
          209  +    rc = sqlite3BtreeGetMeta(pMain, 3, &meta);
          210  +    if( rc!=SQLITE_OK ) goto end_of_vacuum;
          211  +    rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta);
          212  +    if( rc!=SQLITE_OK ) goto end_of_vacuum;
          213  +    rc = sqlite3BtreeGetMeta(pMain, 4, &meta);
          214  +    if( rc!=SQLITE_OK ) goto end_of_vacuum;
          215  +    rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta);
          216  +    if( rc!=SQLITE_OK ) goto end_of_vacuum;
          217  +
          218  +    rc = sqlite3BtreeCopyFile(pMain, pTemp);
          219  +
          220  +    /* FIX ME: Remove the main btree from the transaction so that it is not
          221  +    ** rolled back. This won't be required once the new 'auto-commit'
          222  +    ** model is in place.
          223  +    */
          224  +    rc = sqlite3BtreeCommit(pMain);
          225  +    db->aDb[0].inTrans = 0;
   302    226     }
   303    227   
   304    228   end_of_vacuum:
   305         -  if( rc && zErrMsg!=0 ){
   306         -    sqlite3SetString(pzErrMsg, "unable to vacuum database - ", 
   307         -       zErrMsg, (char*)0);
          229  +  execSql(db, "DETACH vacuum_db;");
          230  +  execSql(db, "ROLLBACK;");
          231  +  if( zTemp ){
          232  +    sqlite3OsDelete(zTemp);
          233  +    sqliteFree(zTemp);
   308    234     }
   309         -  sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
   310         -  if( dbNew ) sqlite3_close(dbNew);
   311         -  sqlite3OsDelete(zTemp);
   312         -  sqliteFree(zTemp);
   313         -  sqliteFree(sVac.s1.z);
   314         -  sqliteFree(sVac.s2.z);
   315         -  if( dbNew ) sqlite3_close(dbNew);
   316         -  if( zErrMsg ) sqlite3_freemem(zErrMsg);
   317         -  if( rc==SQLITE_ABORT ) sVac.rc = SQLITE_ERROR;
   318         -  return sVac.rc;
          235  +  if( zSql ) sqliteFree( zSql );
          236  +  if( pStmt ) sqlite3_finalize( pStmt );
   319    237   #endif
   320         -}
   321         -
   322         -
   323         -
          238  +  return rc;
          239  +} 

Changes to test/attach.test.

     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is testing the ATTACH and DETACH commands
    13     13   # and related functionality.
    14     14   #
    15         -# $Id: attach.test,v 1.17 2004/05/29 02:37:20 danielk1977 Exp $
           15  +# $Id: attach.test,v 1.18 2004/05/29 10:23:20 danielk1977 Exp $
    16     16   #
    17     17   
    18     18   set testdir [file dirname $argv0]
    19     19   source $testdir/tester.tcl
    20     20   
    21     21   for {set i 2} {$i<=15} {incr i} {
    22     22     file delete -force test$i.db
................................................................................
    68     68       SELECT * FROM two.t2;
    69     69     }
    70     70   } {1 {no such table: two.t2}}
    71     71   do_test attach-1.8 {
    72     72     catchsql {
    73     73       ATTACH DATABASE 'test3.db' AS three;
    74     74     }
    75         -} {1 {cannot attach empty database: three}}
           75  +} {0 {}}
    76     76   do_test attach-1.9 {
    77     77     catchsql {
    78     78       SELECT * FROM three.sqlite_master;
    79     79     }
    80         -} {1 {no such table: three.sqlite_master}}
           80  +} {0 {}}
    81     81   do_test attach-1.10 {
    82     82     catchsql {
    83     83       DETACH DATABASE three;
    84     84     }
    85         -} {1 {no such database: three}}
           85  +} {0 {}}
    86     86   do_test attach-1.11 {
    87     87     execsql {
    88     88       ATTACH 'test.db' AS db2;
    89     89       ATTACH 'test.db' AS db3;
    90     90       ATTACH 'test.db' AS db4;
    91     91       ATTACH 'test.db' AS db5;
    92     92       ATTACH 'test.db' AS db6;
................................................................................
   559    559   # Check to make sure we get a sensible error if unable to open
   560    560   # the file that we are trying to attach.
   561    561   #
   562    562   do_test attach-6.1 {
   563    563     catchsql {
   564    564       ATTACH DATABASE 'no-such-file' AS nosuch;
   565    565     }
   566         -} {1 {cannot attach empty database: nosuch}}
          566  +} {0 {}}
   567    567   file delete -force no-such-file
   568    568   if {$tcl_platform(platform)=="unix"} {
   569    569     do_test attach-6.2 {
   570    570       sqlite dbx cannot-read
   571    571       dbx eval {CREATE TABLE t1(a,b,c)}
   572    572       dbx close
   573    573       file attributes cannot-read -permission 0000

Changes to test/attach3.test.

     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is testing the ATTACH and DETACH commands
    13     13   # and schema changes to attached databases.
    14     14   #
    15         -# $Id: attach3.test,v 1.4 2004/05/29 02:37:20 danielk1977 Exp $
           15  +# $Id: attach3.test,v 1.5 2004/05/29 10:23:20 danielk1977 Exp $
    16     16   #
    17     17   
    18     18   
    19     19   set testdir [file dirname $argv0]
    20     20   source $testdir/tester.tcl
    21     21   
    22     22   # Create tables t1 and t2 in the main database
................................................................................
   168    168       SELECT * FROM t3;
   169    169     }
   170    170   } {10 20 20 40}
   171    171   do_test attach3-5.3 {
   172    172     execsql {
   173    173       SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
   174    174     }
   175         -} {trigger tr1 t3 0 {CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN
          175  +} {trigger tr1 t3 0 {CREATE TRIGGER tr1 AFTER INSERT ON t3 BEGIN
   176    176         INSERT INTO t3 VALUES(new.e*2, new.f*2);
   177    177       END}}
   178    178   
   179    179   # Drop the trigger 
   180    180   do_test attach3-8.1 {
   181    181     execsql {
   182    182       DROP TRIGGER aux.tr1;

Changes to test/vacuum.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this file is testing the VACUUM statement.
    13     13   #
    14         -# $Id: vacuum.test,v 1.18 2004/05/26 10:11:07 danielk1977 Exp $
           14  +# $Id: vacuum.test,v 1.19 2004/05/29 10:23:20 danielk1977 Exp $
    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   
    19     19   proc cksum {{db db}} {
    20         -  set txt [$db eval {SELECT name, type, sql FROM sqlite_master}]\n
    21         -  foreach tbl [$db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
           20  +  set sql "SELECT name, type, sql FROM sqlite_master ORDER BY name, type"
           21  +  set txt [$db eval $sql]\n
           22  +  set sql "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
           23  +  foreach tbl [$db eval $sql] {
    22     24       append txt [$db eval "SELECT * FROM $tbl"]\n
    23     25     }
    24     26     foreach prag {default_synchronous default_cache_size} {
    25     27       append txt $prag-[$db eval "PRAGMA $prag"]\n
    26     28     }
    27     29     set cksum [string length $txt]-[md5 $txt]
    28     30     # puts $cksum-[file size test.db]