UNPKG

angie-orm

Version:

A Feature-Complete Database Relationship Manager Designed for NodeJS

205 lines (188 loc) 6.95 kB
/** * @module SqliteConnection.js * @author Joe Groseclose <@benderTheCrime> * @date 8/23/2015 */ // System Modules import fs from 'fs'; import sqlite3 from 'sqlite3'; import {cyan, magenta, gray} from 'chalk'; import $LogProvider from 'angie-log'; // Angie Modules import BaseDBConnection, { $$DatabaseConnectivityError } from './BaseDBConnection'; import { $$InvalidDatabaseConfigError } from '../util/$ExceptionsProvider'; sqlite3.verbose(); const p = process; $LogProvider.sqliteInfo = $LogProvider.info.bind(null, 'Sqlite'); class SqliteConnection extends BaseDBConnection { constructor(database, destructive, dryRun) { super(database, destructive, dryRun); let db = this.database; if (!db.name) { throw new $$InvalidDatabaseConfigError(); } this.name = this.database.name || this.database.alias; } types(field) { let type = field.type; if (!type) { return; } switch (type) { case 'CharField': return `TEXT${field.nullable ? ' NOT NULL' : ''}`; // TODO support different size integers: TINY, SMALL, MEDIUM case 'IntegerField': return `INTEGER${field.nullable ? ' NOT NULL' : ''}`; case 'KeyField': return `INTEGER${field.nullable ? ' NOT NULL' : ''}`; case 'ForeignKeyField': return `INTEGER REFERENCES ${field.rel}(id)`; default: return undefined; } } connect() { let db = this.database; if (!this.connection) { try { this.connection = new sqlite3.Database(this.name); $LogProvider.sqliteInfo('Connection successful'); } catch(err) { throw new $$DatabaseConnectivityError(db); } } return this.connection; } disconnect() { this.connect().close(); return this; } query(query, model, key) { let me = this, name = this.name; return new Promise(function(resolve) { $LogProvider.sqliteInfo(`Query: ${cyan(name)}: ${magenta(query)}`); me.connect().parallelize(function() { me.connection[ key ](query, function(e, rows = []) { if (e) { $LogProvider.warn(e); } resolve([ rows, e ]); }); }); }).then(function(args) { return me.$$queryset(model, query, args[0], args[1]); }); } run(query, model) { return this.query(query, model, 'run'); } all() { const query = super.all.apply(this, arguments); return this.query(query, arguments[0].model, 'all'); } create() { const query = super.create.apply(this, arguments); return this.query(query, arguments[0].model, 'all'); } delete() { const query = super.delete.apply(this, arguments); return this.query(query, arguments[0].model, 'all'); } update() { const query = super.update.apply(this, arguments); return this.query(query, arguments[0].model, 'all'); } raw(query, model) { return this.query(query, model, 'all'); } sync() { let me = this; super.sync().then(function() { let proms = []; // TODO test this in another directory if (!fs.existsSync(me.name)) { // Connection does not exist and we must touch the db file fs.closeSync(fs.openSync(me.name, 'w')); } let models = me.models(); for (let model in models) { // Fetch models and get model name let instance = models[ model ], modelName = instance.name || instance.alias || me.$$name(instance); // Run a table creation with an ID for each table proms.push(me.run( `CREATE TABLE ${modelName} ` + '(id INTEGER PRIMARY KEY AUTOINCREMENT);', modelName )); } return Promise.all(proms).then(function() { return me.migrate(); }); }); } migrate() { let me = this; return super.migrate().then(function() { let models = me.models(), proms = [], logged = true; for (let key in models) { const model = models[ key ], modelName = me.$$name(model.name || model.alias), fields = model.$fields(); if (me.destructive && logged) { // TODO recommmend that the user copy the table over // without the column, delete the original, and // rename the table $LogProvider.error( 'You have specified a destructive Migration and ' + `have fields in the ${cyan(modelName)} model ` + 'which do not exist in your app.Model code. ' + 'However, sqlite3 destructive migrations are not ' + 'supported. Please see the docs for more info.' ); logged = false; } fields.forEach(function(v) { if (model[ v ].type === 'ManyToManyField') { return; } let query, $default; if (model[ v ].default) { $default = model[ v ].default; if (typeof $default === 'function') { $default = $default(); } } query = `ALTER TABLE ${modelName} ADD COLUMN ${v} ` + `${me.types(model[ v ])}` + `${model[ v ].unique ? ' UNIQUE' : ''}` + `${$default ? ` DEFAULT '${$default}'` : ''};`; if (!me.dryRun) { proms.push(me.run(query)); } else { $LogProvider.sqliteInfo(`Dry Run Query: ${gray(query)}`); } }); } return Promise.all(proms); }).then(function() { me.disconnect(); $LogProvider.sqliteInfo( `Successfully Synced & Migrated ${cyan(me.name)}` ); p.exit(0); }); } } export default SqliteConnection;