UNPKG

caminte

Version:

ORM for every database: redis, mysql, neo4j, mongodb, rethinkdb, postgres, sqlite, tingodb

524 lines (477 loc) 18.1 kB
/** * Module dependencies */ var utils = require('../utils'); var safeRequire = utils.safeRequire; var fb = safeRequire('node-firebird'); var quote = function (value) { return '"' + value + '"'; }; exports.initialize = function initializeSchema(schema, callback) { 'use strict'; if (!fb) { return; } var options = {}, s = schema.settings; options.host = s.host; options.port = s.port; options.database = s.database || 'test.fdb'; options.user = s.username || 'sysdba'; options.password = s.password || 'aea0be33'; schema.adapter = new FB(schema, s); if (s.pool) { fb.pool(s.pool, options) .get(function (err, client) { if (!err) { schema.adapter.client = client; console.log('FB', typeof client.execute) process.nextTick(function () { callback(); }); } else { console.error(err); throw new Error(err); } }); } else { fb.attachOrCreate(options, function (err, client) { if (!err) { schema.adapter.client = client; console.log('FB', typeof client.execute) process.nextTick(function () { callback(); }); } else { console.error(err); throw new Error(err); } } ); } }; function FB(schema, s) { this.name = 'firebird'; this._models = {}; this.collections = {}; this.client = {}; this.schema = schema; this.s = s; } FB.prototype.define = function (descr) { if (!descr.settings) { descr.settings = {}; } this._models[descr.model.modelName] = descr; }; FB.prototype.autoupdate = function (callback) { var self = this; self.client.execute('SELECT a.RDB$RELATION_NAME FROM RDB$RELATIONS a' + ' WHERE RDB$SYSTEM_FLAG = 0 AND RDB$RELATION_TYPE = 0', function (err, relations) { if (err) { console.log('err', err) } var tables = ((relations || [])[0] || []).map(function (item) { return (item || '').replace(/^\s+|\s+$/, ''); }); var len = Object.keys(self._models).length; Object.keys(self._models).forEach(function (name) { if (tables.indexOf(name) === -1) { var table = self.schema.tableName(name); var model = self._models[name]; self.client.startTransaction(function (err, tr) { var sql = 'CREATE TABLE ' + quote(table) + '(\n' + ' "id" INTEGER NOT NULL,\n'; Object.keys(model.properties).forEach(function (field) { var str; if (field === 'id') { return; } var f = model.properties[field]; switch (f.type.name) { case 'String': str = 'Varchar(' + (f.length || f.limit || 255) + ')'; break; case 'Number': str = 'Double precision'; break; case 'Date': str = 'Timestamp'; break; case 'Boolean': str = 'Smallint'; break; default: str = 'Blob sub_type 1'; } sql += ' ' + quote(field) + ' ' + str + (f.allowNull === false || f['null'] === false ? ' NOT NULL,' : ',') + '\n'; } ); sql += ' PRIMARY KEY ("id"))'; console.log('sql:', sql) tr.execute(sql, function (err) { if (!err) { var sequence = quote(table + '_SEQ'); tr.execute('create generator ' + sequence); tr.execute('set generator ' + sequence + ' to 0'); tr.execute( 'CREATE TRIGGER ' + quote(table + '_BI') + ' FOR ' + quote(table) + '\n' + 'ACTIVE BEFORE INSERT POSITION 0\n' + 'AS\n' + 'BEGIN\n' + ' IF (NEW."id" IS NULL) THEN\n' + ' NEW."id" = GEN_ID(' + sequence + ', 1);\n' + 'END', function () { if (--len === 0) { tr.commit(callback); } }); } else { if (--len === 0) { tr.rollback(callback); } } }); }); } else { // TODO actualise if (--len === 0) { console.log('autoupdate end') callback(); } } }); }); }; FB.prototype.automigrate = function (cb) { var wait = 0; var self = this; this.client.startTransaction(function (err, tr) { Object.keys(self._models).forEach( function (name) { var table = self.schema.tableName(name); var model = self._models[name]; wait += 1; var sql = 'RECREATE TABLE ' + quote(table) + '(\n' + ' "id" INTEGER NOT NULL,\n'; Object.keys(model.properties).forEach( function (field) { var str; if (field === 'id') return; var f = model.properties[field]; switch (f.type.name) { case 'String': str = 'Varchar(' + (f.length || 255) + ')'; break; case 'Number': str = 'Double precision'; break; case 'Date': str = 'Timestamp'; break; case 'Boolean': str = 'Smallint'; break; default: str = 'Blob sub_type 1'; } sql += ' ' + quote(field) + ' ' + str + (f.allowNull === false || f['null'] === false ? ' NOT NULL,' : ',') + '\n'; } ); sql += ' PRIMARY KEY ("id"))'; tr.execute(sql, function (err) { if (!err) { var sequence = quote(table + '_SEQ'); tr.execute('create generator ' + sequence); tr.execute('set generator ' + sequence + ' to 0'); tr.execute( 'CREATE TRIGGER ' + quote(table + '_BI') + ' FOR ' + quote(table) + '\n' + 'ACTIVE BEFORE INSERT POSITION 0\n' + 'AS\n' + 'BEGIN\n' + ' IF (NEW."id" IS NULL) THEN\n' + ' NEW."id" = GEN_ID(' + sequence + ', 1);\n' + 'END', done); } else { done(err); } }); } ); if (wait === 0) { cb(); } function done(err) { if (err) { tr.rollback(cb); } else { if (--wait === 0) { tr.commit(cb); } } } } ); }; FB.prototype.create = function (name, data, callback) { var table = this.schema.tableName(name); var sql = 'INSERT INTO ' + quote(table); var fields = []; var values = []; var params = []; Object.keys(data).forEach( function (key) { if (key === 'id') return; fields.push(quote(key)); values.push('?'); params.push(data[key]); } ); if (fields.length) { sql += ' (' + fields.join(',') + ') VALUES (' + values.join(',') + ')'; } else { sql += ' VALUES ()'; } sql += ' RETURNING "id"'; this.client.execute(sql, params, function (err, result) { callback(err, (result) ? result[0] : undefined); } ); }; FB.prototype.destroy = function destroy(name, id, callback) { if (id) { var table = this.schema.tableName(name); var sql = 'DELETE FROM ' + quote(table) + ' WHERE "id" = ?'; this.client.execute(sql, id, callback); } else { callback('nothing to destroy'); } }; FB.prototype.save = function (name, data, callback) { var table = this.schema.tableName(name); var sql = 'UPDATE ' + quote(table) + ' SET '; var fields = []; var params = []; var model = this._models[name]; Object.keys(data).forEach( function (key) { if (key === 'id') return; fields.push(quote(key) + ' = ?'); if ((data[key]) && (model.properties[key].type.name === 'Date')) { params.push(new Date(data[key])); } else { params.push(data[key]); } } ); sql += fields.join(',') + ' WHERE "id"=?'; params.push(data.id); this.client.execute(sql, params, callback); }; FB.prototype.findById = function findById(name, id, callback) { var table = this.schema.tableName(name); var sql = 'SELECT FIRST 1 * FROM ' + quote(table) + ' WHERE "id" = ?'; this.client.query(sql, id, function (err, result) { callback(err, (result && result.length === 1) ? result[0] : undefined); } ); }; FB.prototype.all = function (name, filter, callback) { if ('function' === typeof filter) { callback = filter; filter = {}; } if (!filter) { filter = {}; } var table = this.schema.tableName(name); var sql = '* FROM ' + quote(table); var params = []; if (filter) { var self = this; if (filter.where) { sql += ' ' + buildWhere(filter.where); } if (filter.order) { sql += ' ' + buildOrderBy(filter.order); } if (filter.limit) { sql = buildLimit(filter.limit, filter.offset || 0) + ' ' + sql; } } this.client.query('SELECT ' + sql, params, callback); function buildWhere(conds) { var cs = []; var props = self._models[name].properties; Object.keys(conds).forEach( function (key) { var keyEscaped = quote(key); var val = conds[key]; var lst, i; if (conds[key] === null) { cs.push(keyEscaped + ' IS NULL'); } else if (conds[key].constructor.name === 'Object') { switch (Object.keys(conds[key])[0]) { case 'gt': cs.push(keyEscaped + ' > ?'); params.push(val.gt); break; case 'gte': cs.push(keyEscaped + ' >= ?'); params.push(val.gte); break; case 'lt': cs.push(keyEscaped + ' < ?'); params.push(val.lt); break; case 'lte': cs.push(keyEscaped + ' <= ?'); params.push(val.lte); break; case 'between': cs.push(keyEscaped + ' BETWEEN ? AND ?'); params.push(val.between[0]); params.push(val.between[1]); break; case 'in': case 'inq': if (val.inq instanceof Array) { lst = new Array(val.inq.length); for (i = 0; i < val.inq.length; i++) { lst[i] = '?'; params.push(val.inq[i]); } } else { lst = [val.inq]; params.push(val.inq); } cs.push(keyEscaped + ' IN (' + lst.join(',') + ')'); break; case 'nin': if (val.nin instanceof Array) { lst = new Array(val.nin.length); for (i = 0; i < val.nin.length; i++) { lst[i] = '?'; params.push(val.nin[i]); } } else { lst = [val.nin]; params.push(val.nin); } cs.push(keyEscaped + ' NOT IN (' + lst.join(',') + ')'); break; case 'ne': case 'neq': cs.push(keyEscaped + ' != ?'); params.push(val.neq); break; case 'regexp': cs.push(keyEscaped + ' REGEXP ?'); params.push(val.lte); break; } } else { cs.push(keyEscaped + ' = ?'); params.push(val); } } ); if (cs.length === 0) { return ''; } return 'WHERE ' + cs.join(' AND '); } function buildOrderBy(order) { if (typeof order === 'string') { order = order.split(' '); order[0] = [quote(order[0])]; return 'ORDER BY ' + order.join(' '); } else { for (var i = 0; i < order.length; i++) { order[i] = quote(order[i]); } return 'ORDER BY ' + order.join(', '); } } function buildLimit(limit, offset) { var ret = 'FIRST ' + limit; if (offset) { ret += ' SKIP ' + offset; } return ret; } }; FB.prototype.destroyAll = function (name, callback) { var table = this.schema.tableName(name); var sql = 'DELETE FROM ' + quote(table); this.client.query(sql, callback); }; FB.prototype.count = function count(name, callback, where) { var table = this.schema.tableName(name); var params = []; var model = this._models[name]; this.client.execute('SELECT count(*) FROM ' + quote(table) + buildWhere(where), params, function (err, result) { callback(err, (result) ? result[0][0] : undefined); } ); function buildWhere(conds) { var cs = []; Object.keys(conds || {}).forEach( function (key) { if (conds[key] === null) { cs.push(quote(key) + ' IS NULL'); } else { cs.push(quote(key) + ' = ?'); if (model.properties[key].type.name === 'Date') { params.push(new Date(conds[key])); } else { params.push(conds[key]); } } } ); return cs.length ? ' WHERE ' + cs.join(' AND ') : ''; } }; FB.prototype.exists = function count(name, id, callback) { var table = this.schema.tableName(name); var sql = 'SELECT FIRST 1 "id" FROM ' + quote(table) + ' WHERE "id" = ?'; this.client.execute(sql, id, function (err, data) { callback(err, (data) ? data.length === 1 : undefined); } ); }; FB.prototype.updateAttributes = function updateAttrs(model, id, data, cb) { data.id = id; this.save(model, data, cb); }; FB.prototype.updateOrCreate = function (name, data, callback) { var table = this.schema.tableName(name); var sql = 'UPDATE OR INSERT INTO ' + quote(table); var fields = []; var values = []; var params = []; Object.keys(data).forEach( function (key) { fields.push(quote(key)); values.push('?'); params.push(data[key]); } ); if (fields.length) { sql += ' (' + fields.join(',') + ') VALUES (' + values.join(',') + ')'; } else { sql += ' VALUES ()'; } this.client.execute(sql, params, function (err) { callback(err, data); } ); };