@@ -5,6 +5,7 @@ const knex = require('knex');
55const path = require ( 'path' ) ;
66const util = require ( 'util' ) ;
77const _ = require ( './i18n' ) . text ;
8+ const exec = util . promisify ( cp . exec ) ;
89
910const NULL_MARKER = '__null__' ;
1011const SQL_ERROR = {
@@ -57,6 +58,12 @@ function typeOf(x) {
5758 return Object . prototype . toString . call ( x ) . slice ( 8 , - 1 ) . toLowerCase ( ) ;
5859}
5960
61+ async function sleep ( ms ) {
62+ return new Promise ( ( res , rej ) => {
63+ setTimeout ( ( ) => res ( ) , ms ) ;
64+ } ) ;
65+ }
66+
6067class SQLiteCasette {
6168 async newConnection ( options ) {
6269 const dbfile = ( options || { } ) . file || 'db.sqlite' ;
@@ -71,6 +78,9 @@ class SQLiteCasette {
7178 useNullAsDefault : true
7279 } ) ;
7380 }
81+ rows ( result ) {
82+ return result ;
83+ }
7484 explainSql ( s ) {
7585 return `EXPLAIN QUERY PLAN ${ s } ` ;
7686 }
@@ -131,13 +141,6 @@ class SQLiteCasette {
131141
132142class PostgreSQLCasette {
133143 async newConnection ( options ) {
134- const exec = util . promisify ( cp . exec ) ;
135- async function sleep ( ms ) {
136- return new Promise ( ( res , rej ) => {
137- setTimeout ( ( ) => res ( ) , ms ) ;
138- } ) ;
139- }
140-
141144 async function startServer ( ) {
142145 return new Promise ( ( _res , _rej ) => {
143146 let done = false ;
@@ -223,6 +226,9 @@ class PostgreSQLCasette {
223226 }
224227 throw Error ( "Failed to start PostgreSQL server" ) ;
225228 }
229+ rows ( result ) {
230+ return result . rows ;
231+ }
226232 explainSql ( s ) {
227233 return `EXPLAIN ${ s } ` ;
228234 }
@@ -262,8 +268,81 @@ class PostgreSQLCasette {
262268 }
263269}
264270
271+ class MySQLCassette {
272+ async newConnection ( options ) {
273+ for ( let retries = 10 ; retries > 0 ; retries -- ) {
274+ try {
275+ exec ( 'service mysql start' ) ;
276+ const conn = knex ( {
277+ client : 'mysql' ,
278+ connection : {
279+ host : '127.0.0.1' ,
280+ port : 3306 ,
281+ user : 'track' ,
282+ password : 'password' ,
283+ database : 'track' ,
284+ } ,
285+ } ) ;
286+ await conn . raw ( "SELECT 1" ) ;
287+ if ( options && options . clean ) {
288+ try {
289+ let tables = await conn . raw ( "SHOW TABLES" ) ;
290+ for ( let table of tables [ 0 ] ) {
291+ await conn . raw ( `DROP TABLE IF EXISTS ${ table . Tables_in_track } CASCADE` ) ;
292+ }
293+ } catch ( e2 ) { console . error ( e2 ) ; }
294+ }
295+ return conn ;
296+ } catch ( e ) {
297+ await sleep ( 500 ) ;
298+ }
299+ }
300+ throw Error ( "Failed to start MySQL server" ) ;
301+ }
302+ rows ( result ) {
303+ return result [ 0 ] ;
304+ }
305+ explainSql ( s ) {
306+ return `EXPLAIN ${ s } ` ;
307+ }
308+ tableSchemaSql ( ) {
309+ return `
310+ SELECT
311+ ordinal_position AS "order",
312+ column_name AS name,
313+ data_type AS raw_type
314+ FROM information_schema.columns
315+ WHERE
316+ table_schema = 'track' AND
317+ table_name = ?
318+ ORDER BY ordinal_position
319+ ` ;
320+ }
321+ updateAutoIncrementSql ( table , column ) {
322+ return `ALTER TABLE ${ table } AUTO_INCREMENT = ? + 1` ;
323+ }
324+ lastValueSql ( table , idCol ) {
325+ return `SELECT * FROM ${ table } WHERE ${ idCol } = LAST_INSERT_ID()` ;
326+ }
327+ async listFk ( table , conn ) {
328+ return await conn . query ( `
329+ SELECT
330+ kcu.column_name AS column,
331+ ccu.table_name AS foreign_table,
332+ ccu.column_name AS foreign_column
333+ FROM information_schema.table_constraints AS tc
334+ INNER JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
335+ INNER JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
336+ WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = '${ table } '
337+ ` ) ;
338+ }
339+ errorOf ( e ) {
340+ return [ SQL_ERROR . UNKNOWN ] ;
341+ }
342+ }
343+
265344/**
266- * Handles SQLite connection
345+ * Handles connection
267346 */
268347class Connection {
269348 static timeout ( client , extra ) {
@@ -272,6 +351,9 @@ class Connection {
272351 case "postgres" :
273352 case "postgresql" :
274353 return 12000 + ( extra || 0 ) ;
354+ case "my" :
355+ case "mysql" :
356+ return 12000 + ( extra || 0 ) ;
275357 default :
276358 return 2000 + ( extra || 0 ) ;
277359 }
@@ -284,6 +366,10 @@ class Connection {
284366 case "postgresql" :
285367 casette = new PostgreSQLCasette ( options ) ;
286368 break ;
369+ case "my" :
370+ case "mysql" :
371+ casette = new MySQLCassette ( options ) ;
372+ break ;
287373 default :
288374 casette = new SQLiteCasette ( options ) ;
289375 break ;
@@ -417,24 +503,19 @@ class Connection {
417503 * @returns {Promise<Array<object>?> }
418504 */
419505 async query ( sql , opt_args ) {
420-
421- function rows ( records ) {
422- return ! ! records . rows ? records . rows /* postgres */ : records /* sqlite */
423- }
424-
425506 if ( sql . trim ( ) . length === 0 ) {
426507 throw 'Empty query' ;
427508 }
428509
429510 const isSelectStatement = / ^ \s * S E L E C T / i. test ( sql ) ;
430511 if ( isSelectStatement ) {
431- const count = rows ( await this . _conn . raw ( `SELECT count(1) AS count FROM (${ sql . replace ( / ; \s * $ / , "" ) } ) AS x` , opt_args ) ) . length ;
512+ const count = this . _cassette . rows ( await this . _conn . raw ( `SELECT count(1) AS count FROM (${ sql . replace ( / ; \s * $ / , "" ) } ) AS x` , opt_args ) ) . length ;
432513 if ( count > ( this . _options . maxRows || 10000 ) ) {
433514 throw 'Too many records' ;
434515 }
435516 }
436517
437- return rows ( await this . _conn . raw ( sql , opt_args ) ) ;
518+ return this . _cassette . rows ( await this . _conn . raw ( sql , opt_args ) ) ;
438519 }
439520
440521 /**
0 commit comments