Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -20,11 +20,11 @@ ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.362 2005/12/29 01:11:37 drh Exp $ +** $Id: build.c,v 1.363 2005/12/29 19:23:07 drh Exp $ */ #include "sqliteInt.h" #include /* @@ -762,11 +762,11 @@ ** set them now. */ sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); /* file_format */ lbl = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp(v, OP_If, 0, lbl); - sqlite3VdbeAddOp(v, OP_Integer, 1, 0); /* file format defaults to 1 */ + sqlite3VdbeAddOp(v, OP_Integer, SQLITE_DEFAULT_FILE_FORMAT, 0); sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1); sqlite3VdbeAddOp(v, OP_Integer, db->enc, 0); sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 4); sqlite3VdbeResolveLabel(v, lbl); @@ -2110,11 +2110,10 @@ int nName; /* Number of characters in zName */ int i, j; Token nullId; /* Fake token for an empty ID list */ DbFixer sFix; /* For assigning database names to pTable */ int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */ - int descSeen = 0; /* Changes to true if a DESC is seen */ sqlite3 *db = pParse->db; Db *pDb; /* The specific table containing the indexed database */ int iDb; /* Index of the database that is being written */ Token *pName = 0; /* Unqualified name of the index to create */ struct ExprList_item *pListItem; /* For looping over pList */ @@ -2262,11 +2261,11 @@ pIndex->autoIndex = pName==0; pIndex->iDb = iDb; /* Check to see if we should honor DESC requests on index columns */ - if( pDb->file_format>=4 || (!pDb->descIndex && !db->init.busy) ){ + if( pDb->file_format>=4 ){ sortOrderMask = -1; /* Honor DESC */ }else{ sortOrderMask = 0; /* Ignore DESC */ } @@ -2297,15 +2296,12 @@ if( !db->init.busy && sqlite3CheckCollSeq(pParse, pIndex->keyInfo.aColl[i]) ){ goto exit_create_index; } - requestedSortOrder = pListItem->sortOrder; - pDb->descIndex |= requestedSortOrder; - requestedSortOrder &= sortOrderMask; + requestedSortOrder = pListItem->sortOrder & sortOrderMask; pIndex->keyInfo.aSortOrder[i] = requestedSortOrder; - descSeen |= requestedSortOrder; } pIndex->keyInfo.nField = pList->nExpr; sqlite3DefaultRowEst(pIndex); if( pTab==pParse->pNewTable ){ @@ -2402,15 +2398,10 @@ */ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3VdbeAddOp(v, OP_CreateIndex, iDb, 0); sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0); - /* Make sure the file_format is at least 4 if we have DESC indices. */ - if( descSeen ){ - sqlite3MinimumFileFormat(pParse, iDb, 4); - } - /* Gather the complete text of the CREATE INDEX statement into ** the zStmt variable */ if( pStart && pEnd ){ /* A named index with an explicit CREATE INDEX statement */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -11,11 +11,11 @@ ************************************************************************* ** This file contains the implementation of the sqlite3_prepare() ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.10 2005/12/16 01:06:17 drh Exp $ +** $Id: prepare.c,v 1.11 2005/12/29 19:23:07 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include @@ -256,17 +256,17 @@ /* ** file_format==1 Version 3.0.0. ** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN ** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults - ** file_format==4 Version 3.3.0. // DESC indices + ** file_format==4 Version 3.3.0. // DESC indices. Boolean constants */ pDb->file_format = meta[1]; if( pDb->file_format==0 ){ pDb->file_format = 1; } - if( pDb->file_format>4 ){ + if( pDb->file_format>SQLITE_MAX_FILE_FORMAT ){ sqlite3BtreeCloseCursor(curMain); sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0); return SQLITE_ERROR; } Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.443 2005/12/29 01:11:37 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.444 2005/12/29 19:23:07 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ /* @@ -138,10 +138,21 @@ /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #define SQLITE_MAX_VARIABLE_NUMBER 999 +/* +** The "file format" number is an integer that is incremented whenever +** the VDBE-level file format changes. The following macros define the +** the default file format for new databases and the maximum file format +** that the library can read. +*/ +#define SQLITE_MAX_FILE_FORMAT 4 +#ifndef SQLITE_DEFAULT_FILE_FORMAT +# define SQLITE_DEFAULT_FILE_FORMAT 4 +#endif + /* ** Provide a default value for TEMP_STORE in case it is not specified ** on the command-line */ #ifndef TEMP_STORE @@ -358,11 +369,10 @@ Hash aFKey; /* Foreign keys indexed by to-table */ u16 flags; /* Flags associated with this database */ u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ u8 safety_level; /* How aggressive at synching data to disk */ u8 file_format; /* Schema format version for this file */ - u8 descIndex; /* True if any index uses the DESC attribute */ int cache_size; /* Number of pages to use in the cache */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ void *pAux; /* Auxiliary data. Usually NULL */ void (*xFreeAux)(void*); /* Routine to free pAux */ }; Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.508 2005/12/21 18:36:46 drh Exp $ +** $Id: vdbe.c,v 1.509 2005/12/29 19:23:07 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include "vdbeInt.h" @@ -2105,11 +2105,11 @@ ** ** If P3 is NULL then all index fields have the affinity NONE. ** ** See also OP_MakeIdxRec */ -/* Opcode: MakeRecordI P1 P2 P3 +/* Opcode: MakeIdxRec P1 P2 P3 ** ** This opcode works just OP_MakeRecord except that it reads an extra ** integer from the stack (thus reading a total of abs(P1+1) entries) ** and appends that extra integer to the end of the record as a varint. ** This results in an index key. @@ -2147,10 +2147,11 @@ int leaveOnStack; /* If true, leave the entries on the stack */ int nField; /* Number of fields in the record */ int jumpIfNull; /* Jump here if non-zero and any entries are NULL. */ int addRowid; /* True to append a rowid column at the end */ char *zAffinity; /* The affinity string for the record */ + int file_format; /* File format to use for encoding */ leaveOnStack = ((pOp->p1<0)?1:0); nField = pOp->p1 * (leaveOnStack?-1:1); jumpIfNull = pOp->p2; addRowid = pOp->opcode==OP_MakeIdxRec; @@ -2157,10 +2158,11 @@ zAffinity = pOp->p3; pData0 = &pTos[1-nField]; assert( pData0>=p->aStack ); containsNull = 0; + file_format = p->minWriteFileFormat; /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(pRec=pData0; pRec<=pTos; pRec++){ @@ -2168,11 +2170,11 @@ applyAffinity(pRec, zAffinity[pRec-pData0], db->enc); } if( pRec->flags&MEM_Null ){ containsNull = 1; } - serial_type = sqlite3VdbeSerialType(pRec); + serial_type = sqlite3VdbeSerialType(pRec, file_format); nData += sqlite3VdbeSerialTypeLen(serial_type); nHdr += sqlite3VarintLen(serial_type); } /* If we have to append a varint rowid to this record, set 'rowid' @@ -2181,11 +2183,11 @@ */ if( addRowid ){ pRowid = &pTos[0-nField]; assert( pRowid>=p->aStack ); sqlite3VdbeMemIntegerify(pRowid); - serial_type = sqlite3VdbeSerialType(pRowid); + serial_type = sqlite3VdbeSerialType(pRowid, 0); nData += sqlite3VdbeSerialTypeLen(serial_type); nHdr += sqlite3VarintLen(serial_type); } /* Add the initial header varint and total the size */ @@ -2207,21 +2209,21 @@ /* Write the record */ zCsr = zNewRecord; zCsr += sqlite3PutVarint(zCsr, nHdr); for(pRec=pData0; pRec<=pTos; pRec++){ - serial_type = sqlite3VdbeSerialType(pRec); + serial_type = sqlite3VdbeSerialType(pRec, file_format); zCsr += sqlite3PutVarint(zCsr, serial_type); /* serial type */ } if( addRowid ){ - zCsr += sqlite3PutVarint(zCsr, sqlite3VdbeSerialType(pRowid)); + zCsr += sqlite3PutVarint(zCsr, sqlite3VdbeSerialType(pRowid, 0)); } for(pRec=pData0; pRec<=pTos; pRec++){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRec); /* serial data */ + zCsr += sqlite3VdbeSerialPut(zCsr, pRec, file_format); /* serial data */ } if( addRowid ){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRowid); + zCsr += sqlite3VdbeSerialPut(zCsr, pRowid, 0); } assert( zCsr==(zNewRecord+nByte) ); /* Pop entries off the stack if required. Push the new record on. */ if( !leaveOnStack ){ @@ -2513,20 +2515,29 @@ int p2 = pOp->p2; int wrFlag; Btree *pX; int iDb; Cursor *pCur; + Db *pDb; assert( pTos>=p->aStack ); sqlite3VdbeMemIntegerify(pTos); iDb = pTos->i; assert( (pTos->flags & MEM_Dyn)==0 ); pTos--; assert( iDb>=0 && iDbnDb ); - pX = db->aDb[iDb].pBt; + pDb = &db->aDb[iDb]; + pX = pDb->pBt; assert( pX!=0 ); - wrFlag = pOp->opcode==OP_OpenWrite; + if( pOp->opcode==OP_OpenWrite ){ + wrFlag = 1; + if( pDb->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = pDb->file_format; + } + }else{ + wrFlag = 0; + } if( p2<=0 ){ assert( pTos>=p->aStack ); sqlite3VdbeMemIntegerify(pTos); p2 = pTos->i; assert( (pTos->flags & MEM_Dyn)==0 ); Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -305,10 +305,11 @@ u8 resOnStack; /* True if there are result values on the stack */ u8 explain; /* True if EXPLAIN present on SQL command */ u8 changeCntOn; /* True to update the change-counter */ u8 aborted; /* True if ROLLBACK in another VM causes an abort */ u8 expired; /* True if the VM needs to be recompiled */ + u8 minWriteFileFormat; /* Minimum file format for writable database files */ int nChange; /* Number of db changes made since last reset */ i64 startTime; /* Time when query started - used for profiling */ }; /* @@ -330,12 +331,12 @@ #endif #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); #endif int sqlite3VdbeSerialTypeLen(u32); -u32 sqlite3VdbeSerialType(Mem*); -int sqlite3VdbeSerialPut(unsigned char*, Mem*); +u32 sqlite3VdbeSerialType(Mem*, int); +int sqlite3VdbeSerialPut(unsigned char*, Mem*, int); int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); void sqlite3VdbeDeleteAuxData(VdbeFunc*, int); int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(Cursor*, int , const unsigned char*, int*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -780,10 +780,11 @@ p->errorAction = OE_Abort; p->popStack = 0; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; + p->minWriteFileFormat = 255; #ifdef VDBE_PROFILE { int i; for(i=0; inOp; i++){ p->aOp[i].cnt = 0; @@ -1488,30 +1489,36 @@ ** 3 3 signed integer ** 4 4 signed integer ** 5 6 signed integer ** 6 8 signed integer ** 7 8 IEEE float -** 8-11 reserved for expansion +** 8 0 Integer constant 0 +** 9 0 Integer constant 1 +** 10,11 reserved for expansion ** N>=12 and even (N-12)/2 BLOB ** N>=13 and odd (N-13)/2 text ** */ /* ** Return the serial-type for the value stored in pMem. */ -u32 sqlite3VdbeSerialType(Mem *pMem){ +u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ int flags = pMem->flags; if( flags&MEM_Null ){ return 0; } if( flags&MEM_Int ){ /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ # define MAX_6BYTE ((((i64)0x00001000)<<32)-1) i64 i = pMem->i; - u64 u = i<0 ? -i : i; + u64 u; + if( file_format>=4 && (i&1)==i ){ + return 8+i; + } + u = i<0 ? -i : i; if( u<=127 ) return 1; if( u<=32767 ) return 2; if( u<=8388607 ) return 3; if( u<=2147483647 ) return 4; if( u<=MAX_6BYTE ) return 5; @@ -1546,21 +1553,16 @@ /* ** Write the serialized data blob for the value stored in pMem into ** buf. It is assumed that the caller has allocated sufficient space. ** Return the number of bytes written. */ -int sqlite3VdbeSerialPut(unsigned char *buf, Mem *pMem){ - u32 serial_type = sqlite3VdbeSerialType(pMem); +int sqlite3VdbeSerialPut(unsigned char *buf, Mem *pMem, int file_format){ + u32 serial_type = sqlite3VdbeSerialType(pMem, file_format); int len; - /* NULL */ - if( serial_type==0 ){ - return 0; - } - /* Integer and Real */ - if( serial_type<=7 ){ + if( serial_type<=7 && serial_type>0 ){ u64 v; int i; if( serial_type==7 ){ v = *(u64*)&pMem->r; }else{ @@ -1571,16 +1573,20 @@ buf[i] = (v&0xFF); v >>= 8; } return len; } - + /* String or blob */ - assert( serial_type>=12 ); - len = sqlite3VdbeSerialTypeLen(serial_type); - memcpy(buf, pMem->z, len); - return len; + if( serial_type>=12 ){ + len = sqlite3VdbeSerialTypeLen(serial_type); + memcpy(buf, pMem->z, len); + return len; + } + + /* NULL or constants 0 or 1 */ + return 0; } /* ** Deserialize the data blob pointed to by buf as serial type serial_type ** and store the result in pMem. Return the number of bytes read. @@ -1589,12 +1595,10 @@ const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ ){ switch( serial_type ){ - case 8: /* Reserved for future use */ - case 9: /* Reserved for future use */ case 10: /* Reserved for future use */ case 11: /* Reserved for future use */ case 0: { /* NULL */ pMem->flags = MEM_Null; break; @@ -1648,10 +1652,16 @@ }else{ pMem->r = *(double*)&x; pMem->flags = MEM_Real; } return 8; + } + case 8: /* Integer 0 */ + case 9: { /* Integer 1 */ + pMem->i = serial_type-8; + pMem->flags = MEM_Int; + return 0; } default: { int len = (serial_type-12)/2; pMem->z = (char *)buf; pMem->n = len; Index: test/descidx1.test ================================================================== --- test/descidx1.test +++ test/descidx1.test @@ -9,11 +9,11 @@ # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is descending indices. # -# $Id: descidx1.test,v 1.2 2005/12/21 18:36:46 drh Exp $ +# $Id: descidx1.test,v 1.3 2005/12/29 19:23:07 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -38,20 +38,19 @@ set meta [btree_get_meta $bt] btree_close $bt lindex $meta 2 } -# Verify that the file format jumps to (at least) 4 as soon as a -# descending index is created. +# Verify that the file format starts as 4. # do_test descidx1-1.1 { execsql { CREATE TABLE t1(a,b); CREATE INDEX i1 ON t1(b ASC); } get_file_format -} {1} +} {4} do_test descidx1-1.2 { execsql { CREATE INDEX i2 ON t1(a DESC); } get_file_format Index: test/descidx2.test ================================================================== --- test/descidx2.test +++ test/descidx2.test @@ -9,11 +9,11 @@ # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is descending indices. # -# $Id: descidx2.test,v 1.1 2005/12/21 18:36:46 drh Exp $ +# $Id: descidx2.test,v 1.2 2005/12/29 19:23:07 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -38,20 +38,19 @@ set meta [btree_get_meta $bt] btree_close $bt lindex $meta 2 } -# Verify that the file format jumps to (at least) 4 as soon as a -# descending index is created. +# Verify that the file format starts as 4 # do_test descidx2-1.1 { execsql { CREATE TABLE t1(a,b); CREATE INDEX i1 ON t1(b ASC); } get_file_format -} {1} +} {4} do_test descidx2-1.2 { execsql { CREATE INDEX i2 ON t1(a DESC); } get_file_format Index: test/types.test ================================================================== --- test/types.test +++ test/types.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. Specfically # it tests that the different storage classes (integer, real, text etc.) # all work correctly. # -# $Id: types.test,v 1.16 2005/11/14 22:29:06 drh Exp $ +# $Id: types.test,v 1.17 2005/12/29 19:23:07 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests in this file are organized roughly as follows: @@ -206,11 +206,11 @@ # Check that all the record sizes are as we expected. do_test types-2.1.9 { set root [db eval {select rootpage from sqlite_master where name = 't1'}] record_sizes $root -} {3 3 3 4 4 6 6 10 10} +} {2 3 3 4 4 6 6 10 10} # Insert some reals. These should be 10 byte records. do_test types-2.2.1 { execsql { CREATE TABLE t2(a float); @@ -227,11 +227,11 @@ # Check that all the record sizes are as we expected. do_test types-2.2.3 { set root [db eval {select rootpage from sqlite_master where name = 't2'}] record_sizes $root -} {3 10 10} +} {2 10 10} # Insert a NULL. This should be a two byte record. do_test types-2.3.1 { execsql { CREATE TABLE t3(a nullvalue);