-C Up\slogs\sto\s"v=21".
-D 2024-12-23T11:31:16.861
+C Implemenation\sof\sexperimental\sAPI\ssqlite3_schema_copy(),\sfor\scopying\sa\sdatabase\sschema\sbetween\sconnections.\sMore\stesting\sto\scome.
+D 2025-01-06T20:39:57.366
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2
-F src/alter.c aa93e37e4a36a0525bbb2a2aeda20d2018f0aa995542c7dc658e031375e3f532
-F src/analyze.c c011b69c903197f45688799d81e9f216be84f6eb4b3faa23a3d7638111859da2
+F src/alter.c c48e7cbb7f87b28e9e954bfed3327d8e1b8e2a020fbb5bbeca78f6534d6c3c31
+F src/analyze.c 19a2b6ed803a3c0add5f48adbac4735a064d329aa91790b94c968fef0bf7c438
F src/attach.c 08235ab62ed5ccc93c22bf36e640d19effcd632319615851bccf724ec9341333
F src/auth.c 4c1ea890e0069ad73bead5d17a5b12c34cfa4f1a24175c8147ea439b64be271c
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/btree.c 2664c81f217a42afadc7c010bb4a175057d5e53b99e9512234eb74817f2ad59c
F src/btree.h bdeeb35614caa33526b603138f04c8d07a3f90a1300b5ade76848b755edf2027
F src/btreeInt.h caa893e74d2261fb0ff1681fce998533c0552858e882bd04fc6805075f5f6e75
-F src/build.c 08697d6a4df78f8e289582eb58473445492319676f81cc4794ef4056d36ae5fd
+F src/build.c 19d6ba76fb72f81fed5323298284d56f0c32c7a59c38532e56c9811cdd9a89a5
F src/callback.c 43c8ca52b1ecbdec43522f121126fd4e3ee10bc9ca01cdd3ae207cfa419780b6
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 193f6f9a75204274b7e7f45ac6d6517c12c70b55a5dfb39312dfc3a52e2a8138
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c d07d1872161db7c922194c5279d67686e8355c6d304ed0e0646fbaa59b59f561
F src/shell.c.in 40de636c1d90fb8a9ca7f49dc8f50d930f1b60736e73aca5eb37c4c7d0e47f9d
-F src/sqlite.h.in b7ff496637807ae88b2557039fc940518db328bf5d5621e2f7c048dfba32a52b
+F src/sqlite.h.in f991accd827e7853d092366690772b2de831a8fcbe1cb127f3831e39a4302e7e
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
-F src/sqliteInt.h 4281b9ed8b660fe703a58ff8aa70247df07ad6b75f47eb7f45d1380021720f81
+F src/sqliteInt.h 9e7a2d0941b10f059c1c62f13cea460b55bcd6b68c3a3c2fe170ce79e25a0032
F src/sqliteLimit.h da2cffdffa7d71b035f9e59668fbaad74b5939300dbfa9915304e6d8f72b0761
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c c6888598f08dee3d9112a38ef42c8f5c89ca7f3190f4694744d0b84250f4bf8c
F src/tclsqlite.h c6af51f31a2b2172d674608763a4b98fdf5cd587e4025053e546fb8077757262
-F src/test1.c ecbd27140e63d6d70e221ac7e2aa565a13359c126f432b4469ad80f5d66d62ee
+F src/test1.c 10d600bdbd99b4fa078195ca7d4746880f61553196a341ac7f1c246c1e882f24
F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3
F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b
F src/test4.c 13e57ae7ec7a959ee180970aef09deed141252fe9bb07c61054f0dfa4f1dfd5d
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/treeview.c 88aa39b754f5ef7214385c1bbbdd2f3dc20efafeed0cf590e8d1199b9c6e44aa
-F src/trigger.c 247e2d712d5edc6021d52a169f6ac9a9c10d7144bc4ac7ea06c1ed2aa414659f
+F src/trigger.c 6868181a0efd15ddaf82f23658b47eda4a5fa5bee49fbf94ccf8abe383559a32
F src/update.c 2dd1b745acc9253df1b210ac69137c7a6b290e561d3f42da24418c4e807e889b
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e
F test/schema4.test 3b26c9fa916abb6dadf894137adcf41b7796f7b9
F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e
F test/schema6.test e4bd1f23d368695eb9e7b51ef6e02ca0642ea2ab4a52579959826b5e7dce1f9b
+F test/schemacopy.test 63091fa6dd23a5179a789882f2932dd00159225e313c13b3ffa6698e846dcebd
F test/schemafault.test 1936bceca55ac82c5efbcc9fc91a1933e45c8d1e1d106b9a7e56c972a5a2a51e
F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2ff09384
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 6e800b7035f55a211917d28cacf829b1681f37cbd2e6989c4cc20d4027a4192d
-R 9198f76a7ca82655c38c2643038b3bc6
+P dc3a24a784c95398656e0d7885f7eb0ee626b86c896e759a6ac5c243fd6f0ab7
+R 319fabba106aeb0d0322166af764ce3c
+T *branch * schema-copy
+T *sym-schema-copy *
+T -sym-cf8f1552-commit-instr *
U dan
-Z 7e52e40c5c5e6ec4cc1a91e31abd90cb
+Z 463cbc692201d0ebdc4f4cfb189d8b05
# Remove this line to create a well-formed Fossil manifest.
-dc3a24a784c95398656e0d7885f7eb0ee626b86c896e759a6ac5c243fd6f0ab7
+65ede04d2176e7206ca6ac004df14f488c274a6b092f6a7dc897b049012898fb
** Invoke sqlite3WalkExpr() or sqlite3WalkSelect() on all Select or Expr
** objects that are part of the trigger passed as the second argument.
*/
-static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
+void sqlite3WalkTrigger(Walker *pWalker, Trigger *pTrigger){
TriggerStep *pStep;
/* Find tokens to edit in WHEN clause */
}
/* Find tokens to edit in various expressions and selects */
- renameWalkTrigger(&sWalker, sParse.pNewTrigger);
+ sqlite3WalkTrigger(&sWalker, sParse.pNewTrigger);
}
assert( rc==SQLITE_OK );
if( isLegacy==0 ){
rc = renameResolveTrigger(&sParse);
if( rc==SQLITE_OK ){
- renameWalkTrigger(&sWalker, pTrigger);
+ sqlite3WalkTrigger(&sWalker, pTrigger);
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
renameTokenFind(&sParse, &sCtx, pStep->zTarget);
#ifndef SQLITE_OMIT_TRIGGER
rc = renameResolveTrigger(&sParse);
if( rc==SQLITE_OK ){
- renameWalkTrigger(&sWalker, sParse.pNewTrigger);
+ sqlite3WalkTrigger(&sWalker, sParse.pNewTrigger);
}
#endif /* SQLITE_OMIT_TRIGGER */
}
** Grow the pIdx->aSample[] array. Return SQLITE_OK if successful, or
** SQLITE_NOMEM otherwise.
*/
-static int growSampleArray(sqlite3 *db, Index *pIdx, int *piOff){
+static int growSampleArray(sqlite3 *db, Index *pIdx, int nReq, int *piOff){
int nIdxCol = pIdx->nSampleCol;
int nNew = 0;
IndexSample *aNew = 0;
u64 t;
assert( pIdx->nSample==pIdx->nSampleAlloc );
- nNew = SQLITE_STAT4_EST_SAMPLES;
- if( pIdx->nSample ){
- nNew = pIdx->nSample*2;
+ if( nReq==0 ){
+ nNew = SQLITE_STAT4_EST_SAMPLES;
+ if( pIdx->nSample ){
+ nNew = pIdx->nSample*2;
+ }
+ }else{
+ nNew = nReq;
}
/* Set nByte to the required amount of space */
return SQLITE_OK;
}
+int sqlite3AnalyzeCopyStat4(
+ sqlite3 *db,
+ Index *pTo,
+ Index *pFrom
+){
+ if( pFrom->nSample>0 ){
+ Schema *pSchema = pTo->pSchema;
+ int ii;
+
+ pTo->nSample = pTo->nSampleAlloc = 0;
+ if( growSampleArray(db, pTo, pFrom->nSample, &pSchema->nStat4Space) ){
+ return SQLITE_NOMEM;
+ }
+ pTo->nSample = pFrom->nSample;
+ memcpy(pTo->aAvgEq, pFrom->aAvgEq, pFrom->nSampleCol * sizeof(tRowcnt));
+ memcpy(pTo->aSample[0].anEq, pFrom->aSample[0].anEq,
+ pTo->nSampleCol * 3 * sizeof(tRowcnt) * pTo->nSample
+ );
+ for(ii=0; ii<pTo->nSample; ii++){
+ pTo->aSample[ii].p = pFrom->aSample[ii].p;
+ pTo->aSample[ii].n = pFrom->aSample[ii].n;
+ }
+ }
+ return SQLITE_OK;
+}
+
/*
** Allocate the space that will likely be required for the Index.aSample[]
** arrays populated by loading data from the sqlite_stat4 table. Return
if( pSchema->pStat4Space==0 ){
return SQLITE_NOMEM_BKPT;
}
+ pSchema->nStat4Space = nByte;
}
return SQLITE_OK;
pIdx->nSampleCol = pIdx->nColumn;
}
t2 = sqlite3STimeNow();
- if( growSampleArray(db, pIdx, &iBlockOff) ) break;
+ if( growSampleArray(db, pIdx, 0, &iBlockOff) ) break;
if( db->aSchemaTime ){
db->aSchemaTime[SCHEMA_TIME_STAT4_GROWUS] += (sqlite3STimeNow() - t);
}
void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
sqlite3WithDelete(db, (With*)pWith);
}
+
+static Index *schemaCopyIndexList(sqlite3 *db, Table *pTab, Index *pIdx){
+ Schema *pSchema = pTab->pSchema;
+ Index *pRet = 0;
+ Index *p = 0;
+ Index **ppNew = &pRet;
+ for(p=pIdx; p; p=p->pNext){
+ Index *pNew = 0;
+ int nName = sqlite3Strlen30(p->zName) + 1;
+ int nExtra = 0;
+ char *zExtra = 0;
+ int ii;
+ for(ii=0; ii<p->nColumn; ii++){
+ if( p->azColl[ii]!=sqlite3StrBINARY ){
+ nExtra += sqlite3Strlen30(p->azColl[ii]) + 1;
+ }
+ }
+
+ pNew = sqlite3AllocateIndexObject(db, p->nColumn, nName+nExtra, &zExtra);
+ if( pNew ){
+ pNew->zName = zExtra;
+ memcpy(pNew->zName, p->zName, nName);
+ zExtra += nName;
+ memcpy(pNew->aiColumn, p->aiColumn, sizeof(i16) * p->nColumn);
+ memcpy(pNew->aiRowLogEst, p->aiRowLogEst, sizeof(LogEst)*(p->nColumn+1));
+ pNew->pTable = pTab;
+ pNew->zColAff = 0;
+ pNew->pSchema = pSchema;
+ memcpy(pNew->aSortOrder, p->aSortOrder, p->nColumn);
+ for(ii=0; ii<p->nColumn; ii++){
+ char *zColl = 0;
+ if( p->azColl[ii]!=sqlite3StrBINARY ){
+ int nColl = sqlite3Strlen30(p->azColl[ii]) + 1;
+ memcpy(zExtra, p->azColl[ii], nColl);
+ zColl = zExtra;
+ zExtra += nColl;
+ }else{
+ zColl = (char*)sqlite3StrBINARY;
+ }
+ pNew->azColl[ii] = zColl;
+ }
+ pNew->pPartIdxWhere = sqlite3ExprDup(db, p->pPartIdxWhere, 0);
+ pNew->aColExpr = sqlite3ExprListDup(db, p->aColExpr, 0);
+ pNew->tnum = p->tnum;
+ pNew->szIdxRow = p->szIdxRow;
+ memcpy(&pNew->onError,&p->onError,sizeof(Index)-offsetof(Index, onError));
+ pNew->isResized = 0;
+#ifdef SQLITE_ENABLE_STAT4
+ assert( pNew->aiRowEst==0 && p->aiRowEst==0 );
+ sqlite3AnalyzeCopyStat4(db, pNew, p);
+#endif
+
+ if( sqlite3HashInsert(&pSchema->idxHash, pNew->zName, pNew) ){
+ sqlite3OomFault(db);
+ }
+ *ppNew = pNew;
+ ppNew = &pNew->pNext;
+ }
+ }
+
+ return pRet;
+}
+
+static void schemaCopyRefixSrclist(Schema *pSchema, SrcList *pSrc){
+ if( pSrc ){
+ int ii;
+ for(ii=0; ii<pSrc->nSrc; ii++){
+ if( pSrc->a[ii].fg.fixedSchema ){
+ pSrc->a[ii].u4.pSchema = pSchema;
+ }
+ }
+ }
+}
+
+static int schemaCopySelectCb(Walker *pWalker, Select *pSelect){
+ schemaCopyRefixSrclist(pWalker->u.pSchema, pSelect->pSrc);
+ return WRC_Continue;
+}
+
+static TriggerStep *schemaCopyTriggerStepList(
+ sqlite3 *db,
+ Trigger *pTrig,
+ TriggerStep *pList
+){
+ TriggerStep *pRet = 0;
+ TriggerStep *p = 0;
+ TriggerStep **ppNew = &pRet;
+ for(p=pList; p; p=p->pNext){
+ int nTarget = sqlite3Strlen30(p->zTarget) + 1;
+ int nAlloc = sizeof(TriggerStep) + nTarget;
+ TriggerStep *pNew = (TriggerStep*)sqlite3DbMallocZero(db, nAlloc);
+ if( pNew ){
+ pNew->op = p->op;
+ pNew->orconf = p->orconf;
+ pNew->pTrig = pTrig;
+ pNew->pSelect = sqlite3SelectDup(db, p->pSelect, 0);
+ if( p->zTarget ){
+ pNew->zTarget = (char*)&pNew[1];
+ memcpy(pNew->zTarget, p->zTarget, nTarget);
+ }
+ pNew->pFrom = sqlite3SrcListDup(db, p->pFrom, 0);
+ schemaCopyRefixSrclist(pTrig->pSchema, pNew->pFrom);
+ pNew->pWhere = sqlite3ExprDup(db, p->pWhere, 0);
+ pNew->pExprList = sqlite3ExprListDup(db, p->pExprList, 0);
+ pNew->pIdList = sqlite3IdListDup(db, p->pIdList);
+ pNew->pUpsert = sqlite3UpsertDup(db, p->pUpsert);
+ assert( pNew->pUpsert==0 || pNew->pUpsert->pUpsertSrc==0 );
+ pNew->zSpan = sqlite3DbStrDup(db, p->zSpan);
+
+ *ppNew = pNew;
+ ppNew = &pNew->pNext;
+ if( pRet ){
+ pRet->pLast = pNew;
+ }
+ }
+ }
+ return pRet;
+}
+
+static Trigger *schemaCopyTriggerList(sqlite3 *db, Table *pTab, Trigger *pList){
+ Walker sWalker;
+ Schema *pSchema = pTab->pSchema;
+ Trigger *pRet = 0;
+ Trigger *p = 0;
+ Trigger **ppNew = &pRet;
+
+ memset(&sWalker, 0, sizeof(sWalker));
+ sWalker.xSelectCallback = schemaCopySelectCb;
+ sWalker.xExprCallback = sqlite3ExprWalkNoop;
+ sWalker.u.pSchema = pSchema;
+
+ for(p=pList; p; p=p->pNext){
+ Trigger *pNew = sqlite3DbMallocZero(db, sizeof(Trigger));
+ if( pNew ){
+ memcpy(pNew, p, sizeof(Trigger));
+ pNew->zName = sqlite3DbStrDup(db, pNew->zName);
+ pNew->table = sqlite3DbStrDup(db, pNew->table);
+ pNew->pWhen = sqlite3ExprDup(db, pNew->pWhen, 0);
+ pNew->pColumns = sqlite3IdListDup(db, pNew->pColumns);
+ pNew->pSchema = pTab->pSchema;
+ pNew->pTabSchema = pTab->pSchema;
+ pNew->step_list = schemaCopyTriggerStepList(db, pNew, pNew->step_list);
+ if( sqlite3HashInsert(&pSchema->trigHash, pNew->zName, pNew) ){
+ sqlite3OomFault(db);
+ }
+ sqlite3WalkTrigger(&sWalker, pNew);
+ *ppNew = pNew;
+ ppNew = &pNew->pNext;
+ }
+ }
+
+ return pRet;
+}
+
+static FKey *schemaCopyFKeyList(sqlite3 *db, Table *pTab, FKey *pList){
+ Schema *pSchema = pTab->pSchema;
+ FKey *pRet = 0;
+ FKey *p = 0;
+ FKey **ppNew = &pRet;
+ for(p=pList; p; p=p->pNextFrom){
+ FKey *pNew = 0;
+ int nByte = sizeof(FKey) + ((p->nCol - 1) * sizeof(struct sColMap));
+ int ii;
+ nByte += sqlite3Strlen30(p->zTo) + 1;
+ for(ii=0; ii<p->nCol; ii++){
+ nByte += sqlite3Strlen30(p->aCol[ii].zCol) + 1;
+ }
+ pNew = (FKey*)sqlite3DbMallocZero(db, nByte);
+ if( pNew ){
+ FKey *pNextTo = 0;
+ char *z = (char*)&pNew->aCol[p->nCol];
+ int n = 0;
+ pNew->pFrom = pTab;
+ n = sqlite3Strlen30(p->zTo) + 1;
+ pNew->zTo = z;
+ memcpy(pNew->zTo, p->zTo, n);
+ z += n;
+ pNew->nCol = p->nCol;
+ pNew->isDeferred = p->isDeferred;
+ pNew->aAction[0] = p->aAction[0];
+ pNew->aAction[1] = p->aAction[1];
+ for(ii=0; ii<p->nCol; ii++){
+ pNew->aCol[ii].iFrom = p->aCol[ii].iFrom;
+ if( p->aCol[ii].zCol ){
+ n = sqlite3Strlen30(p->aCol[ii].zCol) + 1;
+ pNew->aCol[ii].zCol = z;
+ memcpy(z, p->aCol[ii].zCol, n);
+ z += n;
+ }
+ }
+
+ pNextTo = (FKey*)sqlite3HashInsert(&pSchema->fkeyHash, pNew->zTo, pNew);
+ if( pNextTo==pNew ){
+ sqlite3OomFault(db);
+ }else if( pNextTo ){
+ assert( pNextTo->pPrevTo==0 );
+ pNew->pNextTo = pNextTo;
+ pNextTo->pPrevTo = pNew;
+ }
+
+ *ppNew = pNew;
+ ppNew = &pNew->pNextFrom;
+ }
+ }
+
+ return pRet;
+}
+
+static void schemaCopyTable(sqlite3 *db, Schema *pTo, Table *pTab){
+ Table *pNew = 0;
+
+ pNew = (Table*)sqlite3DbMallocRawNN(db, sizeof(Table));
+ if( pNew ){
+ memcpy(pNew, pTab, sizeof(Table));
+ pNew->zName = sqlite3DbStrDup(db, pNew->zName);
+ pNew->aCol = sqlite3DbMallocRawNN(db, pNew->nCol*sizeof(Column));
+ pNew->nTabRef = 1;
+ if( pNew->aCol ){
+ int ii;
+ memcpy(pNew->aCol, pTab->aCol, pNew->nCol*sizeof(Column));
+ for(ii=0; ii<pNew->nCol; ii++){
+ Column *pCol = &pNew->aCol[ii];
+ const char *zCopy = pCol->zCnName;
+ int nCopy = sqlite3Strlen30(zCopy);
+ if( pCol->colFlags & COLFLAG_HASTYPE ){
+ nCopy++;
+ nCopy += sqlite3Strlen30(&zCopy[nCopy]);
+ }
+ if( pCol->colFlags & COLFLAG_HASCOLL ){
+ nCopy++;
+ nCopy += sqlite3Strlen30(&zCopy[nCopy]);
+ }
+ pCol->zCnName = sqlite3DbStrNDup(db, zCopy, nCopy);
+ }
+ }
+
+ pNew->pSchema = pTo;
+ pNew->pIndex = schemaCopyIndexList(db, pNew, pNew->pIndex);
+ pNew->zColAff = 0;
+ pNew->pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0);
+
+ if( IsView(pNew) ){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(sWalker));
+ sWalker.xSelectCallback = schemaCopySelectCb;
+ sWalker.xExprCallback = sqlite3ExprWalkNoop;
+ sWalker.u.pSchema = pTo;
+ pNew->u.view.pSelect = sqlite3SelectDup(db, pNew->u.view.pSelect, 0);
+ sqlite3WalkSelect(&sWalker, pNew->u.view.pSelect);
+ }else if( IsVirtual(pNew) ){
+ int nAlloc = pNew->u.vtab.nArg * sizeof(char*);
+ pNew->u.vtab.p = 0;
+ pNew->u.vtab.azArg = (char**)sqlite3DbMallocRaw(db, nAlloc);
+ if( pNew->u.vtab.azArg ){
+ int ii;
+ for(ii=0; ii<pNew->u.vtab.nArg; ii++){
+ pNew->u.vtab.azArg[ii] = sqlite3DbStrDup(db, pTab->u.vtab.azArg[ii]);
+ }
+ }
+ }else{
+ pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pNew->u.tab.pDfltList, 0);
+ pNew->u.tab.pFKey = schemaCopyFKeyList(db, pNew, pNew->u.tab.pFKey);
+ }
+
+ pNew->pTrigger = schemaCopyTriggerList(db, pNew, pNew->pTrigger);
+ }
+
+ if( db->mallocFailed==0 ){
+ if( sqlite3HashInsert(&pTo->tblHash, pNew->zName, pNew) ){
+ db->mallocFailed = 1;
+ }
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+ if( strcmp(pNew->zName, "sqlite_sequence")==0 ){
+ pTo->pSeqTab = pNew;
+ }
+#endif
+ }
+ if( db->mallocFailed ){
+ sqlite3DeleteTable(db, pNew);
+ }
+}
+
+int sqlite3_schema_copy(
+ sqlite3 *db, const char *zTo,
+ sqlite3 *dbFrom, const char *zFrom
+){
+ int iTo = 0;
+ int iFrom = 0;
+ Schema *pTo = 0;
+ Schema *pFrom = 0;
+ HashElem *k = 0;
+ int rc = SQLITE_OK;
+
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ DisableLookaside;
+
+ if( zTo ) iTo = sqlite3FindDbName(db, zTo);
+ if( zFrom ) iFrom = sqlite3FindDbName(dbFrom, zFrom);
+
+ if( iFrom<0 || iTo<0 ){
+ rc = SQLITE_ERROR;
+ goto schema_copy_done;
+ }
+
+ if( !DbHasProperty(dbFrom, iFrom, DB_SchemaLoaded)
+ || DbHasProperty(db, iTo, DB_SchemaLoaded)
+ ){
+ goto schema_copy_done;
+ }
+ pTo = db->aDb[iTo].pSchema;
+ pFrom = dbFrom->aDb[iFrom].pSchema;
+ assert( pTo && pFrom );
+
+ pTo->schema_cookie = pFrom->schema_cookie;
+ pTo->iGeneration = pFrom->iGeneration;
+ pTo->file_format = pFrom->file_format;
+ pTo->enc = pFrom->enc;
+ pTo->cache_size = pFrom->cache_size;
+ pTo->schemaFlags = pFrom->schemaFlags;
+
+#ifdef SQLITE_ENABLE_STAT4
+ if( pFrom->pStat4Space ){
+ pTo->pStat4Space = sqlite3_malloc(pFrom->nStat4Space);
+ if( pTo->pStat4Space==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto schema_copy_done;
+ }
+ pTo->nStat4Space = 0;
+ }
+#endif
+
+ for(k=sqliteHashFirst(&pFrom->tblHash); k; k=sqliteHashNext(k)){
+ Table *pTab = (Table*)sqliteHashData(k);
+ schemaCopyTable(db, pTo, pTab);
+ }
+
+ schema_copy_done:
+ EnableLookaside;
+ sqlite3BtreeLeaveAll(db);
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
+ return rc;
+}
+
#endif /* !defined(SQLITE_OMIT_CTE) */
#define SQLITE_COMMIT_CONFLICT_FRAME 3
#define SQLITE_COMMIT_CONFLICT_PGNO 4
+int sqlite3_schema_copy(
+ sqlite3 *pTo, const char *zTo,
+ sqlite3 *pFrom, const char *zFrom
+);
/*
** Undo the hack that converts floating point types to integer for
int cache_size; /* Number of pages to use in the cache */
#ifdef SQLITE_ENABLE_STAT4
void *pStat4Space; /* Memory for stat4 Index.aSample[] arrays */
+ int nStat4Space; /* Size of pStat4Space allocation in bytes */
#endif
};
SrcItem *pSrcItem; /* A single FROM clause item */
DbFixer *pFix; /* See sqlite3FixSelect() */
Mem *aMem; /* See sqlite3BtreeCursorHint() */
+ Schema *pSchema;
} u;
};
void sqlite3ShowTriggerStepList(const TriggerStep*);
void sqlite3ShowTrigger(const Trigger*);
void sqlite3ShowTriggerList(const Trigger*);
+ void sqlite3WalkTrigger(Walker *pWalker, Trigger *pTrigger);
#endif
#ifndef SQLITE_OMIT_WINDOWFUNC
void sqlite3ShowWindow(const Window*);
void sqlite3Stat4ProbeFree(UnpackedRecord*);
int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
+int sqlite3AnalyzeCopyStat4(sqlite3*, Index*, Index *pFrom);
#endif
+TriggerStep *sqlite3SchemaCopyTriggerStepList(sqlite3 *, TriggerStep*);
+
/*
** The interface to the LEMON-generated parser
*/
return TCL_OK;
}
+/*
+** Usage: sqlite3_schema_copy NBYTE
+*/
+static int SQLITE_TCLAPI test_schema_copy(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *dbTo = 0;
+ sqlite3 *dbFrom = 0;
+ const char *zTo = 0;
+ const char *zFrom = 0;
+ int rc = SQLITE_OK;
+
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DBTO DBNAMETO DBFROM DBFROMNAME");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &dbTo) ) return TCL_ERROR;
+ if( getDbPointer(interp, Tcl_GetString(objv[3]), &dbFrom) ) return TCL_ERROR;
+ zTo = Tcl_GetString(objv[2]);
+ zFrom = Tcl_GetString(objv[4]);
+
+ rc = sqlite3_schema_copy(dbTo, zTo, dbFrom, zFrom);
+
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
+ return TCL_OK;
+}
+
/*
** tclcmd: sqlite3_commit_status db DBNAME OP
*/
#endif
{ "sqlite3_commit_status", test_commit_status, 0 },
{ "sqlite3_randomness", test_sqlite3_randomness, 0 },
+ { "sqlite3_schema_copy", test_schema_copy, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
return pTriggerStep;
}
+
/*
** Recursively delete a Trigger structure
*/
--- /dev/null
+# 2025 January 4
+#
+# 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 regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix schemacopy
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2), (3, 4);
+}
+
+do_test 1.1 {
+ sqlite3 db2 test.db
+ sqlite3_schema_copy db2 main db main
+} SQLITE_OK
+
+db close
+
+do_execsql_test -db db2 1.2 {
+ SELECT * FROM t1
+} {1 2 3 4}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE, c INTEGER, d);
+ CREATE INDEX i1 ON t1( (c+d) );
+ CREATE INDEX i2 ON t1( (c+d) DESC, (d-c) ASC);
+ CREATE INDEX i3 ON t1( c, d ) WHERE b<3;
+ CREATE INDEX i4 ON t1( d COLLATE nocase);
+ CREATE INDEX i5 ON t1( c );
+
+ CREATE TABLE t2(x, y PRIMARY KEY, z) WITHOUT ROWID;
+ CREATE TABLE t3(x COLLATE nocase, y, z, PRIMARY KEY(x, y)) WITHOUT ROWID;
+
+ CREATE UNIQUE INDEX t3i1 ON t3(z);
+
+ CREATE TABLE t4(a, b, c);
+ CREATE TABLE log(x);
+ CREATE TRIGGER t4ai AFTER INSERT ON t4 BEGIN
+ INSERT INTO log VALUES(new.a ||','|| new.b ||','|| new.c);
+ UPDATE log SET x = new.rowid || ':' || x WHERE rowid = (
+ SELECT max(rowid) FROM log
+ );
+ END;
+
+ CREATE TABLE x1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE x2(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE x3(a INTEGER PRIMARY KEY, b);
+
+ CREATE VIEW xx AS
+ SELECT a, b FROM x1
+ UNION ALL
+ SELECT a, b FROM x2
+ UNION ALL
+ SELECT a, b FROM x3;
+ SELECT * FROM xx;
+}
+
+do_test 2.1 {
+ sqlite3 db2 test.db
+ sqlite3_schema_copy db2 main db main
+} SQLITE_OK
+
+do_execsql_test -db db2 2.2 {
+ INSERT INTO t1 VALUES(1, 2, 3, 4);
+ INSERT INTO t1 VALUES(4, 3, 2, 1);
+ INSERT INTO t1 VALUES(NULL, 4, NULL, 'abc');
+ INSERT INTO t1 VALUES(NULL, 5, NULL, 'AbC');
+ INSERT INTO t1 VALUES(NULL, 6, NULL, 'DEF');
+ INSERT INTO t1 VALUES(NULL, 7, NULL, 'def');
+ INSERT INTO t1 VALUES(NULL, 8, '456', 11);
+ INSERT INTO t1 VALUES(NULL, 9, '016', 11);
+
+ INSERT INTO t2 VALUES('a', 'b', 'c');
+
+ INSERT INTO t3 VALUES('abc', 'b', '1');
+ INSERT INTO t3 VALUES('abc', 'c', '2');
+ INSERT INTO t3 VALUES('def', 'a', '3');
+ INSERT INTO t3 VALUES('DEF', 'b', '4');
+ INSERT INTO t3 VALUES('aBc', 'd', '5');
+ INSERT INTO t3 VALUES('ABC', 'e', '6');
+}
+
+do_execsql_test -db db2 2.3 {
+ SELECT * FROM t1 WHERE c='000016'
+} {10 9 16 11}
+
+do_eqp_test 2.4 {
+ SELECT * FROM t1 WHERE c='000016'
+} {i5}
+
+do_execsql_test -db db2 2.5.1 {
+ INSERT INTO t4 VALUES('a', 'b', 'c');
+}
+do_execsql_test -db db2 2.5.2 {
+ INSERT INTO t4 VALUES(1, 2, 3);
+}
+do_execsql_test -db db2 2.5.3 {
+ SELECT * FROM log;
+} {
+ 1:a,b,c
+ 2:1,2,3
+}
+
+do_execsql_test -db db2 2.6.1 {
+ INSERT INTO x1 VALUES(1, 'ii');
+ INSERT INTO x2 VALUES(3, 'iv');
+ INSERT INTO x3 VALUES(5, 'vi');
+ SELECT * FROM xx;
+} {
+ 1 ii 3 iv 5 vi
+}
+
+do_execsql_test 2.integrity {
+ PRAGMA integrity_check
+} {ok}
+
+finish_test