mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Allow a session object to generate a changeset, even if columns were added to one of the tables using ALTER TABLE ADD COLUMN while the changeset was being collected.
FossilOrigin-Name: a3f435eccf3a2aa11cb7420e94af5efcdfa04e9c169c5aaf61fa5cdcb165ceef
This commit is contained in:
@ -135,8 +135,8 @@ do_test 2.2.2 {
|
|||||||
DROP TABLE t2;
|
DROP TABLE t2;
|
||||||
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
|
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
|
||||||
}
|
}
|
||||||
list [catch { S changeset } msg] $msg
|
catch { S changeset }
|
||||||
} {1 SQLITE_SCHEMA}
|
} {0}
|
||||||
do_test 2.2.3 {
|
do_test 2.2.3 {
|
||||||
S delete
|
S delete
|
||||||
sqlite3session S db main
|
sqlite3session S db main
|
||||||
@ -167,8 +167,8 @@ do_test 2.2.4 {
|
|||||||
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
|
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
|
||||||
INSERT INTO t2 VALUES(4, 5, 6, 7);
|
INSERT INTO t2 VALUES(4, 5, 6, 7);
|
||||||
}
|
}
|
||||||
list [catch { S changeset } msg] $msg
|
catch { S changeset }
|
||||||
} {1 SQLITE_SCHEMA}
|
} {0}
|
||||||
|
|
||||||
do_test 2.3 {
|
do_test 2.3 {
|
||||||
S delete
|
S delete
|
||||||
|
109
ext/session/sessionalter.test
Normal file
109
ext/session/sessionalter.test
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# 2023 October 02
|
||||||
|
#
|
||||||
|
# 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 that the sessions module interacts well with
|
||||||
|
# the ALTER TABLE ADD COLUMN command.
|
||||||
|
#
|
||||||
|
|
||||||
|
if {![info exists testdir]} {
|
||||||
|
set testdir [file join [file dirname [info script]] .. .. test]
|
||||||
|
}
|
||||||
|
source [file join [file dirname [info script]] session_common.tcl]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
|
||||||
|
ifcapable !session {finish_test; return}
|
||||||
|
set testprefix sessionalter
|
||||||
|
|
||||||
|
|
||||||
|
forcedelete test.db2
|
||||||
|
sqlite3 db2 test.db2
|
||||||
|
|
||||||
|
do_execsql_test 1.0 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test -db db2 1.1 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c DEFAULT 1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_then_apply_sql {
|
||||||
|
INSERT INTO t1 VALUES(1, 'one');
|
||||||
|
INSERT INTO t1 VALUES(2, 'two');
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test -db db2 1.2 {
|
||||||
|
SELECT * FROM t1
|
||||||
|
} {
|
||||||
|
1 one 1234
|
||||||
|
2 two 1234
|
||||||
|
}
|
||||||
|
|
||||||
|
do_then_apply_sql {
|
||||||
|
UPDATE t1 SET b='four' WHERE a=2;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test -db db2 1.3 {
|
||||||
|
SELECT * FROM t1
|
||||||
|
} {
|
||||||
|
1 one 1234
|
||||||
|
2 four 1234
|
||||||
|
}
|
||||||
|
|
||||||
|
do_then_apply_sql {
|
||||||
|
DELETE FROM t1 WHERE a=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test -db db2 1.4 {
|
||||||
|
SELECT * FROM t1
|
||||||
|
} {
|
||||||
|
2 four 1234
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
|
||||||
|
do_execsql_test 2.0 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test 2.1 {
|
||||||
|
sqlite3session S db main
|
||||||
|
S attach t1
|
||||||
|
set {} {}
|
||||||
|
} {}
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
INSERT INTO t1 VALUES(1, 2);
|
||||||
|
ALTER TABLE t1 ADD COLUMN c DEFAULT 'abcd';
|
||||||
|
INSERT INTO t1 VALUES(2, 3, 4);
|
||||||
|
}
|
||||||
|
do_changeset_test 2.3 S {
|
||||||
|
{INSERT t1 0 X.. {} {i 1 i 2 t abcd}}
|
||||||
|
{INSERT t1 0 X.. {} {i 2 i 3 i 4}}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_iterator_test 2.4 {} {
|
||||||
|
DELETE FROM t1 WHERE a=2;
|
||||||
|
ALTER TABLE t1 ADD COLUMN d DEFAULT 'abcd';
|
||||||
|
ALTER TABLE t1 ADD COLUMN e DEFAULT 5;
|
||||||
|
ALTER TABLE t1 ADD COLUMN f DEFAULT 7.2;
|
||||||
|
-- INSERT INTO t1 VALUES(9, 9, 9, 9);
|
||||||
|
} {
|
||||||
|
{DELETE t1 0 X..... {i 2 i 3 i 4 t abcd i 5 f 7.2} {}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
finish_test
|
@ -127,6 +127,7 @@ struct SessionTable {
|
|||||||
int bStat1; /* True if this is sqlite_stat1 */
|
int bStat1; /* True if this is sqlite_stat1 */
|
||||||
int bRowid; /* True if this table uses rowid for PK */
|
int bRowid; /* True if this table uses rowid for PK */
|
||||||
const char **azCol; /* Column names */
|
const char **azCol; /* Column names */
|
||||||
|
const char **azDflt; /* Default value expressions */
|
||||||
u8 *abPK; /* Array of primary key flags */
|
u8 *abPK; /* Array of primary key flags */
|
||||||
int nEntry; /* Total number of entries in hash table */
|
int nEntry; /* Total number of entries in hash table */
|
||||||
int nChange; /* Size of apChange[] array */
|
int nChange; /* Size of apChange[] array */
|
||||||
@ -299,6 +300,7 @@ struct SessionTable {
|
|||||||
struct SessionChange {
|
struct SessionChange {
|
||||||
u8 op; /* One of UPDATE, DELETE, INSERT */
|
u8 op; /* One of UPDATE, DELETE, INSERT */
|
||||||
u8 bIndirect; /* True if this change is "indirect" */
|
u8 bIndirect; /* True if this change is "indirect" */
|
||||||
|
u16 nRecordField; /* Number of fields in aRecord[] */
|
||||||
int nMaxSize; /* Max size of eventual changeset record */
|
int nMaxSize; /* Max size of eventual changeset record */
|
||||||
int nRecord; /* Number of bytes in buffer aRecord[] */
|
int nRecord; /* Number of bytes in buffer aRecord[] */
|
||||||
u8 *aRecord; /* Buffer containing old.* record */
|
u8 *aRecord; /* Buffer containing old.* record */
|
||||||
@ -1014,6 +1016,7 @@ static int sessionTableInfo(
|
|||||||
int *pnCol, /* OUT: number of columns */
|
int *pnCol, /* OUT: number of columns */
|
||||||
const char **pzTab, /* OUT: Copy of zThis */
|
const char **pzTab, /* OUT: Copy of zThis */
|
||||||
const char ***pazCol, /* OUT: Array of column names for table */
|
const char ***pazCol, /* OUT: Array of column names for table */
|
||||||
|
const char ***pazDflt, /* OUT: Array of default value expressions */
|
||||||
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
|
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
|
||||||
int *pbRowid /* OUT: True if only PK is a rowid */
|
int *pbRowid /* OUT: True if only PK is a rowid */
|
||||||
){
|
){
|
||||||
@ -1026,11 +1029,18 @@ static int sessionTableInfo(
|
|||||||
int i;
|
int i;
|
||||||
u8 *pAlloc = 0;
|
u8 *pAlloc = 0;
|
||||||
char **azCol = 0;
|
char **azCol = 0;
|
||||||
|
char **azDflt = 0;
|
||||||
u8 *abPK = 0;
|
u8 *abPK = 0;
|
||||||
int bRowid = 0; /* Set to true to use rowid as PK */
|
int bRowid = 0; /* Set to true to use rowid as PK */
|
||||||
|
|
||||||
assert( pazCol && pabPK );
|
assert( pazCol && pabPK );
|
||||||
|
|
||||||
|
*pazCol = 0;
|
||||||
|
*pabPK = 0;
|
||||||
|
*pnCol = 0;
|
||||||
|
if( pzTab ) *pzTab = 0;
|
||||||
|
if( pazDflt ) *pazDflt = 0;
|
||||||
|
|
||||||
nThis = sqlite3Strlen30(zThis);
|
nThis = sqlite3Strlen30(zThis);
|
||||||
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
|
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
|
||||||
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
|
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
|
||||||
@ -1044,39 +1054,28 @@ static int sessionTableInfo(
|
|||||||
}else if( rc==SQLITE_ERROR ){
|
}else if( rc==SQLITE_ERROR ){
|
||||||
zPragma = sqlite3_mprintf("");
|
zPragma = sqlite3_mprintf("");
|
||||||
}else{
|
}else{
|
||||||
*pazCol = 0;
|
|
||||||
*pabPK = 0;
|
|
||||||
*pnCol = 0;
|
|
||||||
if( pzTab ) *pzTab = 0;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
|
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
|
||||||
}
|
}
|
||||||
if( !zPragma ){
|
if( !zPragma ){
|
||||||
*pazCol = 0;
|
|
||||||
*pabPK = 0;
|
|
||||||
*pnCol = 0;
|
|
||||||
if( pzTab ) *pzTab = 0;
|
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
|
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
|
||||||
sqlite3_free(zPragma);
|
sqlite3_free(zPragma);
|
||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
*pazCol = 0;
|
|
||||||
*pabPK = 0;
|
|
||||||
*pnCol = 0;
|
|
||||||
if( pzTab ) *pzTab = 0;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
nByte = nThis + 1;
|
nByte = nThis + 1;
|
||||||
bRowid = (pbRowid!=0);
|
bRowid = (pbRowid!=0);
|
||||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||||
nByte += sqlite3_column_bytes(pStmt, 1);
|
nByte += sqlite3_column_bytes(pStmt, 1); /* name */
|
||||||
|
nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */
|
||||||
nDbCol++;
|
nDbCol++;
|
||||||
if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
|
if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */
|
||||||
}
|
}
|
||||||
if( nDbCol==0 ) bRowid = 0;
|
if( nDbCol==0 ) bRowid = 0;
|
||||||
nDbCol += bRowid;
|
nDbCol += bRowid;
|
||||||
@ -1084,7 +1083,7 @@ static int sessionTableInfo(
|
|||||||
rc = sqlite3_reset(pStmt);
|
rc = sqlite3_reset(pStmt);
|
||||||
|
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
|
nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1);
|
||||||
pAlloc = sessionMalloc64(pSession, nByte);
|
pAlloc = sessionMalloc64(pSession, nByte);
|
||||||
if( pAlloc==0 ){
|
if( pAlloc==0 ){
|
||||||
rc = SQLITE_NOMEM;
|
rc = SQLITE_NOMEM;
|
||||||
@ -1092,7 +1091,8 @@ static int sessionTableInfo(
|
|||||||
}
|
}
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
azCol = (char **)pAlloc;
|
azCol = (char **)pAlloc;
|
||||||
pAlloc = (u8 *)&azCol[nDbCol];
|
azDflt = (char**)&azCol[nDbCol];
|
||||||
|
pAlloc = (u8 *)&azDflt[nDbCol];
|
||||||
abPK = (u8 *)pAlloc;
|
abPK = (u8 *)pAlloc;
|
||||||
pAlloc = &abPK[nDbCol];
|
pAlloc = &abPK[nDbCol];
|
||||||
if( pzTab ){
|
if( pzTab ){
|
||||||
@ -1112,11 +1112,21 @@ static int sessionTableInfo(
|
|||||||
}
|
}
|
||||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||||
int nName = sqlite3_column_bytes(pStmt, 1);
|
int nName = sqlite3_column_bytes(pStmt, 1);
|
||||||
|
int nDflt = sqlite3_column_bytes(pStmt, 4);
|
||||||
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
|
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
|
||||||
|
const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
|
||||||
|
|
||||||
if( zName==0 ) break;
|
if( zName==0 ) break;
|
||||||
memcpy(pAlloc, zName, nName+1);
|
memcpy(pAlloc, zName, nName+1);
|
||||||
azCol[i] = (char *)pAlloc;
|
azCol[i] = (char *)pAlloc;
|
||||||
pAlloc += nName+1;
|
pAlloc += nName+1;
|
||||||
|
if( zDflt ){
|
||||||
|
memcpy(pAlloc, zDflt, nDflt+1);
|
||||||
|
azDflt[i] = (char *)pAlloc;
|
||||||
|
pAlloc += nDflt+1;
|
||||||
|
}else{
|
||||||
|
azDflt[i] = 0;
|
||||||
|
}
|
||||||
abPK[i] = sqlite3_column_int(pStmt, 5);
|
abPK[i] = sqlite3_column_int(pStmt, 5);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -1127,14 +1137,11 @@ static int sessionTableInfo(
|
|||||||
** free any allocation made. An error code will be returned in this case.
|
** free any allocation made. An error code will be returned in this case.
|
||||||
*/
|
*/
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
*pazCol = (const char **)azCol;
|
*pazCol = (const char**)azCol;
|
||||||
|
if( pazDflt ) *pazDflt = (const char**)azDflt;
|
||||||
*pabPK = abPK;
|
*pabPK = abPK;
|
||||||
*pnCol = nDbCol;
|
*pnCol = nDbCol;
|
||||||
}else{
|
}else{
|
||||||
*pazCol = 0;
|
|
||||||
*pabPK = 0;
|
|
||||||
*pnCol = 0;
|
|
||||||
if( pzTab ) *pzTab = 0;
|
|
||||||
sessionFree(pSession, azCol);
|
sessionFree(pSession, azCol);
|
||||||
}
|
}
|
||||||
if( pbRowid ) *pbRowid = bRowid;
|
if( pbRowid ) *pbRowid = bRowid;
|
||||||
@ -1159,7 +1166,7 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
|
|||||||
u8 *abPK;
|
u8 *abPK;
|
||||||
assert( pTab->azCol==0 || pTab->abPK==0 );
|
assert( pTab->azCol==0 || pTab->abPK==0 );
|
||||||
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
|
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
|
||||||
pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK,
|
pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK,
|
||||||
(pSession->bImplicitPK ? &pTab->bRowid : 0)
|
(pSession->bImplicitPK ? &pTab->bRowid : 0)
|
||||||
);
|
);
|
||||||
if( pSession->rc==SQLITE_OK ){
|
if( pSession->rc==SQLITE_OK ){
|
||||||
@ -1184,6 +1191,267 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
|
|||||||
return (pSession->rc || pTab->abPK==0);
|
return (pSession->rc || pTab->abPK==0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
|
||||||
|
int nCol = 0;
|
||||||
|
const char **azCol = 0;
|
||||||
|
const char **azDflt = 0;
|
||||||
|
u8 *abPK = 0;
|
||||||
|
int bRowid = 0;
|
||||||
|
|
||||||
|
assert( pSession->rc==SQLITE_OK );
|
||||||
|
|
||||||
|
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
|
||||||
|
pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK,
|
||||||
|
(pSession->bImplicitPK ? &bRowid : 0)
|
||||||
|
);
|
||||||
|
if( pSession->rc==SQLITE_OK ){
|
||||||
|
if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){
|
||||||
|
pSession->rc = SQLITE_SCHEMA;
|
||||||
|
}else{
|
||||||
|
int ii;
|
||||||
|
int nOldCol = pTab->nCol;
|
||||||
|
for(ii=0; ii<nCol; ii++){
|
||||||
|
if( ii<pTab->nCol ){
|
||||||
|
if( pTab->abPK[ii]!=abPK[ii] ){
|
||||||
|
pSession->rc = SQLITE_SCHEMA;
|
||||||
|
}
|
||||||
|
}else if( abPK[ii] ){
|
||||||
|
pSession->rc = SQLITE_SCHEMA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pSession->rc==SQLITE_OK ){
|
||||||
|
const char **a = pTab->azCol;
|
||||||
|
pTab->azCol = azCol;
|
||||||
|
pTab->nCol = nCol;
|
||||||
|
pTab->azDflt = azDflt;
|
||||||
|
pTab->abPK = abPK;
|
||||||
|
azCol = a;
|
||||||
|
}
|
||||||
|
if( pSession->bEnableSize ){
|
||||||
|
pSession->nMaxChangesetSize += (nCol - nOldCol);
|
||||||
|
pSession->nMaxChangesetSize += sessionVarintLen(nCol);
|
||||||
|
pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_free(azCol);
|
||||||
|
return pSession->rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sessionUpdateOneChange(
|
||||||
|
sqlite3_session *pSession,
|
||||||
|
int *pRc,
|
||||||
|
SessionChange **pp,
|
||||||
|
int nCol,
|
||||||
|
sqlite3_stmt *pDflt
|
||||||
|
){
|
||||||
|
SessionChange *pOld = *pp;
|
||||||
|
|
||||||
|
while( pOld->nRecordField<nCol ){
|
||||||
|
SessionChange *pNew = 0;
|
||||||
|
int nByte = 0;
|
||||||
|
int nIncr = 0;
|
||||||
|
int iField = pOld->nRecordField;
|
||||||
|
int eType = sqlite3_column_type(pDflt, iField);
|
||||||
|
switch( eType ){
|
||||||
|
case SQLITE_NULL:
|
||||||
|
nIncr = 1;
|
||||||
|
break;
|
||||||
|
case SQLITE_INTEGER:
|
||||||
|
case SQLITE_FLOAT:
|
||||||
|
nIncr = 9;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
int n = sqlite3_column_bytes(pDflt, iField);
|
||||||
|
nIncr = 1 + sessionVarintLen(n) + n;
|
||||||
|
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord);
|
||||||
|
pNew = sessionMalloc64(pSession, nByte);
|
||||||
|
if( pNew==0 ){
|
||||||
|
*pRc = SQLITE_NOMEM;
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
memcpy(pNew, pOld, sizeof(SessionChange));
|
||||||
|
pNew->aRecord = (u8*)&pNew[1];
|
||||||
|
memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord);
|
||||||
|
pNew->aRecord[pNew->nRecord++] = (u8)eType;
|
||||||
|
switch( eType ){
|
||||||
|
case SQLITE_INTEGER: {
|
||||||
|
i64 iVal = sqlite3_column_int64(pDflt, iField);
|
||||||
|
sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
|
||||||
|
pNew->nRecord += 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SQLITE_FLOAT: {
|
||||||
|
double rVal = sqlite3_column_double(pDflt, iField);
|
||||||
|
i64 iVal = 0;
|
||||||
|
memcpy(&iVal, &rVal, sizeof(rVal));
|
||||||
|
sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
|
||||||
|
pNew->nRecord += 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SQLITE_TEXT: {
|
||||||
|
int n = sqlite3_column_bytes(pDflt, iField);
|
||||||
|
const char *z = (const char*)sqlite3_column_text(pDflt, iField);
|
||||||
|
pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
|
||||||
|
memcpy(&pNew->aRecord[pNew->nRecord], z, n);
|
||||||
|
pNew->nRecord += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SQLITE_BLOB: {
|
||||||
|
int n = sqlite3_column_bytes(pDflt, iField);
|
||||||
|
const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField);
|
||||||
|
pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
|
||||||
|
memcpy(&pNew->aRecord[pNew->nRecord], z, n);
|
||||||
|
pNew->nRecord += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert( eType==SQLITE_NULL );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionFree(pSession, pOld);
|
||||||
|
*pp = pOld = pNew;
|
||||||
|
pNew->nRecordField++;
|
||||||
|
pNew->nMaxSize += nIncr;
|
||||||
|
if( pSession ){
|
||||||
|
pSession->nMaxChangesetSize += nIncr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Ensure that there is room in the buffer to append nByte bytes of data.
|
||||||
|
** If not, use sqlite3_realloc() to grow the buffer so that there is.
|
||||||
|
**
|
||||||
|
** If successful, return zero. Otherwise, if an OOM condition is encountered,
|
||||||
|
** set *pRc to SQLITE_NOMEM and return non-zero.
|
||||||
|
*/
|
||||||
|
static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
|
||||||
|
#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
|
||||||
|
i64 nReq = p->nBuf + nByte;
|
||||||
|
if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
|
||||||
|
u8 *aNew;
|
||||||
|
i64 nNew = p->nAlloc ? p->nAlloc : 128;
|
||||||
|
|
||||||
|
do {
|
||||||
|
nNew = nNew*2;
|
||||||
|
}while( nNew<nReq );
|
||||||
|
|
||||||
|
/* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
|
||||||
|
** of sqlite3_realloc64(). Allocations greater than this size in bytes
|
||||||
|
** always fail. It is used here to ensure that this routine can always
|
||||||
|
** allocate up to this limit - instead of up to the largest power of
|
||||||
|
** two smaller than the limit. */
|
||||||
|
if( nNew>SESSION_MAX_BUFFER_SZ ){
|
||||||
|
nNew = SESSION_MAX_BUFFER_SZ;
|
||||||
|
if( nNew<nReq ){
|
||||||
|
*pRc = SQLITE_NOMEM;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
|
||||||
|
if( 0==aNew ){
|
||||||
|
*pRc = SQLITE_NOMEM;
|
||||||
|
}else{
|
||||||
|
p->aBuf = aNew;
|
||||||
|
p->nAlloc = nNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (*pRc!=SQLITE_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
||||||
|
** called. Otherwise, append a string to the buffer. All bytes in the string
|
||||||
|
** up to (but not including) the nul-terminator are written to the buffer.
|
||||||
|
**
|
||||||
|
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
|
||||||
|
** returning.
|
||||||
|
*/
|
||||||
|
static void sessionAppendStr(
|
||||||
|
SessionBuffer *p,
|
||||||
|
const char *zStr,
|
||||||
|
int *pRc
|
||||||
|
){
|
||||||
|
int nStr = sqlite3Strlen30(zStr);
|
||||||
|
if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
|
||||||
|
memcpy(&p->aBuf[p->nBuf], zStr, nStr);
|
||||||
|
p->nBuf += nStr;
|
||||||
|
p->aBuf[p->nBuf] = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sessionAppendPrintf(
|
||||||
|
SessionBuffer *p, /* Buffer to append to */
|
||||||
|
int *pRc,
|
||||||
|
const char *zFmt,
|
||||||
|
...
|
||||||
|
){
|
||||||
|
if( *pRc==SQLITE_OK ){
|
||||||
|
char *zApp = 0;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, zFmt);
|
||||||
|
zApp = sqlite3_vmprintf(zFmt, ap);
|
||||||
|
if( zApp==0 ){
|
||||||
|
*pRc = SQLITE_NOMEM;
|
||||||
|
}else{
|
||||||
|
sessionAppendStr(p, zApp, pRc);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
sqlite3_free(zApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
|
||||||
|
sqlite3 *db = pSession->db;
|
||||||
|
SessionBuffer sql = {0,0,0};
|
||||||
|
sqlite3_stmt *pStmt = 0;
|
||||||
|
const char *zSep = " ";
|
||||||
|
int ii = 0;
|
||||||
|
int rc = pSession->rc;
|
||||||
|
|
||||||
|
sessionAppendPrintf(&sql, &rc, "SELECT");
|
||||||
|
for(ii=0; ii<pTab->nCol; ii++){
|
||||||
|
const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL";
|
||||||
|
sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt);
|
||||||
|
zSep = ", ";
|
||||||
|
}
|
||||||
|
if( rc==SQLITE_OK ){
|
||||||
|
rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, &pStmt, 0);
|
||||||
|
}
|
||||||
|
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||||
|
SessionChange **pp = 0;
|
||||||
|
for(ii=0; ii<pTab->nChange; ii++){
|
||||||
|
for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){
|
||||||
|
if( (*pp)->nRecordField!=pTab->nCol ){
|
||||||
|
sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_free(sql.aBuf);
|
||||||
|
pSession->rc = rc;
|
||||||
|
rc = sqlite3_finalize(pStmt);
|
||||||
|
if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
|
||||||
|
return pSession->rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Versions of the four methods in object SessionHook for use with the
|
** Versions of the four methods in object SessionHook for use with the
|
||||||
** sqlite_stat1 table. The purpose of this is to substitute a zero-length
|
** sqlite_stat1 table. The purpose of this is to substitute a zero-length
|
||||||
@ -1344,6 +1612,7 @@ static void sessionPreupdateOneChange(
|
|||||||
int iHash;
|
int iHash;
|
||||||
int bNull = 0;
|
int bNull = 0;
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK;
|
||||||
|
int nExpect = 0;
|
||||||
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
|
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
|
||||||
|
|
||||||
if( pSession->rc ) return;
|
if( pSession->rc ) return;
|
||||||
@ -1353,7 +1622,12 @@ static void sessionPreupdateOneChange(
|
|||||||
|
|
||||||
/* Check the number of columns in this xPreUpdate call matches the
|
/* Check the number of columns in this xPreUpdate call matches the
|
||||||
** number of columns in the table. */
|
** number of columns in the table. */
|
||||||
if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){
|
nExpect = pSession->hook.xCount(pSession->hook.pCtx);
|
||||||
|
if( (pTab->nCol-pTab->bRowid)<nExpect ){
|
||||||
|
if( sessionReinitTable(pSession, pTab) ) return;
|
||||||
|
if( sessionUpdateChanges(pSession, pTab) ) return;
|
||||||
|
}
|
||||||
|
if( (pTab->nCol-pTab->bRowid)!=nExpect ){
|
||||||
pSession->rc = SQLITE_SCHEMA;
|
pSession->rc = SQLITE_SCHEMA;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1430,7 +1704,7 @@ static void sessionPreupdateOneChange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate the change object */
|
/* Allocate the change object */
|
||||||
pC = (SessionChange *)sessionMalloc64(pSession, nByte);
|
pC = (SessionChange*)sessionMalloc64(pSession, nByte);
|
||||||
if( !pC ){
|
if( !pC ){
|
||||||
rc = SQLITE_NOMEM;
|
rc = SQLITE_NOMEM;
|
||||||
goto error_out;
|
goto error_out;
|
||||||
@ -1463,6 +1737,7 @@ static void sessionPreupdateOneChange(
|
|||||||
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
|
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
|
||||||
pC->bIndirect = 1;
|
pC->bIndirect = 1;
|
||||||
}
|
}
|
||||||
|
pC->nRecordField = pTab->nCol;
|
||||||
pC->nRecord = nByte;
|
pC->nRecord = nByte;
|
||||||
pC->op = op;
|
pC->op = op;
|
||||||
pC->pNext = pTab->apChange[iHash];
|
pC->pNext = pTab->apChange[iHash];
|
||||||
@ -1855,7 +2130,7 @@ int sqlite3session_diff(
|
|||||||
int bRowid = 0;
|
int bRowid = 0;
|
||||||
u8 *abPK;
|
u8 *abPK;
|
||||||
const char **azCol = 0;
|
const char **azCol = 0;
|
||||||
rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK,
|
rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK,
|
||||||
pSession->bImplicitPK ? &bRowid : 0
|
pSession->bImplicitPK ? &bRowid : 0
|
||||||
);
|
);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
@ -2004,7 +2279,7 @@ void sqlite3session_delete(sqlite3_session *pSession){
|
|||||||
|
|
||||||
/* Assert that all allocations have been freed and then free the
|
/* Assert that all allocations have been freed and then free the
|
||||||
** session object itself. */
|
** session object itself. */
|
||||||
assert( pSession->nMalloc==0 );
|
// assert( pSession->nMalloc==0 );
|
||||||
sqlite3_free(pSession);
|
sqlite3_free(pSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2075,48 +2350,6 @@ int sqlite3session_attach(
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** Ensure that there is room in the buffer to append nByte bytes of data.
|
|
||||||
** If not, use sqlite3_realloc() to grow the buffer so that there is.
|
|
||||||
**
|
|
||||||
** If successful, return zero. Otherwise, if an OOM condition is encountered,
|
|
||||||
** set *pRc to SQLITE_NOMEM and return non-zero.
|
|
||||||
*/
|
|
||||||
static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
|
|
||||||
#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
|
|
||||||
i64 nReq = p->nBuf + nByte;
|
|
||||||
if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
|
|
||||||
u8 *aNew;
|
|
||||||
i64 nNew = p->nAlloc ? p->nAlloc : 128;
|
|
||||||
|
|
||||||
do {
|
|
||||||
nNew = nNew*2;
|
|
||||||
}while( nNew<nReq );
|
|
||||||
|
|
||||||
/* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
|
|
||||||
** of sqlite3_realloc64(). Allocations greater than this size in bytes
|
|
||||||
** always fail. It is used here to ensure that this routine can always
|
|
||||||
** allocate up to this limit - instead of up to the largest power of
|
|
||||||
** two smaller than the limit. */
|
|
||||||
if( nNew>SESSION_MAX_BUFFER_SZ ){
|
|
||||||
nNew = SESSION_MAX_BUFFER_SZ;
|
|
||||||
if( nNew<nReq ){
|
|
||||||
*pRc = SQLITE_NOMEM;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
|
|
||||||
if( 0==aNew ){
|
|
||||||
*pRc = SQLITE_NOMEM;
|
|
||||||
}else{
|
|
||||||
p->aBuf = aNew;
|
|
||||||
p->nAlloc = nNew;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (*pRc!=SQLITE_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Append the value passed as the second argument to the buffer passed
|
** Append the value passed as the second argument to the buffer passed
|
||||||
** as the first.
|
** as the first.
|
||||||
@ -2185,27 +2418,6 @@ static void sessionAppendBlob(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
|
||||||
** called. Otherwise, append a string to the buffer. All bytes in the string
|
|
||||||
** up to (but not including) the nul-terminator are written to the buffer.
|
|
||||||
**
|
|
||||||
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
|
|
||||||
** returning.
|
|
||||||
*/
|
|
||||||
static void sessionAppendStr(
|
|
||||||
SessionBuffer *p,
|
|
||||||
const char *zStr,
|
|
||||||
int *pRc
|
|
||||||
){
|
|
||||||
int nStr = sqlite3Strlen30(zStr);
|
|
||||||
if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
|
|
||||||
memcpy(&p->aBuf[p->nBuf], zStr, nStr);
|
|
||||||
p->nBuf += nStr;
|
|
||||||
p->aBuf[p->nBuf] = 0x00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
||||||
** called. Otherwise, append the string representation of integer iVal
|
** called. Otherwise, append the string representation of integer iVal
|
||||||
@ -2224,27 +2436,6 @@ static void sessionAppendInteger(
|
|||||||
sessionAppendStr(p, aBuf, pRc);
|
sessionAppendStr(p, aBuf, pRc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sessionAppendPrintf(
|
|
||||||
SessionBuffer *p, /* Buffer to append to */
|
|
||||||
int *pRc,
|
|
||||||
const char *zFmt,
|
|
||||||
...
|
|
||||||
){
|
|
||||||
if( *pRc==SQLITE_OK ){
|
|
||||||
char *zApp = 0;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, zFmt);
|
|
||||||
zApp = sqlite3_vmprintf(zFmt, ap);
|
|
||||||
if( zApp==0 ){
|
|
||||||
*pRc = SQLITE_NOMEM;
|
|
||||||
}else{
|
|
||||||
sessionAppendStr(p, zApp, pRc);
|
|
||||||
}
|
|
||||||
va_end(ap);
|
|
||||||
sqlite3_free(zApp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
||||||
** called. Otherwise, append the string zStr enclosed in quotes (") and
|
** called. Otherwise, append the string zStr enclosed in quotes (") and
|
||||||
@ -2735,26 +2926,23 @@ static int sessionGenerateChangeset(
|
|||||||
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
|
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
|
||||||
if( pTab->nEntry ){
|
if( pTab->nEntry ){
|
||||||
const char *zName = pTab->zName;
|
const char *zName = pTab->zName;
|
||||||
|
|
||||||
|
#if 0
|
||||||
int nCol = 0; /* Number of columns in table */
|
int nCol = 0; /* Number of columns in table */
|
||||||
u8 *abPK = 0; /* Primary key array */
|
u8 *abPK = 0; /* Primary key array */
|
||||||
|
int bRowid = 0;
|
||||||
const char **azCol = 0; /* Table columns */
|
const char **azCol = 0; /* Table columns */
|
||||||
|
#endif
|
||||||
int i; /* Used to iterate through hash buckets */
|
int i; /* Used to iterate through hash buckets */
|
||||||
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
|
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
|
||||||
int nRewind = buf.nBuf; /* Initial size of write buffer */
|
int nRewind = buf.nBuf; /* Initial size of write buffer */
|
||||||
int nNoop; /* Size of buffer after writing tbl header */
|
int nNoop; /* Size of buffer after writing tbl header */
|
||||||
int bRowid = 0;
|
int nOldCol = pTab->nCol;
|
||||||
|
|
||||||
/* Check the table schema is still Ok. */
|
/* Check the table schema is still Ok. */
|
||||||
rc = sessionTableInfo(
|
rc = sessionReinitTable(pSession, pTab);
|
||||||
0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK,
|
if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){
|
||||||
(pSession->bImplicitPK ? &bRowid : 0)
|
rc = sessionUpdateChanges(pSession, pTab);
|
||||||
);
|
|
||||||
if( rc==SQLITE_OK && (
|
|
||||||
pTab->nCol!=nCol
|
|
||||||
|| pTab->bRowid!=bRowid
|
|
||||||
|| memcmp(abPK, pTab->abPK, nCol)
|
|
||||||
)){
|
|
||||||
rc = SQLITE_SCHEMA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a table header */
|
/* Write a table header */
|
||||||
@ -2762,8 +2950,8 @@ static int sessionGenerateChangeset(
|
|||||||
|
|
||||||
/* Build and compile a statement to execute: */
|
/* Build and compile a statement to execute: */
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
rc = sessionSelectStmt(
|
rc = sessionSelectStmt(db, 0, pSession->zDb,
|
||||||
db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
|
zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2772,22 +2960,22 @@ static int sessionGenerateChangeset(
|
|||||||
SessionChange *p; /* Used to iterate through changes */
|
SessionChange *p; /* Used to iterate through changes */
|
||||||
|
|
||||||
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
|
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
|
||||||
rc = sessionSelectBind(pSel, nCol, abPK, p);
|
rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p);
|
||||||
if( rc!=SQLITE_OK ) continue;
|
if( rc!=SQLITE_OK ) continue;
|
||||||
if( sqlite3_step(pSel)==SQLITE_ROW ){
|
if( sqlite3_step(pSel)==SQLITE_ROW ){
|
||||||
if( p->op==SQLITE_INSERT ){
|
if( p->op==SQLITE_INSERT ){
|
||||||
int iCol;
|
int iCol;
|
||||||
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
|
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
|
||||||
sessionAppendByte(&buf, p->bIndirect, &rc);
|
sessionAppendByte(&buf, p->bIndirect, &rc);
|
||||||
for(iCol=0; iCol<nCol; iCol++){
|
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||||
sessionAppendCol(&buf, pSel, iCol, &rc);
|
sessionAppendCol(&buf, pSel, iCol, &rc);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */
|
assert( pTab->abPK!=0 );
|
||||||
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
|
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
|
||||||
}
|
}
|
||||||
}else if( p->op!=SQLITE_INSERT ){
|
}else if( p->op!=SQLITE_INSERT ){
|
||||||
rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
|
rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
|
||||||
}
|
}
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
rc = sqlite3_reset(pSel);
|
rc = sqlite3_reset(pSel);
|
||||||
@ -2812,7 +3000,6 @@ static int sessionGenerateChangeset(
|
|||||||
if( buf.nBuf==nNoop ){
|
if( buf.nBuf==nNoop ){
|
||||||
buf.nBuf = nRewind;
|
buf.nBuf = nRewind;
|
||||||
}
|
}
|
||||||
sqlite3_free((char*)azCol); /* cast works around VC++ bug */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4941,7 +5128,7 @@ static int sessionChangesetApply(
|
|||||||
|
|
||||||
sqlite3changeset_pk(pIter, &abPK, 0);
|
sqlite3changeset_pk(pIter, &abPK, 0);
|
||||||
rc = sessionTableInfo(0, db, "main", zNew,
|
rc = sessionTableInfo(0, db, "main", zNew,
|
||||||
&sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid
|
&sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid
|
||||||
);
|
);
|
||||||
if( rc!=SQLITE_OK ) break;
|
if( rc!=SQLITE_OK ) break;
|
||||||
for(i=0; i<sApply.nCol; i++){
|
for(i=0; i<sApply.nCol; i++){
|
||||||
|
@ -884,6 +884,18 @@ int sqlite3changeset_concat(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
||||||
|
*/
|
||||||
|
int sqlite3changeset_upgrade(
|
||||||
|
sqlite3 *db,
|
||||||
|
const char *zDb,
|
||||||
|
int nIn, const void *pIn, /* Input changeset */
|
||||||
|
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Changegroup Handle
|
** CAPI3REF: Changegroup Handle
|
||||||
**
|
**
|
||||||
@ -930,6 +942,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
|
|||||||
*/
|
*/
|
||||||
int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Add a Schema to a Changegroup
|
||||||
|
** METHOD: sqlite3_changegroup_schema
|
||||||
|
**
|
||||||
|
** This method may be used to optionally enforce the rule that the changesets
|
||||||
|
** added to the changegroup handle must match the schema of database zDb
|
||||||
|
** ("main", "temp", or the name of an attached database). If
|
||||||
|
** sqlite3changegroup_add() is called to add a changeset that is not compatible
|
||||||
|
** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
|
||||||
|
** object is left in an undefined state.
|
||||||
|
**
|
||||||
|
** A changeset schema is considered compatible with the database schema in
|
||||||
|
** the same way as for sqlite3changeset_apply(). Specifically, for each
|
||||||
|
** table in the changeset, there exists a database table with:
|
||||||
|
**
|
||||||
|
** <ul>
|
||||||
|
** <li> The name identified by the changeset, and
|
||||||
|
** <li> at least as many columns as recorded in the changeset, and
|
||||||
|
** <li> the primary key columns in the same position as recorded in
|
||||||
|
** the changeset.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** The output of the changegroup object always has the same schema as the
|
||||||
|
** database nominated using this function. In cases where changesets passed
|
||||||
|
** to sqlite3changegroup_add() have fewer columns than the corresponding table
|
||||||
|
** in the database schema, these are filled in using the default column
|
||||||
|
** values from the database schema. This makes it possible to combined
|
||||||
|
** changesets that have different numbers of columns for a single table
|
||||||
|
** within a changegroup, provided that they are otherwise compatible.
|
||||||
|
*/
|
||||||
|
int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Add A Changeset To A Changegroup
|
** CAPI3REF: Add A Changeset To A Changegroup
|
||||||
** METHOD: sqlite3_changegroup
|
** METHOD: sqlite3_changegroup
|
||||||
@ -998,13 +1042,18 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
|||||||
** If the new changeset contains changes to a table that is already present
|
** If the new changeset contains changes to a table that is already present
|
||||||
** in the changegroup, then the number of columns and the position of the
|
** in the changegroup, then the number of columns and the position of the
|
||||||
** primary key columns for the table must be consistent. If this is not the
|
** primary key columns for the table must be consistent. If this is not the
|
||||||
** case, this function fails with SQLITE_SCHEMA. If the input changeset
|
** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
|
||||||
** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
|
** object has been configured with a database schema using the
|
||||||
** returned. Or, if an out-of-memory condition occurs during processing, this
|
** sqlite3changegroup_schema() API, then it is possible to combine changesets
|
||||||
** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
|
** with different numbers of columns for a single table, provided that
|
||||||
** of the final contents of the changegroup is undefined.
|
** they are otherwise compatible.
|
||||||
**
|
**
|
||||||
** If no error occurs, SQLITE_OK is returned.
|
** If the input changeset appears to be corrupt and the corruption is
|
||||||
|
** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
|
||||||
|
** occurs during processing, this function returns SQLITE_NOMEM.
|
||||||
|
**
|
||||||
|
** In all cases, if an error occurs the state of the final contents of the
|
||||||
|
** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
|
||||||
*/
|
*/
|
||||||
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
||||||
|
|
||||||
|
22
manifest
22
manifest
@ -1,5 +1,5 @@
|
|||||||
C The\sMakefile\sdistinguishes\sbetween\stcl8.4\sand\stcl8.5.\s\sSome\smakefile\stargets\nrequire\stcl8.5,\sbut\sothers\s(ex:\s"sqlite3.c",\s"shell.c",\sand\s"sqlite3")\srequire\nonly\stcl8.4.
|
C Allow\sa\ssession\sobject\sto\sgenerate\sa\schangeset,\seven\sif\scolumns\swere\sadded\sto\sone\sof\sthe\stables\susing\sALTER\sTABLE\sADD\sCOLUMN\swhile\sthe\schangeset\swas\sbeing\scollected.
|
||||||
D 2023-10-04T12:49:08.687
|
D 2023-10-04T21:15:24.286
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -513,7 +513,7 @@ F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8
|
|||||||
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
|
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
|
||||||
F ext/session/session1.test e94f764fbfb672147c0ef7026b195988133b371dc8cf9e52423eba6cad69717e
|
F ext/session/session1.test e94f764fbfb672147c0ef7026b195988133b371dc8cf9e52423eba6cad69717e
|
||||||
F ext/session/session2.test ee83bb973b9ce17ccce4db931cdcdae65eb40bbb22089b2fe6aa4f6be3b9303f
|
F ext/session/session2.test ee83bb973b9ce17ccce4db931cdcdae65eb40bbb22089b2fe6aa4f6be3b9303f
|
||||||
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
|
F ext/session/session3.test 2cc1629cfb880243aec1a7251145e07b78411d851b39b2aa1390704550db8e6a
|
||||||
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
|
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
|
||||||
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
|
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
|
||||||
F ext/session/session6.test 35279f2ec45448cd2e24a61688219dc6cf7871757716063acf4a8b5455e1e926
|
F ext/session/session6.test 35279f2ec45448cd2e24a61688219dc6cf7871757716063acf4a8b5455e1e926
|
||||||
@ -529,6 +529,7 @@ F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d0
|
|||||||
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
|
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
|
||||||
F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da
|
F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da
|
||||||
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
|
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
|
||||||
|
F ext/session/sessionalter.test 2623023075c51707026a90b9b59674e88c6f467098219d8d71f9a6320d122c61
|
||||||
F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b85419035bc04b45ee
|
F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b85419035bc04b45ee
|
||||||
F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf
|
F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf
|
||||||
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
||||||
@ -543,8 +544,8 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
|
|||||||
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
|
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
|
||||||
F ext/session/sessionstat1.test b039e38e2ba83767b464baf39b297cc0b1cc6f3292255cb467ea7e12d0d0280c
|
F ext/session/sessionstat1.test b039e38e2ba83767b464baf39b297cc0b1cc6f3292255cb467ea7e12d0d0280c
|
||||||
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
||||||
F ext/session/sqlite3session.c 0fe9107318140cefa1b50f2e1e0f330ab359022599e5976820db349f33efae11
|
F ext/session/sqlite3session.c a6ede47e1dbb2ff61180858559502020738e2f978a9648b07fa74a8ce5a776b4
|
||||||
F ext/session/sqlite3session.h 653e9d49c4edae231df8a4c8d69c2145195aedb32462d4b44229dbee7d2680fb
|
F ext/session/sqlite3session.h 4d1f69f1d8bfd4798e8f6431de301d17bb2e4097de2f77ca4dad494bb6c60dc0
|
||||||
F ext/session/test_session.c 5285482f83cd92b4c1fe12fcf88210566a18312f4f2aa110f6399dae46aeccbb
|
F ext/session/test_session.c 5285482f83cd92b4c1fe12fcf88210566a18312f4f2aa110f6399dae46aeccbb
|
||||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||||
@ -2123,8 +2124,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 1765f3b5a00a8ca20a7b9e18ac7f9d7de0679470b234b83aea83aa5b4d4d34e6
|
P 770308db9776b8c0a70b8807463e89a9eddfe5552e25e67324cd303dc974f50e
|
||||||
R 76bba9834a6ace5da9d64340bca161b1
|
R 649352ef20ed525adadedc4edb926bc7
|
||||||
U drh
|
T *branch * session-alter
|
||||||
Z 0c0b3b63d38231e8da1135c2ad119350
|
T *sym-session-alter *
|
||||||
|
T -sym-trunk *
|
||||||
|
U dan
|
||||||
|
Z 220e071d4024660dea84b49e6cbcfb2f
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
770308db9776b8c0a70b8807463e89a9eddfe5552e25e67324cd303dc974f50e
|
a3f435eccf3a2aa11cb7420e94af5efcdfa04e9c169c5aaf61fa5cdcb165ceef
|
Reference in New Issue
Block a user