@@ -22,6 +22,8 @@ const {sequence} = require('@tryghost/promise');
2222const urlServiceUtils = require ( './url-service-utils' ) ;
2323
2424let dbInitialized = false ;
25+ let mysqlSnapshotDatabase = null ;
26+ const mysqlSnapshotTablePrefix = '__ghost_snapshot_' ;
2527
2628/**
2729 * Checks if the current active connection is a MySQL database
@@ -69,11 +71,11 @@ module.exports.reset = async ({truncate} = {truncate: false}) => {
6971 if ( truncate ) {
7072 // Perform a fast reset by tearing down all the tables and inserting the fixtures
7173 try {
72- await truncateAll ( ) ;
73- await knexMigrator . init ( { only : 3 } ) ;
74+ await resetMySQLFromSnapshot ( ) ;
7475 } catch ( err ) {
7576 // If it fails, try a normal restore
7677 await forceReinit ( ) ;
78+ await createMySQLSnapshot ( ) ;
7779 }
7880 } else {
7981 // Do a full database reset + initialisation
@@ -93,6 +95,8 @@ module.exports.teardown = async () => {
9395 } catch ( err ) {
9496 await knexMigrator . reset ( { force : true } ) ;
9597 }
98+
99+ await dropMySQLSnapshots ( ) ;
96100} ;
97101
98102/**
@@ -124,6 +128,96 @@ module.exports.truncate = async (tableName) => {
124128const forceReinit = async ( ) => {
125129 await knexMigrator . reset ( { force : true } ) ;
126130 await knexMigrator . init ( ) ;
131+ await dropMySQLSnapshots ( ) ;
132+ } ;
133+
134+ const getResetTables = ( ) => {
135+ return schemaTables . concat ( [ 'migrations' ] ) ;
136+ } ;
137+
138+ const getMySQLSnapshotTableName = ( table ) => {
139+ return `${ mysqlSnapshotTablePrefix } ${ table } ` ;
140+ } ;
141+
142+ const getMySQLDatabaseName = ( ) => {
143+ return config . get ( 'database:connection:database' ) ;
144+ } ;
145+
146+ const isMySQLSnapshotCurrent = ( ) => {
147+ return mysqlSnapshotDatabase === getMySQLDatabaseName ( ) ;
148+ } ;
149+
150+ const resetMySQLFromSnapshot = async ( ) => {
151+ if ( ! isMySQLSnapshotCurrent ( ) ) {
152+ await truncateAll ( ) ;
153+ await knexMigrator . init ( { only : 3 } ) ;
154+ await createMySQLSnapshot ( ) ;
155+ return ;
156+ }
157+
158+ await restoreMySQLSnapshot ( ) ;
159+ } ;
160+
161+ const createMySQLSnapshot = async ( ) => {
162+ if ( ! module . exports . isMySQL ( ) ) {
163+ return ;
164+ }
165+
166+ const tables = getResetTables ( ) ;
167+
168+ await sequence ( tables . map ( table => async ( ) => {
169+ const snapshotTable = getMySQLSnapshotTableName ( table ) ;
170+
171+ await db . knex . schema . dropTableIfExists ( snapshotTable ) ;
172+ await db . knex . raw ( 'CREATE TABLE ?? LIKE ??' , [ snapshotTable , table ] ) ;
173+ await db . knex . raw ( 'INSERT INTO ?? SELECT * FROM ??' , [ snapshotTable , table ] ) ;
174+ } ) ) ;
175+
176+ mysqlSnapshotDatabase = getMySQLDatabaseName ( ) ;
177+ } ;
178+
179+ const restoreMySQLSnapshot = async ( ) => {
180+ debug ( 'Database snapshot restore' ) ;
181+ urlServiceUtils . reset ( ) ;
182+
183+ const tables = getResetTables ( ) ;
184+
185+ await db . knex . transaction ( async ( trx ) => {
186+ try {
187+ await db . knex . raw ( 'SET FOREIGN_KEY_CHECKS=0;' ) . transacting ( trx ) ;
188+
189+ await sequence ( tables . map ( table => async ( ) => {
190+ const snapshotTable = getMySQLSnapshotTableName ( table ) ;
191+
192+ await db . knex . raw ( 'DELETE FROM ??' , [ table ] ) . transacting ( trx ) ;
193+ await db . knex . raw ( 'INSERT INTO ?? SELECT * FROM ??' , [ table , snapshotTable ] ) . transacting ( trx ) ;
194+ } ) ) ;
195+ } finally {
196+ await db . knex . raw ( 'SET FOREIGN_KEY_CHECKS=1;' ) . transacting ( trx ) ;
197+ debug ( 'Database snapshot restore end' ) ;
198+ }
199+ } ) ;
200+ } ;
201+
202+ const dropMySQLSnapshots = async ( ) => {
203+ if ( ! module . exports . isMySQL ( ) ) {
204+ return ;
205+ }
206+
207+ mysqlSnapshotDatabase = null ;
208+
209+ try {
210+ await sequence ( getResetTables ( ) . map ( table => ( ) => {
211+ return db . knex . schema . dropTableIfExists ( getMySQLSnapshotTableName ( table ) ) ;
212+ } ) ) ;
213+ } catch ( err ) {
214+ // CASE: table does not exist || DB does not exist
215+ if ( err . errno === 1146 || err . errno === 1049 ) {
216+ return Promise . resolve ( ) ;
217+ }
218+
219+ throw err ;
220+ }
127221} ;
128222
129223/**
@@ -135,7 +229,7 @@ const truncateAll = async () => {
135229 debug ( 'Database teardown' ) ;
136230 urlServiceUtils . reset ( ) ;
137231
138- const tables = schemaTables . concat ( [ 'migrations' ] ) ;
232+ const tables = getResetTables ( ) ;
139233
140234 if ( module . exports . isSQLite ( ) ) {
141235 try {
0 commit comments