不要怂,就是干,撸起袖子干!

Commit d7aa81d1 by Jan Aagaard Meier

almost there

1 parent 8404668d
...@@ -131,7 +131,7 @@ module.exports = (function() { ...@@ -131,7 +131,7 @@ module.exports = (function() {
var definition = self.daoFactory.rawAttributes[attrName] var definition = self.daoFactory.rawAttributes[attrName]
, isHstore = !!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type , isHstore = !!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type
, isEnum = definition.type && (definition.type.toString() === DataTypes.ENUM.toString()) , isEnum = definition.type && (definition.type.toString() === DataTypes.ENUM.toString())
, isMySQL = self.daoFactory.daoFactoryManager.sequelize.options.dialect === "mysql" , isMySQL = ['mysql', 'mariadb'].indexOf(self.daoFactory.daoFactoryManager.sequelize.options.dialect) !== -1
, ciCollation = !!self.daoFactory.options.collate && self.daoFactory.options.collate.match(/_ci$/i) , ciCollation = !!self.daoFactory.options.collate && self.daoFactory.options.collate.match(/_ci$/i)
// Unfortunately for MySQL CI collation we need to map/lowercase values again // Unfortunately for MySQL CI collation we need to map/lowercase values again
......
...@@ -79,7 +79,7 @@ module.exports = (function() { ...@@ -79,7 +79,7 @@ module.exports = (function() {
connect.call(self, function (err, connection) { connect.call(self, function (err, connection) {
connection.queryType = 'read' connection.queryType = 'read'
done(null, connection) done(err, connection)
}, config); }, config);
}, },
destroy: function(client) { destroy: function(client) {
...@@ -94,7 +94,7 @@ module.exports = (function() { ...@@ -94,7 +94,7 @@ module.exports = (function() {
create: function (done) { create: function (done) {
connect.call(self, function (err, connection) { connect.call(self, function (err, connection) {
connection.queryType = 'write' connection.queryType = 'write'
done(null, connection) done(err, connection)
}, self.config.replication.write); }, self.config.replication.write);
}, },
destroy: function(client) { destroy: function(client) {
...@@ -231,7 +231,8 @@ module.exports = (function() { ...@@ -231,7 +231,8 @@ module.exports = (function() {
user: config.username, user: config.username,
password: config.password, password: config.password,
db: config.database, db: config.database,
ssl: config.ssl || undefined ssl: config.ssl || undefined,
metadata: true
// timezone: 'Z' // unsupported by mariasql // timezone: 'Z' // unsupported by mariasql
}) })
...@@ -241,12 +242,14 @@ module.exports = (function() { ...@@ -241,12 +242,14 @@ module.exports = (function() {
this.isConnecting = false this.isConnecting = false
done(null, connection) done(null, connection)
}).on('error', function() { }).on('error', function(err) {
disconnect.call(self, connection) this.isConnecting = false
done(err)
// disconnect.call(self, connection)
}).on('close', function() { }).on('close', function() {
disconnect.call(self, connection) disconnect.call(self, connection)
}) })
} }
var enqueue = function(queueItem, options) { var enqueue = function(queueItem, options) {
......
...@@ -25,54 +25,92 @@ module.exports = (function() { ...@@ -25,54 +25,92 @@ module.exports = (function() {
var resultSet = [], var resultSet = [],
errorDetected = false, errorDetected = false,
alreadyEnded = false, // This is needed because CALL queries emit 'end' twice...
self = this self = this
this.client.query(this.sql) this.client.query(this.sql)
.on('result', function(results) { .on('result', function(results) {
results
results.on('row', function(row) { .on('row', function(row, metadata) {
// iterate through each property to convert for (var prop in row) {
// strings into JS objects when possible if (row.hasOwnProperty(prop)) {
for(var prop in row) { if (row[prop] === null) {
// take care of properties that shouldn't be strings continue
if( row[prop] == null) { }
continue; // don't mess with null
} else if( parseIso8601.call(this, row[prop]) ) { type = metadata.types[prop]
// if string can be converted into a JS date, it probably
// is a JS date -- replace the string with the date switch (type) {
row[prop] = parseIso8601.call(this, row[prop] + 'Z') case "TINYINT":
} else if( !isNaN(row[prop]) ) { case "SMALLINT":
// take care of strings that are really numbers case "INTEGER":
row[prop] = +(row[prop]); case "MEDIUMINT":
} case "BIGINT":
} case "YEAR":
resultSet.push(row) row[prop] = parseInt(row[prop], 10)
}) break
.on('error', function(err) { case "DECIMAL":
errorDetected = true case "FLOAT":
self.emit('sql', this.sql) case "DOUBLE":
self.emit('error', err, this.callee) row[prop] = parseFloat(row[prop])
}.bind(this)) break
.on('end', function(info) { case "DATE":
if(errorDetected) { case "TIMESTAMP":
return case "DATETIME":
} row[prop] = new Date(row[prop] + 'Z')
self.emit('sql', this.sql) break;
// we need to figure out whether to send the result set case "BIT":
// or info depending upon the type of query case "BLOB":
if( /^show/.test(this.sql.toLowerCase()) || case "TINYBLOB":
/^select/.test(this.sql.toLowerCase()) || case "MEDIUMBLOB":
/^describe/.test(this.sql.toLowerCase()) || case "LONGBLOB":
( resultSet.length && /^call/.test(this.sql.toLowerCase()) ) ) { if (metadata.charsetNrs[prop] === 63) { // binary
self.emit('success', this.formatResults(resultSet)) row[prop] = new Buffer(row[prop])
} else { }
self.emit('success', this.formatResults(info)) break;
} case "TIME":
case "CHAR":
}.bind(this)); case "VARCHAR":
case "SET":
case "ENUM":
case "GEOMETRY":
case "NULL":
break;
default:
// blank
}
}
}
resultSet.push(row)
})
.on('error', function(err) {
errorDetected = true
self.emit('sql', this.sql)
self.emit('error', err, this.callee)
}.bind(this))
.on('end', function(info) {
if (alreadyEnded || errorDetected) {
return
}
alreadyEnded = true
self.emit('sql', this.sql)
// we need to figure out whether to send the result set
// or info depending upon the type of query
if (/^call/.test(this.sql.toLowerCase())) {
self.emit('success', resultSet)
} else if( /^show/.test(this.sql.toLowerCase()) ||
/^select/.test(this.sql.toLowerCase()) ||
/^describe/.test(this.sql.toLowerCase())) {
self.emit('success', this.formatResults(resultSet))
} else {
self.emit('success', this.formatResults(info))
}
}.bind(this));
}.bind(this)) }.bind(this))
.on('error', function(err) { .on('error', function(err) {
if(errorDetected) { if (errorDetected) {
return return
} }
errorDetected = true errorDetected = true
...@@ -80,74 +118,12 @@ module.exports = (function() { ...@@ -80,74 +118,12 @@ module.exports = (function() {
self.emit('error', err, this.callee) self.emit('error', err, this.callee)
}.bind(this)) }.bind(this))
.on('end', function(info) { .on('end', function(info) {
// nothing here (query info is returned during the 'result' event) // nothing here (query info is returned during the 'result' event)
}.bind(this)).setMaxListeners(100)
}.bind(this)).setMaxListeners(100)
return this return this
} }
var parseIso8601 = function(CurDate) {
// note -- this function is borrowed from the following author
// Author: Jim Davis, The Depressed Press of Boston
// Library: DP_DateExtensions
// Website: www.depressedpress.com
// Check the input parameters
if ( typeof CurDate != "string" || CurDate == "" ) {
return null;
};
// Set the fragment expressions
var S = "[\\-/:.]";
var Yr = "((?:1[6-9]|[2-9][0-9])[0-9]{2})";
var Mo = S + "((?:1[012])|(?:0[1-9])|[1-9])";
var Dy = S + "((?:3[01])|(?:[12][0-9])|(?:0[1-9])|[1-9])";
var Hr = "(2[0-4]|[01]?[0-9])";
var Mn = S + "([0-5]?[0-9])";
var Sd = "(?:" + S + "([0-5]?[0-9])(?:[.,]([0-9]+))?)?";
var TZ = "(?:(Z)|(?:([\+\-])(1[012]|[0]?[0-9])(?::?([0-5]?[0-9]))?))?";
// RegEx the input
// First check: Just date parts (month and day are optional)
// Second check: Full date plus time (seconds, milliseconds and TimeZone info are optional)
var TF;
if ( TF = new RegExp("^" + Yr + "(?:" + Mo + "(?:" + Dy + ")?)?" + "$").exec(CurDate) ) {} else if ( TF = new RegExp("^" + Yr + Mo + Dy + "[Tt ]" + Hr + Mn + Sd + TZ + "$").exec(CurDate) ) {};
// If the date couldn't be parsed, return null
if ( !TF ) { return null };
// Default the Time Fragments if they're not present
if ( !TF[2] ) { TF[2] = 1 } else { TF[2] = TF[2] - 1 };
if ( !TF[3] ) { TF[3] = 1 };
if ( !TF[4] ) { TF[4] = 0 };
if ( !TF[5] ) { TF[5] = 0 };
if ( !TF[6] ) { TF[6] = 0 };
if ( !TF[7] ) { TF[7] = 0 };
if ( !TF[8] ) { TF[8] = null };
if ( TF[9] != "-" && TF[9] != "+" ) { TF[9] = null };
if ( !TF[10] ) { TF[10] = 0 } else { TF[10] = TF[9] + TF[10] };
if ( !TF[11] ) { TF[11] = 0 } else { TF[11] = TF[9] + TF[11] };
// If there's no timezone info the data is local time
if ( !TF[8] && !TF[9] ) {
return new Date(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]);
};
// If the UTC indicator is set the date is UTC
if ( TF[8] == "Z" ) {
return new Date(Date.UTC(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]));
};
// If the date has a timezone offset
if ( TF[9] == "-" || TF[9] == "+" ) {
// Get current Timezone information
var CurTZ = new Date().getTimezoneOffset();
var CurTZh = TF[10] - ((CurTZ >= 0 ? "-" : "+") + Math.floor(Math.abs(CurTZ) / 60))
var CurTZm = TF[11] - ((CurTZ >= 0 ? "-" : "+") + (Math.abs(CurTZ) % 60))
// Return the date
return new Date(TF[1], TF[2], TF[3], TF[4] - CurTZh, TF[5] - CurTZm, TF[6], TF[7]);
};
// If we've reached here we couldn't deal with the input, return null
return null;
};
return Query return Query
})() })()
......
...@@ -108,7 +108,7 @@ module.exports = (function() { ...@@ -108,7 +108,7 @@ module.exports = (function() {
maxConcurrentQueries: this.options.maxConcurrentQueries, maxConcurrentQueries: this.options.maxConcurrentQueries,
dialectOptions: this.options.dialectOptions, dialectOptions: this.options.dialectOptions,
} }
try { try {
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager") var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
} catch(err) { } catch(err) {
...@@ -193,7 +193,7 @@ module.exports = (function() { ...@@ -193,7 +193,7 @@ module.exports = (function() {
attributes[name].validate = attributes[name].validate || { attributes[name].validate = attributes[name].validate || {
_checkEnum: function(value) { _checkEnum: function(value) {
var hasValue = value !== undefined var hasValue = value !== undefined
, isMySQL = self.options.dialect === "mysql" , isMySQL = ['mysql', 'mariadb'].indexOf(self.options.dialect) !== -1
, ciCollation = !!options.collate && options.collate.match(/_ci$/i) !== null , ciCollation = !!options.collate && options.collate.match(/_ci$/i) !== null
, valueOutOfScope , valueOutOfScope
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
"mocha": "~1.13.0", "mocha": "~1.13.0",
"chai-datetime": "~1.1.1", "chai-datetime": "~1.1.1",
"sinon": "~1.7.3", "sinon": "~1.7.3",
"mariasql": "~0.1.19" "mariasql": "git://github.com/sequelize/node-mariasql.git"
}, },
"keywords": [ "keywords": [
"mysql", "mysql",
......
...@@ -3462,7 +3462,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -3462,7 +3462,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
UserPublic.schema('special').sync({ force: true }).success(function() { UserPublic.schema('special').sync({ force: true }).success(function() {
self.sequelize.queryInterface.describeTable('Publics') self.sequelize.queryInterface.describeTable('Publics')
.on('sql', function(sql) { .on('sql', function(sql) {
if (dialect === "sqlite" || dialect === "mysql") { if (dialect === "sqlite" || dialect === "mysql" || dialect === 'mariadb') {
expect(sql).to.not.contain('special') expect(sql).to.not.contain('special')
_done() _done()
} }
...@@ -3475,7 +3475,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -3475,7 +3475,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.sequelize.queryInterface.describeTable('Publics', 'special') self.sequelize.queryInterface.describeTable('Publics', 'special')
.on('sql', function(sql) { .on('sql', function(sql) {
if (dialect === "sqlite" || dialect === "mysql") { if (dialect === "sqlite" || dialect === "mysql" || dialect === 'mariadb') {
expect(sql).to.contain('special') expect(sql).to.contain('special')
_done() _done()
} }
...@@ -3673,7 +3673,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -3673,7 +3673,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
describe("dataset", function() { describe("dataset", function() {
it("returns a node-sql instance with the correct dialect", function() { it("returns a node-sql instance with the correct dialect", function() {
expect(this.User.dataset().sql.dialectName).to.equal(dialect) var _dialect = dialect === 'mariadb' ? 'mysql' : dialect
expect(this.User.dataset().sql.dialectName).to.equal(_dialect)
}) })
it("allows me to generate sql queries", function() { it("allows me to generate sql queries", function() {
......
...@@ -7,7 +7,7 @@ var chai = require('chai') ...@@ -7,7 +7,7 @@ var chai = require('chai')
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql|mariadb/)) {
describe('[MYSQL Specific] Associations', function() { describe('[MYSQL Specific] Associations', function() {
describe('many-to-many', function() { describe('many-to-many', function() {
describe('where tables have the same prefix', function() { describe('where tables have the same prefix', function() {
......
...@@ -7,7 +7,7 @@ var chai = require('chai') ...@@ -7,7 +7,7 @@ var chai = require('chai')
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql|mariadb/)) {
describe('[MYSQL Specific] Connector Manager', function() { describe('[MYSQL Specific] Connector Manager', function() {
this.timeout(10000) this.timeout(10000)
......
...@@ -8,7 +8,7 @@ var chai = require('chai') ...@@ -8,7 +8,7 @@ var chai = require('chai')
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql|mariadb/)) {
describe("[MYSQL Specific] DAOFactory", function () { describe("[MYSQL Specific] DAOFactory", function () {
describe('constructor', function() { describe('constructor', function() {
it("handles extended attributes (unique)", function(done) { it("handles extended attributes (unique)", function(done) {
......
...@@ -9,7 +9,7 @@ var chai = require('chai') ...@@ -9,7 +9,7 @@ var chai = require('chai')
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql|mariadb/)) {
describe("[MYSQL Specific] QueryGenerator", function () { describe("[MYSQL Specific] QueryGenerator", function () {
var suites = { var suites = {
attributesToSQL: [ attributesToSQL: [
......
...@@ -18,7 +18,7 @@ describe(Support.getTestDialectTeaser("QueryGenerators"), function () { ...@@ -18,7 +18,7 @@ describe(Support.getTestDialectTeaser("QueryGenerators"), function () {
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
var sql = '' var sql = ''
if (dialect === "mysql") { if (dialect === "mysql" || dialect === 'mariadb') {
sql = 'SELECT COLUMN_COMMENT as cmt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \'' + self.sequelize.config.database + '\' AND TABLE_NAME = \'Users\' AND COLUMN_NAME = \'username\''; sql = 'SELECT COLUMN_COMMENT as cmt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \'' + self.sequelize.config.database + '\' AND TABLE_NAME = \'Users\' AND COLUMN_NAME = \'username\'';
} }
else if (dialect === "postgres" || dialect === "postgres-native") { else if (dialect === "postgres" || dialect === "postgres-native") {
......
...@@ -13,7 +13,7 @@ chai.Assertion.includeStack = true ...@@ -13,7 +13,7 @@ chai.Assertion.includeStack = true
var qq = function(str) { var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') { if (dialect == 'postgres' || dialect == 'sqlite') {
return '"' + str + '"' return '"' + str + '"'
} else if (dialect == 'mysql') { } else if (dialect == 'mysql' || dialect === 'mariadb') {
return '`' + str + '`' return '`' + str + '`'
} else { } else {
return str return str
...@@ -147,7 +147,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -147,7 +147,7 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
}) })
if (dialect == 'mysql') { if (dialect == 'mysql' || dialect === 'mariadb') {
it('executes stored procedures', function(done) { it('executes stored procedures', function(done) {
var self = this var self = this
self.sequelize.query(this.insertQuery).success(function() { self.sequelize.query(this.insertQuery).success(function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!