var mysql = require('mysql'); const loopbackConnector = require('loopback-connector'); const SqlConnector = loopbackConnector.SqlConnector; const ParameterizedSQL = loopbackConnector.ParameterizedSQL; var MySQL = require('loopback-connector-mysql').MySQL; var EnumFactory = require('loopback-connector-mysql').EnumFactory; var debug = require('debug')('loopback-connector-sql'); exports.initialize = function(dataSource, callback) { dataSource.driver = mysql; dataSource.connector = new VnMySQL(dataSource.settings); dataSource.connector.dataSource = dataSource; var modelBuilder = dataSource.modelBuilder; var defineType = modelBuilder.defineValueType ? modelBuilder.defineValueType.bind(modelBuilder) : modelBuilder.constructor.registerType.bind(modelBuilder.constructor); defineType(function Point() {}); dataSource.EnumFactory = EnumFactory; if (callback) { if (dataSource.settings.lazyConnect) { process.nextTick(function() { callback(); }); } else { dataSource.connector.connect(callback); } } }; exports.VnMySQL = VnMySQL; function VnMySQL(settings) { SqlConnector.call(this, 'mysql', settings); } VnMySQL.prototype = Object.create(MySQL.prototype); VnMySQL.constructor = VnMySQL; VnMySQL.prototype.toColumnValue = function(prop, val) { if (val == null || !prop || prop.type !== Date) return MySQL.prototype.toColumnValue.call(this, prop, val); val = new Date(val); return val.getFullYear() + '-' + fillZeros(val.getMonth() + 1) + '-' + fillZeros(val.getDate()) + ' ' + fillZeros(val.getHours()) + ':' + fillZeros(val.getMinutes()) + ':' + fillZeros(val.getSeconds()); function fillZeros(v) { return v < 10 ? '0' + v : v; } }; /** * Private make method * @param {Object} model Model instance * @param {Object} where Where filter * @return {ParameterizedSQL} Parametized object */ VnMySQL.prototype._makeWhere = function(model, where) { let columnValue; let sqlExp; if (!where) { return new ParameterizedSQL(''); } if (typeof where !== 'object' || Array.isArray(where)) { debug('Invalid value for where: %j', where); return new ParameterizedSQL(''); } var self = this; var props = self.getModelDefinition(model).properties; var whereStmts = []; for (var key in where) { var stmt = new ParameterizedSQL('', []); // Handle and/or operators if (key === 'and' || key === 'or') { var branches = []; var branchParams = []; var clauses = where[key]; if (Array.isArray(clauses)) { for (var i = 0, n = clauses.length; i < n; i++) { var stmtForClause = self._makeWhere(model, clauses[i]); if (stmtForClause.sql) { stmtForClause.sql = '(' + stmtForClause.sql + ')'; branchParams = branchParams.concat(stmtForClause.params); branches.push(stmtForClause.sql); } } stmt.merge({ sql: branches.join(' ' + key.toUpperCase() + ' '), params: branchParams }); whereStmts.push(stmt); continue; } // The value is not an array, fall back to regular fields } var p = props[key]; // eslint-disable one-var var expression = where[key]; var columnName = self.columnEscaped(model, key); // eslint-enable one-var if (expression === null || expression === undefined) { stmt.merge(columnName + ' IS NULL'); } else if (expression && expression.constructor === Object) { var operator = Object.keys(expression)[0]; // Get the expression without the operator expression = expression[operator]; if (operator === 'inq' || operator === 'nin' || operator === 'between') { columnValue = []; if (Array.isArray(expression)) { // Column value is a list for (var j = 0, m = expression.length; j < m; j++) { columnValue.push(this.toColumnValue(p, expression[j])); } } else { columnValue.push(this.toColumnValue(p, expression)); } if (operator === 'between') { // BETWEEN v1 AND v2 var v1 = columnValue[0] === undefined ? null : columnValue[0]; var v2 = columnValue[1] === undefined ? null : columnValue[1]; columnValue = [v1, v2]; } else { // IN (v1,v2,v3) or NOT IN (v1,v2,v3) if (columnValue.length === 0) { if (operator === 'inq') { columnValue = [null]; } else { // nin () is true continue; } } } } else if (operator === 'regexp' && expression instanceof RegExp) { // do not coerce RegExp based on property definitions columnValue = expression; } else { columnValue = this.toColumnValue(p, expression); } sqlExp = self.buildExpression(columnName, operator, columnValue, p); stmt.merge(sqlExp); } else { // The expression is the field value, not a condition columnValue = self.toColumnValue(p, expression); if (columnValue === null) { stmt.merge(columnName + ' IS NULL'); } else if (columnValue instanceof ParameterizedSQL) { stmt.merge(columnName + '=').merge(columnValue); } else { stmt.merge({ sql: columnName + '=?', params: [columnValue] }); } } whereStmts.push(stmt); } var params = []; var sqls = []; for (var k = 0, s = whereStmts.length; k < s; k++) { sqls.push(whereStmts[k].sql); params = params.concat(whereStmts[k].params); } var whereStmt = new ParameterizedSQL({ sql: sqls.join(' AND '), params: params }); return whereStmt; }; /** * Build the SQL WHERE clause for the where object * @param {string} model Model name * @param {object} where An object for the where conditions * @return {ParameterizedSQL} The SQL WHERE clause */ VnMySQL.prototype.makeWhere = function(model, where) { var whereClause = this._makeWhere(model, where); if (whereClause.sql) { whereClause.sql = 'WHERE ' + whereClause.sql; } return whereClause; };