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

Commit 4f74575a by Jan Aagaard Meier

feat(datatypes) Initial work on datatypes refactor

1 parent 1b8d2ade
......@@ -943,16 +943,34 @@ var QueryGenerator = {
*/
escape: function(value, field, options) {
options = options || {};
if (value && value._isSequelizeMethod) {
return this.handleSequelizeMethod(value);
} else {
if (['INSERT', 'UPDATE'].indexOf(options.context) !== -1 && this.typeValidation && field && field.type && value) {
if (field.type.validate) {
field.type.validate(value);
if (value) {
if (value._isSequelizeMethod) {
return this.handleSequelizeMethod(value);
} else {
if (field && field.type) {
if (['INSERT', 'UPDATE'].indexOf(options.context) !== -1 && this.typeValidation && field && field.type && value) {
if (field.type.validate) {
field.type.validate(value);
}
}
if (field.type.stringify) {
// Users shouldn't have to worry about these args - just give them a function that takes a single arg
var simpleEscape = _.partialRight(SqlString.escape, this.options.timezone, this.dialect, field);
value = field.type.stringify(value, { escape: simpleEscape, field: field, timezone: this.options.timezone });
if (field.type.escape === false) {
// The data-type already did the required escaping
return value;
}
}
}
}
return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
}
return SqlString.escape(value, this.options.timezone, this.dialect, field);
},
/**
......
......@@ -5,7 +5,7 @@ var _ = require('lodash')
, ConnectionManager = require('./connection-manager')
, Query = require('./query')
, QueryGenerator = require('./query-generator')
, DataTypes = require('./data-types');
, DataTypes = require('../../data-types').mssql;
var MssqlDialect = function(sequelize) {
this.sequelize = sequelize;
......
......@@ -4,7 +4,11 @@ var AbstractConnectionManager = require('../abstract/connection-manager')
, ConnectionManager
, Utils = require('../../utils')
, Promise = require('../../promise')
, sequelizeErrors = require('../../errors');
, sequelizeErrors = require('../../errors')
, dataTypes = require('../../data-types').mysql
, _ = require('lodash')
, parserMap = {};
ConnectionManager = function(dialect, sequelize) {
AbstractConnectionManager.call(this, dialect, sequelize);
......@@ -16,10 +20,23 @@ ConnectionManager = function(dialect, sequelize) {
} catch (err) {
throw new Error('Please install mysql package manually');
}
this.refreshTypeParser(dataTypes);
};
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.refreshTypeParser = function (dataTypes) {
_.each(dataTypes, function (dataType, key) {
if (dataType.parse && dataType.types.mysql) {
dataType.types.mysql.forEach(function (type) {
parserMap[type] = dataType.parse;
});
}
});
};
ConnectionManager.prototype.connect = function(config) {
var self = this;
return new Promise(function (resolve, reject) {
......@@ -29,7 +46,14 @@ ConnectionManager.prototype.connect = function(config) {
user: config.username,
password: config.password,
database: config.database,
timezone: self.sequelize.options.timezone
timezone: self.sequelize.options.timezone,
typeCast: function (field, next) {
if (parserMap[field.type]) {
return parserMap[field.type](field);
}
return next();
}
};
if (config.dialectOptions) {
......
'use strict';
var BaseTypes = require('../../data-types')
, util = require('util')
var util = require('util')
, _ = require('lodash');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html';
module.exports = function (BaseTypes) {
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html';
var UUID = function() {
if (!(this instanceof UUID)) return new UUID();
BaseTypes.UUID.apply(this, arguments);
};
util.inherits(UUID, BaseTypes.UUID);
BaseTypes.DATE.types.mysql = ['DATETIME'];
BaseTypes.STRING.types.mysql = ['VAR_STRING'];
BaseTypes.CHAR.types.mysql = ['STRING'];
BaseTypes.TEXT.types.mysql = ['BLOB'];
BaseTypes.INTEGER.types.mysql = ['LONG'];
BaseTypes.BIGINT.types.mysql = ['BIGINT'];
BaseTypes.FLOAT.types.mysql = ['FLOAT'];
BaseTypes.TIME.types.mysql = ['TIME'];
BaseTypes.DATEONLY.types.mysql = ['DATE'];
BaseTypes.BOOLEAN.types.mysql = ['TINY'];
BaseTypes.BLOB.types.mysql = ['TINYBLOB', 'BLOB', 'LONGBLOB'];
BaseTypes.DECIMAL.types.mysql = ['DECIMAL'];
BaseTypes.UUID.types.mysql = ['UUID'];
BaseTypes.ENUM.types.mysql = ['TEXT']; // Questionable .. overwritten by text
BaseTypes.REAL.types.mysql = ['DOUBLE'];
BaseTypes.DOUBLE.types.mysql = ['DOUBLE'];
UUID.prototype.toSql = function() {
return 'CHAR(36) BINARY';
};
var DATE = BaseTypes.DATE.inherits();
var SUPPORTED_GEOMETRY_TYPES = ['POINT', 'LINESTRING', 'POLYGON'];
var GEOMETRY = function() {
if (!(this instanceof GEOMETRY)) return new GEOMETRY();
BaseTypes.GEOMETRY.apply(this, arguments);
if (_.isEmpty(this.type)) {
this.sqlType = this.key;
} else if (_.includes(SUPPORTED_GEOMETRY_TYPES, this.type)) {
this.sqlType = this.type;
} else {
throw new Error('Supported geometry types are: ' + SUPPORTED_GEOMETRY_TYPES.join(', '));
}
};
util.inherits(GEOMETRY, BaseTypes.GEOMETRY);
DATE.prototype.$stringify = function (date, options) {
date = BaseTypes.DATE.prototype.$applyTimezone(date, options);
GEOMETRY.prototype.toSql = function() {
return this.sqlType;
};
return date.format('YYYY-MM-DD HH:mm:ss');
};
module.exports = {
UUID: UUID,
GEOMETRY: GEOMETRY
};
var UUID = BaseTypes.UUID.inherits();
UUID.prototype.toSql = function() {
return 'CHAR(36) BINARY';
};
var SUPPORTED_GEOMETRY_TYPES = ['POINT', 'LINESTRING', 'POLYGON'];
var GEOMETRY = BaseTypes.GEOMETRY.inherits(function() {
if (!(this instanceof GEOMETRY)) return new GEOMETRY();
BaseTypes.GEOMETRY.apply(this, arguments);
_.forIn(module.exports, function (DataType, key) {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function(oldType) {
return new DataType(oldType.options);
};
}
});
if (_.isEmpty(this.type)) {
this.sqlType = this.key;
} else if (_.includes(SUPPORTED_GEOMETRY_TYPES, this.type)) {
this.sqlType = this.type;
} else {
throw new Error('Supported geometry types are: ' + SUPPORTED_GEOMETRY_TYPES.join(', '));
}
});
util.inherits(GEOMETRY, BaseTypes.GEOMETRY);
GEOMETRY.prototype.toSql = function() {
return this.sqlType;
};
BaseTypes.GEOMETRY.types.mysql = ['GEOMETRY'];
BaseTypes.DATE.types.mysql = ['DATETIME'];
var exports = {
DATE: DATE,
UUID: UUID,
GEOMETRY: GEOMETRY
};
_.forIn(exports, function (DataType, key) {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function(oldType) {
return new DataType(oldType.options);
};
}
});
return exports;
};
......@@ -5,7 +5,7 @@ var _ = require('lodash')
, ConnectionManager = require('./connection-manager')
, Query = require('./query')
, QueryGenerator = require('./query-generator')
, DataTypes = require('./data-types');
, DataTypes = require('../../data-types').mysql;
var MysqlDialect = function(sequelize) {
this.sequelize = sequelize;
......
......@@ -312,23 +312,6 @@ var QueryGenerator = {
return Utils.addTicks(identifier, '`');
},
escape: function(value, field, options) {
options = options || {};
if (value && value._isSequelizeMethod) {
return this.handleSequelizeMethod(value);
} else if (value && field && field.type instanceof DataTypes.GEOMETRY) {
return 'GeomFromText(\'' + Wkt.stringify(value) + '\')';
} else {
if (['INSERT', 'UPDATE'].indexOf(options.context) !== -1 && this.typeValidation && field && field.type && value) {
if (field.type.validate) {
field.type.validate(value);
}
}
return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
}
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......@@ -353,7 +336,7 @@ var QueryGenerator = {
},
geometrySelect: function(column) {
return 'AsText(' + this.quoteIdentifiers(column) + ') AS ' + column;
return this.quoteIdentifiers(column);
}
};
......
......@@ -5,7 +5,12 @@ var AbstractConnectionManager = require('../abstract/connection-manager')
, Utils = require('../../utils')
, Promise = require('../../promise')
, sequelizeErrors = require('../../errors')
<<<<<<< HEAD
, semver = require('semver');
=======
, dataTypes = require('../../data-types').postgres
, _ = require('lodash');
>>>>>>> feat(datatypes) Initial work on datatypes refactor
ConnectionManager = function(dialect, sequelize) {
AbstractConnectionManager.call(this, dialect, sequelize);
......@@ -17,10 +22,39 @@ ConnectionManager = function(dialect, sequelize) {
} catch (err) {
throw new Error('Please install \'' + (sequelize.config.dialectModulePath || 'pg') + '\' module manually');
}
this.refreshTypeParser(dataTypes);
};
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.refreshTypeParser = function (dataTypes) {
var self = this;
_.each(dataTypes, function (dataType, key) {
if (dataType.parse && dataType.types.postgres) {
if (dataType.types.postgres.oids) {
dataType.types.postgres.oids.forEach(function (oid) {
self.lib.types.setTypeParser(oid, function (value) {
return dataType.parse(value, oid, self.lib.types.getTypeParser);
});
});
}
if (dataType.types.postgres.array_oids) {
dataType.types.postgres.array_oids.forEach(function (oid) {
self.lib.types.setTypeParser(oid, function (value) {
return self.lib.types.arrayParser.create(value, function (value) {
return dataType.parse(value, oid, self.lib.types.getTypeParser);
}).parse();
});
});
}
}
});
};
ConnectionManager.prototype.connect = function(config) {
var self = this
, connectionConfig = {};
......
......@@ -5,16 +5,13 @@ var _ = require('lodash')
, ConnectionManager = require('./connection-manager')
, Query = require('./query')
, QueryGenerator = require('./query-generator')
, DataTypes = require('./data-types');
, DataTypes = require('../../data-types').postgres;
var PostgresDialect = function(sequelize) {
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.connectionManager.initPools();
// parseDialectSpecificFields needs access to the pg lib in order to use its array parser. We cannot simply require pg in query.js since the user can specify another library path (such as pg-native etc)
Query.prototype.parseDialectSpecificFields.lib = this.connectionManager.lib;
this.QueryGenerator = _.extend({}, QueryGenerator, {
options: sequelize.options,
_dialect: this,
......
......@@ -845,48 +845,6 @@ var QueryGenerator = {
},
/*
Escape a value (e.g. a string, number or date)
*/
escape: function(value, field, options) {
options = options || {};
if (value && value._isSequelizeMethod) {
return this.handleSequelizeMethod(value);
}
if (['INSERT', 'UPDATE'].indexOf(options.context) !== -1 && this.typeValidation && field && field.type && value) {
if (field.type.validate) {
field.type.validate(value);
}
}
if (Utils._.isObject(value) && field && (field.type instanceof DataTypes.HSTORE || DataTypes.ARRAY.is(field.type, DataTypes.HSTORE))) {
if (field.type instanceof DataTypes.HSTORE){
return "'" + hstore.stringify(value) + "'";
} else if (DataTypes.ARRAY.is(field.type, DataTypes.HSTORE)) {
return 'ARRAY[' + Utils._.map(value, function(v){return "'" + hstore.stringify(v) + "'::hstore";}).join(',') + ']::HSTORE[]';
}
} else if(Utils._.isArray(value) && field && (field.type instanceof DataTypes.RANGE || DataTypes.ARRAY.is(field.type, DataTypes.RANGE))) {
if(field.type instanceof DataTypes.RANGE) { // escape single value
return "'" + range.stringify(value) + "'";
}
else if (DataTypes.ARRAY.is(field.type, DataTypes.RANGE)) { // escape array of ranges
return 'ARRAY[' + Utils._.map(value, function(v){return "'" + range.stringify(v) + "'";}).join(',') + ']::' + field.type.toString();
}
} else if (value!==null && field && field.type instanceof DataTypes.JSON) {
value = JSON.stringify(value);
} else if (Array.isArray(value) && field && DataTypes.ARRAY.is(field.type, DataTypes.JSON)) {
var jsonType = field.type.type; // type may be JSON or JSONB
return 'ARRAY[' + value.map(function (v) {
return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field);
}, this).join(',') + ']::' + jsonType.key + '[]';
} else if (value && field && field.type instanceof DataTypes.GEOMETRY) {
return "ST_GeomFromGeoJSON('" + JSON.stringify(value) + "')";
}
return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
......
......@@ -2,80 +2,17 @@
var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query')
, DataTypes = require('../../data-types')
, hstore = require('./hstore')
, range = require('./range')
, QueryTypes = require('../../query-types')
, Promise = require('../../promise')
, sequelizeErrors = require('../../errors.js');
var parseDialectSpecificFields,
dialectSpecificTypes;
parseDialectSpecificFields = {
// Parses hstore fields if the model has any hstore fields.
// This cannot be done in the 'pg' lib because hstore is a UDT.
hstore: function (value, options) {
if (value === null) return null;
return DataTypes.ARRAY.is(options.dataType, DataTypes.HSTORE) ?
this.lib.types.arrayParser.create(value, hstore.parse).parse() : hstore.parse(value);
},
range: function (value, options) {
if (value === null) return null;
return DataTypes.ARRAY.is(options.dataType, DataTypes.RANGE) ?
this.lib.types.arrayParser.create(value, function (v) {
return range.parse(v, options.dataType.type);
}).parse() : range.parse(value, options.dataType);
},
geometry: function (value, options) {
if (value === null) return null;
return options.dataType.parse(value);
}
};
dialectSpecificTypes = Utils._.keys(parseDialectSpecificFields);
function dialectSpecificFieldDatatypeMap (options, prefix) {
if (!Utils._.isArray(options.types))
return [];
prefix = prefix || '';
var fields = {};
if (options.callee) {
if (options.as)
prefix += options.as + '.';
Utils._.each(options.types, function (type) {
Utils._.each(options.callee['_' + type + 'Attributes'], function (attrName) {
fields[prefix + attrName] = {
dataType: options.callee.attributes[attrName].type || null,
type: type
};
});
});
}
Utils._.each(options.include, function (include) {
fields = Utils._.merge(fields, dialectSpecificFieldDatatypeMap({
callee: include.model,
as: include.as,
include: include.include,
types: options.types
}, prefix));
});
return fields;
}
, sequelizeErrors = require('../../errors.js')
, _ = require('lodash');
var Query = function(client, sequelize, options) {
this.client = client;
this.sequelize = sequelize;
this.instance = options.instance;
this.model = options.model;
this.options = Utils._.extend({
this.options = _.extend({
logging: console.log,
plain: false,
raw: false
......@@ -156,22 +93,17 @@ Query.prototype.run = function(sql, parameters) {
}).spread(function(rows, sql, result) {
var results = rows
, isTableNameQuery = (sql.indexOf('SELECT table_name FROM information_schema.tables') === 0)
, isRelNameQuery = (sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0)
, dialectSpecificFields
, isDialectSpecificField = Utils._.memoize(function (key) { return dialectSpecificFields.hasOwnProperty(key); });
, isRelNameQuery = (sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0);
if (isTableNameQuery || isRelNameQuery) {
if (isRelNameQuery) {
results = rows.map(function(row) {
return {
name: row.relname,
tableName: row.relname.split('_')[0]
};
});
} else {
results = rows.map(function(row) { return Utils._.values(row); });
}
return results;
if (isRelNameQuery) {
return rows.map(function(row) {
return {
name: row.relname,
tableName: row.relname.split('_')[0]
};
});
} else if (isTableNameQuery) {
return rows.map(function(row) { return _.values(row); });
}
if (rows[0] && rows[0].sequelize_caught_exception !== undefined) {
......@@ -193,12 +125,11 @@ Query.prototype.run = function(sql, parameters) {
results.forEach(function (result) {
var attributes = /ON .*? (?:USING .*?\s)?\((.*)\)/gi.exec(result.definition)[1].split(',')
, field
, attribute
, columns;
// Map column index in table to column name
columns = Utils._.zipObject(
columns = _.zipObject(
result.column_indexes,
self.sequelize.queryInterface.QueryGenerator.fromArray(result.column_names)
);
......@@ -246,9 +177,9 @@ Query.prototype.run = function(sql, parameters) {
// Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes
if (self.options.raw === false && self.sequelize.options.quoteIdentifiers === false) {
var attrsMap = Utils._.reduce(self.model.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m; }, {});
var attrsMap = _.reduce(self.model.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m; }, {});
rows.forEach(function(row) {
Utils._.keys(row).forEach(function(key) {
_.keys(row).forEach(function(key) {
var targetAttr = attrsMap[key];
if (typeof targetAttr === 'string' && targetAttr !== key) {
row[targetAttr] = row[key];
......@@ -258,25 +189,6 @@ Query.prototype.run = function(sql, parameters) {
});
}
if (!!self.model && rows.length > 0) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.model,
include: self.options.include,
types: dialectSpecificTypes
});
// check whether custom fields exist in responses model
if (!Utils._.isEmpty(dialectSpecificFields)) {
rows.forEach(function (row, rowId, rows) {
Utils._.each(row, function (value, key) {
if (isDialectSpecificField(key))
rows[rowId][key] =
parseDialectSpecificFields[dialectSpecificFields[key].type](value, { dataType: dialectSpecificFields[key].dataType });
});
});
}
}
return self.handleSelectQuery(rows);
} else if (QueryTypes.DESCRIBE === self.options.type) {
result = {};
......@@ -320,24 +232,6 @@ Query.prototype.run = function(sql, parameters) {
return parseInt(result.rowCount, 10);
}
if (!!self.model && rows.length > 0) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.model,
types: dialectSpecificTypes
});
// check whether custom fields exist in responses model
if (!Utils._.isEmpty(dialectSpecificFields)) {
rows.forEach(function (row, rowId, rows) {
Utils._.each(row, function (value, key) {
if (isDialectSpecificField(key))
rows[rowId][key] =
parseDialectSpecificFields[dialectSpecificFields[key].type](value, { dataType: dialectSpecificFields[key].dataType });
});
});
}
}
return self.handleSelectQuery(rows);
} else if (QueryTypes.BULKDELETE === self.options.type) {
return parseInt(result.rowCount, 10);
......@@ -345,27 +239,11 @@ Query.prototype.run = function(sql, parameters) {
return rows[0].sequelize_upsert;
} else if (self.isInsertQuery() || self.isUpdateQuery()) {
if (self.instance && self.instance.dataValues) {
if (self.model) {
dialectSpecificFields = dialectSpecificFieldDatatypeMap({
callee: self.model,
types: dialectSpecificTypes
});
// check whether custom fields exist in responses model
if (!Utils._.isEmpty(dialectSpecificFields)) {
Utils._.each(rows[0], function (value, key) {
if (isDialectSpecificField(key))
rows[0][key] =
parseDialectSpecificFields[dialectSpecificFields[key].type](value, { dataType: dialectSpecificFields[key].dataType });
});
}
}
for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
var record = rows[0][key];
var attr = Utils._.find(self.model.rawAttributes, function (attribute) {
var attr = _.find(self.model.rawAttributes, function (attribute) {
return attribute.fieldName === key || attribute.field === key;
});
......@@ -418,19 +296,19 @@ Query.prototype.formatError = function (err) {
match = errDetail.replace(/"/g, '').match(/Key \((.*?)\)=\((.*?)\)/);
if (match) {
fields = Utils._.zipObject(match[1].split(', '), match[2].split(', '));
fields = _.zipObject(match[1].split(', '), match[2].split(', '));
errors = [];
message = 'Validation error';
Utils._.forOwn(fields, function(value, field) {
_.forOwn(fields, function(value, field) {
errors.push(new sequelizeErrors.ValidationErrorItem(
self.getUniqueConstraintErrorMessage(field),
'unique violation', field, value));
});
if (this.model && this.model.uniqueKeys) {
Utils._.forOwn(this.model.uniqueKeys, function(constraint) {
if (Utils._.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) {
_.forOwn(this.model.uniqueKeys, function(constraint) {
if (_.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) {
message = constraint.msg;
return false;
}
......@@ -455,7 +333,7 @@ Query.prototype.formatError = function (err) {
match = errDetail.match(/Key \((.*?)\)=\((.*?)\)/);
if (match) {
fields = Utils._.zipObject(match[1].split(', '), match[2].split(', '));
fields = _.zipObject(match[1].split(', '), match[2].split(', '));
}
message = 'Exclusion constraint error';
......
'use strict';
var Utils = require('../../utils'),
moment = require('moment');
var _ = require('lodash');
function stringify (data) {
if (data === null) return null;
if (!Utils._.isArray(data) || data.length !== 2) return '';
if (!_.isArray(data) || data.length !== 2) return '';
if (Utils._.any(data, Utils._.isNull)) return '';
if (_.any(data, _.isNull)) return '';
if (data.hasOwnProperty('inclusive')) {
if (!data.inclusive) data.inclusive = [false, false];
......@@ -17,8 +16,8 @@ function stringify (data) {
data.inclusive = [false, false];
}
Utils._.each(data, function (value, index) {
if (Utils._.isObject(value)) {
_.each(data, function (value, index) {
if (_.isObject(value)) {
if (value.hasOwnProperty('inclusive')) data.inclusive[index] = !!value.inclusive;
if (value.hasOwnProperty('value')) data[index] = value.value;
}
......@@ -27,12 +26,9 @@ function stringify (data) {
return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')');
}
function parse (value, AttributeType) {
function parse (value, parser) {
if (value === null) return null;
if(typeof AttributeType === 'function') AttributeType = new AttributeType();
AttributeType = AttributeType || ''; // if attribute is not defined, assign empty string in order to prevent
// AttributeType.toString() to fail with uncaught exception later in the code
var result = value
.substring(1, value.length - 1)
.split(',', 2);
......@@ -41,18 +37,7 @@ function parse (value, AttributeType) {
result = result
.map(function (value) {
switch (AttributeType.toString()) {
case 'int4range':
return parseInt(value, 10);
case 'numrange':
return parseFloat(value);
case 'daterange':
case 'tsrange':
case 'tstzrange':
return moment(value).toDate();
}
return value;
return parser(value);
});
result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')];
......
......@@ -4,6 +4,7 @@ var AbstractConnectionManager = require('../abstract/connection-manager')
, ConnectionManager
, Utils = require('../../utils')
, Promise = require('../../promise')
, dataTypes = require('../../data-types').sqlite
, sequelizeErrors = require('../../errors');
ConnectionManager = function(dialect, sequelize) {
......@@ -20,10 +21,16 @@ ConnectionManager = function(dialect, sequelize) {
} catch (err) {
throw new Error('Please install sqlite3 package manually');
}
this.refreshTypeParser(dataTypes);
};
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.refreshTypeParser = require('./parsing').refresh;
ConnectionManager.prototype.getConnection = function(options) {
var self = this;
options = options || {};
......
'use strict';
var BaseTypes = require('../../data-types')
, util = require('util')
, _ = require('lodash');
var _ = require('lodash');
module.exports = function (BaseTypes) {
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://www.sqlite.org/datatype3.html';
BaseTypes.DATE.types.sqlite = ['DATETIME'];
BaseTypes.STRING.types.sqlite = ['VARCHAR', 'VARCHAR BINARY'];
BaseTypes.CHAR.types.sqlite = ['CHAR', 'CHAR BINARY'];
BaseTypes.TEXT.types.sqlite = ['TEXT'];
BaseTypes.INTEGER.types.sqlite = ['INTEGER'];
BaseTypes.BIGINT.types.sqlite = ['BIGINT'];
BaseTypes.FLOAT.types.sqlite = ['FLOAT'];
BaseTypes.TIME.types.sqlite = ['TIME'];
BaseTypes.DATEONLY.types.sqlite = ['DATE'];
BaseTypes.BOOLEAN.types.sqlite = ['TINYINT'];
BaseTypes.BLOB.types.sqlite = ['TINYBLOB', 'BLOB', 'LONGBLOB'];
BaseTypes.DECIMAL.types.sqlite = ['DECIMAL'];
BaseTypes.UUID.types.sqlite = ['UUID'];
BaseTypes.ENUM.types.sqlite = ['TEXT']; // Questionable .. overwritten by text
BaseTypes.REAL.types.sqlite = ['REAL'];
BaseTypes.DOUBLE.types.sqlite = ['DOUBLE PRECISION'];
var DATE = BaseTypes.DATE.inherits();
DATE.parse = function (date, options) {
if (date.indexOf('+') === -1) {
// For backwards compat. Dates inserted by sequelize < 2.0dev12 will not have a timestamp set
return new Date(date + options.timezone);
} else {
return new Date(date); // We already have a timezone stored in the string
}
};
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://www.sqlite.org/datatype3.html';
var STRING = BaseTypes.STRING.inherits();
STRING.prototype.toSql = function() {
if (this._binary) {
return 'VARCHAR BINARY(' + this._length + ')';
} else {
return BaseTypes.STRING.prototype.toSql.call(this);
}
};
var STRING = function() {
if (!(this instanceof STRING)) return new STRING();
BaseTypes.STRING.apply(this, arguments);
};
util.inherits(STRING, BaseTypes.STRING);
STRING.prototype.toSql = function() {
if (this._binary) {
return 'VARCHAR BINARY(' + this._length + ')';
} else {
return BaseTypes.STRING.prototype.toSql.call(this);
}
};
var TEXT = BaseTypes.TEXT.inherits();
TEXT.prototype.toSql = function() {
if (this._length) {
this.warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
}
return 'TEXT';
};
BaseTypes.TEXT.prototype.toSql = function() {
if (this._length) {
this.warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
}
return 'TEXT';
};
var CHAR = BaseTypes.CHAR.inherits();
CHAR.prototype.toSql = function() {
if (this._binary) {
return 'CHAR BINARY(' + this._length + ')';
} else {
return BaseTypes.CHAR.prototype.toSql.call(this);
}
};
var CHAR = function() {
if (!(this instanceof CHAR)) return new CHAR();
BaseTypes.CHAR.apply(this, arguments);
};
util.inherits(CHAR, BaseTypes.CHAR);
CHAR.prototype.toSql = function() {
if (this._binary) {
return 'CHAR BINARY(' + this._length + ')';
} else {
return BaseTypes.CHAR.prototype.toSql.call(this);
}
};
var NUMBER = BaseTypes.NUMBER.inherits();
NUMBER.prototype.toSql = function() {
var result = this.key;
var NUMBER = function() {
BaseTypes.NUMBER.apply(this, arguments);
};
util.inherits(NUMBER, BaseTypes.NUMBER);
NUMBER.prototype.toSql = function() {
var result = this.key;
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
}
if (this._length) {
result += '(' + this._length;
if (typeof this._decimals === 'number') {
result += ',' + this._decimals;
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
}
result += ')';
}
return result;
};
var INTEGER = function(length) {
var options = typeof length === 'object' && length || {
length: length
if (this._length) {
result += '(' + this._length;
if (typeof this._decimals === 'number') {
result += ',' + this._decimals;
}
result += ')';
}
return result;
};
if (!(this instanceof INTEGER)) return new INTEGER(options);
NUMBER.call(this, options);
};
util.inherits(INTEGER, BaseTypes.INTEGER);
INTEGER.prototype.key = INTEGER.key = 'INTEGER';
INTEGER.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
var BIGINT = function(length) {
var options = typeof length === 'object' && length || {
length: length
var INTEGER = BaseTypes.INTEGER.inherits(function(length) {
var options = typeof length === 'object' && length || {
length: length
};
if (!(this instanceof INTEGER)) return new INTEGER(options);
NUMBER.call(this, options);
});
INTEGER.prototype.key = INTEGER.key = 'INTEGER';
INTEGER.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
if (!(this instanceof BIGINT)) return new BIGINT(options);
NUMBER.call(this, options);
};
util.inherits(BIGINT, BaseTypes.BIGINT);
BIGINT.prototype.key = BIGINT.key = 'BIGINT';
BIGINT.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
var FLOAT = function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
var BIGINT = BaseTypes.BIGINT.inherits(function(length) {
var options = typeof length === 'object' && length || {
length: length
};
if (!(this instanceof BIGINT)) return new BIGINT(options);
NUMBER.call(this, options);
});
BIGINT.prototype.key = BIGINT.key = 'BIGINT';
BIGINT.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
if (!(this instanceof FLOAT)) return new FLOAT(options);
NUMBER.call(this, options);
};
util.inherits(FLOAT, BaseTypes.FLOAT);
FLOAT.prototype.key = FLOAT.key = 'FLOAT';
FLOAT.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
var DOUBLE = function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
var FLOAT = BaseTypes.FLOAT.inherits(function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
};
if (!(this instanceof FLOAT)) return new FLOAT(options);
NUMBER.call(this, options);
});
FLOAT.prototype.key = FLOAT.key = 'FLOAT';
FLOAT.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
if (!(this instanceof DOUBLE)) return new DOUBLE(options);
NUMBER.call(this, options);
};
util.inherits(DOUBLE, BaseTypes.DOUBLE);
DOUBLE.prototype.key = DOUBLE.key = 'DOUBLE PRECISION';
DOUBLE.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
var REAL = function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
var DOUBLE = BaseTypes.DOUBLE.inherits(function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
};
if (!(this instanceof DOUBLE)) return new DOUBLE(options);
NUMBER.call(this, options);
});
DOUBLE.prototype.key = DOUBLE.key = 'DOUBLE PRECISION';
DOUBLE.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
if (!(this instanceof REAL)) return new REAL(options);
NUMBER.call(this, options);
};
util.inherits(REAL, BaseTypes.REAL);
REAL.prototype.key = REAL.key = 'REAL';
REAL.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
module.exports = {
STRING: STRING,
CHAR: CHAR,
NUMBER: NUMBER,
FLOAT: FLOAT,
REAL: REAL,
'DOUBLE PRECISION': DOUBLE,
INTEGER: INTEGER,
BIGINT: BIGINT
};
_.forIn(module.exports, function (DataType, key) {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function(oldType) {
return new DataType(oldType.options);
var REAL = BaseTypes.REAL.inherits(function(length, decimals) {
var options = typeof length === 'object' && length || {
length: length,
decimals: decimals
};
}
});
\ No newline at end of file
if (!(this instanceof REAL)) return new REAL(options);
NUMBER.call(this, options);
});
REAL.prototype.key = REAL.key = 'REAL';
REAL.prototype.toSql = function() {
return NUMBER.prototype.toSql.call(this);
};
var exports = {
DATE: DATE,
STRING: STRING,
CHAR: CHAR,
NUMBER: NUMBER,
FLOAT: FLOAT,
REAL: REAL,
'DOUBLE PRECISION': DOUBLE,
INTEGER: INTEGER,
BIGINT: BIGINT,
TEXT: TEXT
};
_.forIn(exports, function (DataType, key) {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function(oldType) {
return new DataType(oldType.options);
};
}
});
return exports;
};
......@@ -5,7 +5,7 @@ var _ = require('lodash')
, ConnectionManager = require('./connection-manager')
, Query = require('./query')
, QueryGenerator = require('./query-generator')
, DataTypes = require('./data-types');
, DataTypes = require('../../data-types').sqlite;
var SqliteDialect = function(sequelize) {
this.sequelize = sequelize;
......
'use strict';
var _ = require('lodash');
module.exports.parsers = {};
module.exports.refresh = function (dataTypes) {
_.each(dataTypes, function (dataType, key) {
if (dataType.parse && dataType.types.sqlite) {
dataType.types.sqlite.forEach(function (type) {
module.exports.parsers[type] = dataType.parse;
});
}
});
};
......@@ -195,7 +195,7 @@ var QueryGenerator = {
if (Utils._.isObject(dataType)) {
var template = '<%= type %>'
, replacements = { type: dataType.type };
, replacements = { type: dataType.type.toString() };
if (dataType.type instanceof DataTypes.ENUM) {
replacements.type = 'TEXT';
......
'use strict';
var Utils = require('../../utils')
, _ = require('lodash')
, AbstractQuery = require('../abstract/query')
, QueryTypes = require('../../query-types')
, sequelizeErrors = require('../../errors.js');
, sequelizeErrors = require('../../errors.js')
, parsers = require('./parsing').parsers;
var Query = function(database, sequelize, options) {
this.database = database;
this.sequelize = sequelize;
this.instance = options.instance;
this.model = options.model;
this.options = Utils._.extend({
this.options = _.extend({
logging: console.log,
plain: false,
raw: false
......@@ -47,6 +49,28 @@ Query.formatBindParameters = function(sql, values, dialect) {
return [sql, bindParam];
};
Query.prototype.$collectModels = function(include, prefix) {
var ret = {};
if (include) {
include.forEach(function (include) {
var key;
if (!prefix) {
key = include.as;
} else {
key = prefix + '.' + include.as;
}
ret[key] = include.model;
if (include.include) {
_.merge(ret, this.$collectModels(include.include, key));
}
}, this);
}
return ret;
};
Query.prototype.run = function(sql, parameters) {
var self = this
, promise;
......@@ -93,29 +117,49 @@ Query.prototype.run = function(sql, parameters) {
result = results.map(function(resultSet) { return resultSet.name; });
} else if (self.isSelectQuery()) {
if (!self.options.raw) {
// This is a map of prefix strings to models, e.g. user.projects -> Project model
var prefixes = self.$collectModels(self.options.include);
results = results.map(function(result) {
for (var name in result) {
if (result.hasOwnProperty(name) && metaData.columnTypes[name]) {
if (metaData.columnTypes[name] === 'DATETIME') {
// we need to convert the timestamps into actual date objects
var val = result[name];
if (val !== null) {
if (val.indexOf('+') === -1) {
// For backwards compat. Dates inserted by sequelize < 2.0dev12 will not have a timestamp set
result[name] = new Date(val + self.sequelize.options.timezone);
} else {
result[name] = new Date(val); // We already have a timezone stored in the string
}
}
} else if (metaData.columnTypes[name].lastIndexOf('BLOB') !== -1) {
if (result[name]) {
result[name] = new Buffer(result[name]);
return _.mapValues(result, function (value, name) {
var model;
if (name.indexOf('.') !== -1) {
var lastind = name.lastIndexOf('.');
model = prefixes[name.substr(0, lastind)];
name = name.substr(lastind + 1);
} else {
model = self.options.model;
}
var tableTypes = metaData.columnTypes[model.tableName];
if (!(name in tableTypes)) {
// The column is aliased
_.forOwn(model.rawAttributes, function (attribute, key) {
if (name === key && attribute.field) {
name = attribute.field;
return false;
}
}
});
}
var type = tableTypes[name];
if (type.indexOf('(') !== -1) {
// Remove the lenght part
type = type.substr(0, type.indexOf('('));
}
}
return result;
type = type.replace('UNSIGNED', '').replace('ZEROFILL', '');
type = type.trim();
var parse = parsers[type];
if (value !== null && parse) {
return parse(value, { timezone: self.sequelize.options.timezone});
}
return value;
});
});
}
......@@ -192,15 +236,20 @@ Query.prototype.run = function(sql, parameters) {
if (!tableNames.length) {
return executeSql();
} else {
// If we already have the metadata for the table, there no need to ask for it again
tableNames = _.filter(tableNames, function (tableName) {
return !(tableName in columnTypes);
});
return Utils.Promise.map(tableNames, function(tableName) {
if (tableName !== 'sqlite_master') {
return new Utils.Promise(function(resolve) {
// get the column types
columnTypes[tableName] = {};
self.database.all('PRAGMA table_info(`' + tableName + '`)', function(err, results) {
if (!err) {
for (var i = 0, l = results.length; i < l; i++) {
columnTypes[tableName + '.' + results[i].name] = columnTypes[results[i].name] = results[i].type;
}
results.forEach(function (result) {
columnTypes[tableName][result.name] = result.type;
});
}
resolve();
});
......@@ -257,8 +306,8 @@ Query.prototype.formatError = function (err) {
});
if (this.model) {
Utils._.forOwn(this.model.uniqueKeys, function(constraint) {
if (Utils._.isEqual(constraint.fields, fields) && !!constraint.msg) {
_.forOwn(this.model.uniqueKeys, function(constraint) {
if (_.isEqual(constraint.fields, fields) && !!constraint.msg) {
message = constraint.msg;
return false;
}
......
......@@ -428,6 +428,10 @@ Sequelize.prototype.InvalidConnectionError = Sequelize.InvalidConnectionError =
Sequelize.prototype.ConnectionTimedOutError = Sequelize.ConnectionTimedOutError =
sequelizeErrors.ConnectionTimedOutError;
Sequelize.prototype.refreshTypes = function () {
this.connectionManager.refreshTypeParser(DataTypes);
};
/**
* Returns the specified dialect.
*
......
'use strict';
/* jshint -W110 */
var moment = require('moment-timezone')
, isArrayBufferView
var dataTypes = require('./data-types')
, SqlString = exports;
if (typeof ArrayBufferView === 'function') {
isArrayBufferView = function(object) { return object && (object instanceof ArrayBufferView); };
} else {
var arrayBufferViews = [
Int8Array, Uint8Array, Int16Array, Uint16Array,
Int32Array, Uint32Array, Float32Array, Float64Array
];
isArrayBufferView = function(object) {
for (var i = 0; i < 8; i++) {
if (object instanceof arrayBufferViews[i]) {
return true;
}
}
return false;
};
}
SqlString.escapeId = function(val, forbidQualified) {
if (forbidQualified) {
return '`' + val.replace(/`/g, '``') + '`';
......@@ -30,19 +11,7 @@ SqlString.escapeId = function(val, forbidQualified) {
return '`' + val.replace(/`/g, '``').replace(/\./g, '`.`') + '`';
};
SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
if (arguments.length === 1 && typeof val === 'object' && val !== null) {
val = val.val || val.value || null;
stringifyObjects = val.stringifyObjects || val.objects || undefined;
timeZone = val.timeZone || val.zone || null;
dialect = val.dialect || null;
field = val.field || null;
}
else if (arguments.length === 2 && typeof stringifyObjects === 'object' && stringifyObjects !== null) {
timeZone = stringifyObjects.timeZone || stringifyObjects.zone || null;
dialect = stringifyObjects.dialect || null;
field = stringifyObjects.field || null;
}
SqlString.escape = function(val, timeZone, dialect) {
if (val === undefined || val === null) {
return 'NULL';
}
......@@ -60,22 +29,11 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
}
if (val instanceof Date) {
val = SqlString.dateToString(val, timeZone || 'Z', dialect);
val = dataTypes[dialect].DATE.prototype.stringify(val, { options: timeZone });
}
if (Buffer.isBuffer(val)) {
return SqlString.bufferToString(val, dialect);
}
if (Array.isArray(val) || isArrayBufferView(val)) {
return SqlString.arrayToList(val, timeZone, dialect, field);
}
if (typeof val === 'object' && val !== null) {
if (stringifyObjects) {
val = val.toString();
} else {
return SqlString.objectToValues(val, timeZone);
}
return dataTypes[dialect].BLOB.prototype.stringify(val);
}
if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') {
......@@ -98,43 +56,6 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
return "'" + val + "'";
};
SqlString.arrayToList = function(array, timeZone, dialect, field) {
var valstr, i;
if (dialect === 'postgres') {
valstr = '';
if (array.map) {
valstr = array.map(function(v) {
return SqlString.escape(v, true, timeZone, dialect, field);
}).join(',');
} else {
for (i = 0; i < array.length; i++) {
valstr += SqlString.escape(array[i], true, timeZone, dialect, field) + ',';
}
valstr = valstr.slice(0, -1);
}
var ret = 'ARRAY[' + valstr + ']';
if (!!field && !!field.type) {
ret += '::' + field.type.toSql().replace(/\(\d+\)/g, '');
}
return ret;
} else {
if (array.map) {
return array.map(function(v) {
if (Array.isArray(v)) {
return '(' + SqlString.arrayToList(v, timeZone, dialect) + ')';
}
return SqlString.escape(v, true, timeZone, dialect);
}).join(', ');
} else {
valstr = '';
for (i = 0; i < array.length; i++) {
valstr += SqlString.escape(array[i], true, timeZone, dialect) + ', ';
}
return valstr.slice(0, -2);
}
}
};
SqlString.format = function(sql, values, timeZone, dialect) {
values = [].concat(values);
......@@ -143,7 +64,7 @@ SqlString.format = function(sql, values, timeZone, dialect) {
return match;
}
return SqlString.escape(values.shift(), false, timeZone, dialect);
return SqlString.escape(values.shift(), timeZone, dialect);
});
};
......@@ -154,51 +75,9 @@ SqlString.formatNamedParameters = function(sql, values, timeZone, dialect) {
}
if (values[key] !== undefined) {
return SqlString.escape(values[key], false, timeZone, dialect);
return SqlString.escape(values[key], timeZone, dialect);
} else {
throw new Error('Named parameter "' + value + '" has no value in the given object.');
}
});
};
SqlString.dateToString = function(date, timeZone, dialect) {
if (moment.tz.zone(timeZone)) {
date = moment(date).tz(timeZone);
} else {
date = moment(date).utcOffset(timeZone);
}
if (dialect === 'mysql' || dialect === 'mariadb') {
return date.format('YYYY-MM-DD HH:mm:ss');
} else {
// ZZ here means current timezone, _not_ UTC
return date.format('YYYY-MM-DD HH:mm:ss.SSS Z');
}
};
SqlString.bufferToString = function(buffer, dialect) {
var hex = buffer.toString('hex');
if (dialect === 'postgres') {
// bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html
return "E'\\\\x" + hex + "'";
} else if (dialect === 'mssql') {
return '0x' + hex;
}
return "X'" + hex + "'";
};
SqlString.objectToValues = function(object, timeZone) {
var values = [];
for (var key in object) {
var value = object[key];
if (typeof value === 'function') {
continue;
}
values.push(this.escapeId(key) + ' = ' + SqlString.escape(value, true, timeZone));
}
return values.join(', ');
};
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, Sequelize = require('../../index')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + '/../../lib/data-types')
, dialect = Support.getTestDialect()
, sinon = require('sinon')
, _ = require('lodash')
, moment = require('moment')
, current = Support.sequelize;
describe.only(Support.getTestDialectTeaser('DataTypes'), function() {
it('allows me to use a custom parse function', function () {
// var parseStub = sinon.stub(this.sequelize.DATE, 'parse', function (value) {
// new moment(value);
// });
var parse = Sequelize.DATE.parse = sinon.spy(function (value) {
return moment(value, 'YYYY-MM-DD HH:mm:ss Z');
});
var stringify = Sequelize.DATE.prototype.stringify = sinon.spy(function (value, options) {
if (!moment.isMoment(value)) {
value = this.$applyTimezone(value, options);
}
return value.format('YYYY-MM-DD HH:mm:ss Z');
});
this.sequelize.refreshTypes();
var User = this.sequelize.define('user', {
dateField: Sequelize.DATE
}, {
timestamps: false
});
return this.sequelize.sync({ force: true }).then(function () {
return User.create({
dateField: moment("2011 10 31", 'YYYY MM DD')
});
}).then(function () {
return User.findAll().get(0);
}).then(function (user) {
expect(parse).to.have.been.called;
expect(stringify).to.have.been.called;
expect(moment.isMoment(user.dateField)).to.be.ok;
});
});
var testType = function (Type, value) {
it((new Type()).toSql(), function () {
var parse = Type.parse = sinon.spy(function (value) {
return value;
});
var stringify = Type.prototype.stringify = sinon.spy(function (value) {
return value;
});
this.sequelize.refreshTypes();
var User = this.sequelize.define('user', {
field: new Type()
}, {
timestamps: false
});
return this.sequelize.sync({ force: true }).then(function () {
return User.create({
field: value
});
}).then(function () {
return User.findAll().get(0);
}).then(function (user) {
expect(parse).to.have.been.called;
expect(stringify).to.have.been.called;
});
});
};
[Sequelize.TEXT, Sequelize.STRING, Sequelize.CHAR].forEach(function (Type) {
testType(Type, 'foobar');
});
[Sequelize.BOOLEAN, Sequelize.DOUBLE, Sequelize.REAL, Sequelize.INTEGER].forEach(function (Type) {
testType(Type, 1);
});
});
......@@ -659,8 +659,8 @@ if (dialect.match(/^postgres/)) {
return this.User.create({ username: 'user', email: ['foo@bar.com'], course_period: period}).then(function(newUser) {
// Check to see if the default value for a range field works
expect(newUser.acceptable_marks.length).to.equal(2);
expect(newUser.acceptable_marks[0]).to.equal(0.65); // lower bound
expect(newUser.acceptable_marks[1]).to.equal(1); // upper bound
expect(newUser.acceptable_marks[0]).to.equal('0.65'); // lower bound
expect(newUser.acceptable_marks[1]).to.equal('1'); // upper bound
expect(newUser.acceptable_marks.inclusive).to.deep.equal([false, false]); // not inclusive
expect(newUser.course_period[0] instanceof Date).to.be.ok; // lower bound
expect(newUser.course_period[1] instanceof Date).to.be.ok; // upper bound
......@@ -731,8 +731,8 @@ if (dialect.match(/^postgres/)) {
return User.create({ username: 'user', email: ['foo@bar.com'], course_period: period }).then(function(newUser) {
// Check to see if the default value for a range field works
expect(newUser.acceptable_marks.length).to.equal(2);
expect(newUser.acceptable_marks[0]).to.equal(0.65); // lower bound
expect(newUser.acceptable_marks[1]).to.equal(1); // upper bound
expect(newUser.acceptable_marks[0]).to.equal('0.65'); // lower bound
expect(newUser.acceptable_marks[1]).to.equal('1'); // upper bound
expect(newUser.acceptable_marks.inclusive).to.deep.equal([false, false]); // not inclusive
expect(newUser.course_period[0] instanceof Date).to.be.ok;
expect(newUser.course_period[1] instanceof Date).to.be.ok;
......
......@@ -846,7 +846,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
logging: function(sql) {
test = true;
expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1);
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1);
expect(sql.indexOf('ARRAY[]::VARCHAR(255)[]')).to.be.above(-1);
}
});
}).then(function() {
......@@ -874,7 +874,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
logging: function(sql) {
test = true;
expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1);
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1);
expect(sql.indexOf('ARRAY[]::VARCHAR(255)[]')).to.be.above(-1);
}
});
});
......
......@@ -312,4 +312,4 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
}
});
\ No newline at end of file
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!