sequelize.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. var url = require("url")
  2. , Path = require("path")
  3. , Utils = require("./utils")
  4. , DAOFactory = require("./dao-factory")
  5. , DAOValidator = require("./dao-validator")
  6. , DataTypes = require('./data-types')
  7. , DAOFactoryManager = require("./dao-factory-manager")
  8. , QueryInterface = require("./query-interface")
  9. , Transaction = require("./transaction")
  10. , TransactionManager = require('./transaction-manager')
  11. , QueryTypes = require('./query-types')
  12. module.exports = (function() {
  13. /**
  14. Main class of the project.
  15. @param {String} database The name of the database.
  16. @param {String} username The username which is used to authenticate against the database.
  17. @param {String} [password=null] The password which is used to authenticate against the database.
  18. @param {Object} [options={}] An object with options.
  19. @param {String} [options.dialect='mysql'] The dialect of the relational database.
  20. @param {String} [options.dialectModulePath=null] If specified, load the dialect library from this path.
  21. @param {String} [options.host='localhost'] The host of the relational database.
  22. @param {Integer} [options.port=] The port of the relational database.
  23. @param {String} [options.protocol='tcp'] The protocol of the relational database.
  24. @param {Object} [options.define={}] Options, which shall be default for every model definition.
  25. @param {Object} [options.query={}] I have absolutely no idea.
  26. @param {Object} [options.sync={}] Options, which shall be default for every `sync` call.
  27. @param {Function} [options.logging=console.log] A function that gets executed everytime Sequelize would log something.
  28. @param {Boolean} [options.omitNull=false] A flag that defines if null values should be passed to SQL queries or not.
  29. @param {Boolean} [options.queue=true] I have absolutely no idea.
  30. @param {Boolean} [options.native=false] A flag that defines if native library shall be used or not.
  31. @param {Boolean} [options.replication=false] I have absolutely no idea.
  32. @param {Object} [options.pool={}] Something.
  33. @param {Boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them.
  34. @example
  35. // without password and options
  36. var sequelize = new Sequelize('database', 'username')
  37. // without options
  38. var sequelize = new Sequelize('database', 'username', 'password')
  39. // without password / with blank password
  40. var sequelize = new Sequelize('database', 'username', null, {})
  41. // with password and options
  42. var sequelize = new Sequelize('my_database', 'john', 'doe', {})
  43. @class Sequelize
  44. @constructor
  45. */
  46. var Sequelize = function(database, username, password, options) {
  47. var urlParts
  48. options = options || {}
  49. if (arguments.length === 1 || (arguments.length === 2 && typeof username === 'object')) {
  50. options = username || {}
  51. urlParts = url.parse(arguments[0])
  52. // SQLite don't have DB in connection url
  53. if (urlParts.pathname) {
  54. database = urlParts.pathname.replace(/^\//, '')
  55. }
  56. dialect = urlParts.protocol
  57. options.dialect = urlParts.protocol.replace(/:$/, '')
  58. options.host = urlParts.hostname
  59. if (urlParts.port) {
  60. options.port = urlParts.port
  61. }
  62. if (urlParts.auth) {
  63. username = urlParts.auth.split(':')[0]
  64. password = urlParts.auth.split(':')[1]
  65. }
  66. }
  67. this.options = Utils._.extend({
  68. dialect: 'mysql',
  69. dialectModulePath: null,
  70. host: 'localhost',
  71. protocol: 'tcp',
  72. define: {},
  73. query: {},
  74. sync: {},
  75. logging: console.log,
  76. omitNull: false,
  77. queue: true,
  78. native: false,
  79. replication: false,
  80. ssl: undefined,
  81. pool: {},
  82. quoteIdentifiers: true,
  83. language: 'en'
  84. }, options || {})
  85. if (this.options.logging === true) {
  86. console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
  87. this.options.logging = console.log
  88. }
  89. this.config = {
  90. database: database,
  91. username: username,
  92. password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
  93. host : this.options.host,
  94. port : this.options.port,
  95. pool : this.options.pool,
  96. protocol: this.options.protocol,
  97. queue : this.options.queue,
  98. native : this.options.native,
  99. ssl : this.options.ssl,
  100. replication: this.options.replication,
  101. dialectModulePath: this.options.dialectModulePath,
  102. maxConcurrentQueries: this.options.maxConcurrentQueries,
  103. dialectOptions: this.options.dialectOptions,
  104. }
  105. try {
  106. var Dialect = require("./dialects/" + this.getDialect())
  107. this.dialect = new Dialect(this)
  108. } catch(err) {
  109. throw new Error("The dialect " + this.getDialect() + " is not supported.")
  110. }
  111. this.daoFactoryManager = new DAOFactoryManager(this)
  112. this.transactionManager = new TransactionManager(this)
  113. this.importCache = {}
  114. }
  115. /**
  116. Reference to Utils
  117. */
  118. Sequelize.Utils = Utils
  119. Sequelize.QueryTypes = QueryTypes
  120. Sequelize.DAOValidator = DAOValidator
  121. Sequelize.DAOFactory = Sequelize.Model = DAOFactory
  122. for (var dataType in DataTypes) {
  123. Sequelize[dataType] = DataTypes[dataType]
  124. }
  125. /**
  126. * Polyfill for the default connector manager.
  127. */
  128. Object.defineProperty(Sequelize.prototype, 'connectorManager', {
  129. get: function() {
  130. return this.transactionManager.getConnectorManager()
  131. }
  132. })
  133. /**
  134. * Returns the specified dialect.
  135. *
  136. * @return {String} The specified dialect.
  137. */
  138. Sequelize.prototype.getDialect = function() {
  139. return this.options.dialect
  140. }
  141. /**
  142. Returns an instance of QueryInterface.
  143. @method getQueryInterface
  144. @return {QueryInterface} An instance (singleton) of QueryInterface.
  145. */
  146. Sequelize.prototype.getQueryInterface = function() {
  147. this.queryInterface = this.queryInterface || new QueryInterface(this)
  148. return this.queryInterface
  149. }
  150. /**
  151. Returns an instance (singleton) of Migrator.
  152. @method getMigrator
  153. @param {Object} [options={}] Some options
  154. @param {Boolean} [force=false] A flag that defines if the migrator should get instantiated or not.
  155. @return {Migrator} An instance of Migrator.
  156. */
  157. Sequelize.prototype.getMigrator = function(options, force) {
  158. var Migrator = require("./migrator")
  159. if (force) {
  160. this.migrator = new Migrator(this, options)
  161. } else {
  162. this.migrator = this.migrator || new Migrator(this, options)
  163. }
  164. return this.migrator
  165. }
  166. Sequelize.prototype.define = function(daoName, attributes, options) {
  167. options = options || {}
  168. var self = this
  169. , globalOptions = this.options
  170. if (globalOptions.define) {
  171. options = Utils._.extend({}, globalOptions.define, options)
  172. Utils._(['classMethods', 'instanceMethods']).each(function(key) {
  173. if (globalOptions.define[key]) {
  174. options[key] = options[key] || {}
  175. Utils._.extend(options[key], globalOptions.define[key])
  176. }
  177. })
  178. }
  179. options.omitNull = globalOptions.omitNull
  180. options.language = globalOptions.language
  181. // If you don't specify a valid data type lets help you debug it
  182. Utils._.each(attributes, function(dataType, name) {
  183. if (Utils.isHash(dataType)) {
  184. // We have special cases where the type is an object containing
  185. // the values (e.g. Sequelize.ENUM(value, value2) returns an object
  186. // instead of a function)
  187. // Copy these values to the dataType
  188. dataType.values = (dataType.type && dataType.type.values) || dataType.values;
  189. // We keep on working with the actual type object
  190. dataType = dataType.type
  191. }
  192. if (dataType === undefined) {
  193. throw new Error('Unrecognized data type for field '+ name)
  194. }
  195. if (dataType.toString() === "ENUM") {
  196. attributes[name].validate = attributes[name].validate || {
  197. _checkEnum: function(value) {
  198. var hasValue = value !== undefined
  199. , isMySQL = ['mysql', 'mariadb'].indexOf(self.options.dialect) !== -1
  200. , ciCollation = !!options.collate && options.collate.match(/_ci$/i) !== null
  201. , valueOutOfScope
  202. if (isMySQL && ciCollation && hasValue) {
  203. var scopeIndex = (attributes[name].values || []).map(function(d) { return d.toLowerCase() }).indexOf(value.toLowerCase())
  204. valueOutOfScope = scopeIndex === -1
  205. } else {
  206. valueOutOfScope = ((attributes[name].values || []).indexOf(value) === -1)
  207. }
  208. if (hasValue && valueOutOfScope && !(attributes[name].allowNull === true && values[attrName] === null)) {
  209. throw new Error('Value "' + value + '" for ENUM ' + name + ' is out of allowed scope. Allowed values: ' + attributes[name].values.join(', '))
  210. }
  211. }
  212. }
  213. }
  214. })
  215. // if you call "define" multiple times for the same daoName, do not clutter the factory
  216. if(this.isDefined(daoName)) {
  217. this.daoFactoryManager.removeDAO(this.daoFactoryManager.getDAO(daoName))
  218. }
  219. var factory = new DAOFactory(daoName, attributes, options)
  220. this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
  221. return factory
  222. }
  223. /**
  224. Fetch a DAO factory
  225. @param {String} daoName The name of a model defined with Sequelize.define
  226. @returns {DAOFactory} The DAOFactory for daoName
  227. */
  228. Sequelize.prototype.model = function(daoName) {
  229. if(!this.isDefined(daoName)) {
  230. throw new Error(daoName + ' has not been defined')
  231. }
  232. return this.daoFactoryManager.getDAO(daoName)
  233. }
  234. Sequelize.prototype.isDefined = function(daoName) {
  235. var daos = this.daoFactoryManager.daos
  236. return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
  237. }
  238. Sequelize.prototype.import = function(path) {
  239. // is it a relative path?
  240. if (Path.normalize(path).indexOf(path.sep) !== 0) {
  241. // make path relative to the caller
  242. var callerFilename = Utils.stack()[1].getFileName()
  243. , callerPath = Path.dirname(callerFilename)
  244. path = Path.resolve(callerPath, path)
  245. }
  246. if (!this.importCache[path]) {
  247. var defineCall = (arguments.length > 1 ? arguments[1] : require(path))
  248. this.importCache[path] = defineCall(this, DataTypes)
  249. }
  250. return this.importCache[path]
  251. }
  252. Sequelize.prototype.migrate = function(options) {
  253. return this.getMigrator().migrate(options)
  254. }
  255. Sequelize.prototype.query = function(sql, callee, options, replacements) {
  256. if (arguments.length === 4) {
  257. if (Array.isArray(replacements)) {
  258. sql = Utils.format([sql].concat(replacements), this.options.dialect)
  259. }
  260. else {
  261. sql = Utils.formatNamedParameters(sql, replacements, this.options.dialect)
  262. }
  263. } else if (arguments.length === 3) {
  264. options = options
  265. } else if (arguments.length === 2) {
  266. options = {}
  267. } else {
  268. options = { raw: true }
  269. }
  270. options = Utils._.extend(Utils._.clone(this.options.query), options)
  271. options = Utils._.defaults(options, {
  272. logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log,
  273. type: (sql.toLowerCase().indexOf('select') === 0) ? QueryTypes.SELECT : false
  274. })
  275. return this.transactionManager.query(sql, callee, options)
  276. }
  277. Sequelize.prototype.createSchema = function(schema) {
  278. var chainer = new Utils.QueryChainer()
  279. chainer.add(this.getQueryInterface().createSchema(schema))
  280. return chainer.run()
  281. }
  282. Sequelize.prototype.showAllSchemas = function() {
  283. var chainer = new Utils.QueryChainer()
  284. chainer.add(this.getQueryInterface().showAllSchemas())
  285. return chainer.run()
  286. }
  287. Sequelize.prototype.dropSchema = function(schema) {
  288. var chainer = new Utils.QueryChainer()
  289. chainer.add(this.getQueryInterface().dropSchema(schema))
  290. return chainer.run()
  291. }
  292. Sequelize.prototype.dropAllSchemas = function() {
  293. var self = this
  294. var chainer = new Utils.QueryChainer()
  295. chainer.add(self.getQueryInterface().dropAllSchemas())
  296. return chainer.run()
  297. }
  298. Sequelize.prototype.sync = function(options) {
  299. options = options || {}
  300. if (this.options.sync) {
  301. options = Utils._.extend({}, this.options.sync, options)
  302. }
  303. options.logging = options.logging === undefined ? false : options.logging
  304. var chainer = new Utils.QueryChainer()
  305. // Topologically sort by foreign key constraints to give us an appropriate
  306. // creation order
  307. this.daoFactoryManager.forEachDAO(function(dao, daoName) {
  308. if (dao) {
  309. chainer.add(dao, 'sync', [options])
  310. } else {
  311. // DB should throw an SQL error if referencing inexistant table
  312. }
  313. })
  314. return chainer.runSerially()
  315. }
  316. Sequelize.prototype.drop = function() {
  317. var self = this
  318. return new Utils.CustomEventEmitter(function(emitter) {
  319. var chainer = new Utils.QueryChainer
  320. self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })
  321. chainer
  322. .run()
  323. .success(function() { emitter.emit('success', null) })
  324. .error(function(err) { emitter.emit('error', err) })
  325. }).run()
  326. }
  327. Sequelize.prototype.authenticate = function() {
  328. var self = this
  329. return new Utils.CustomEventEmitter(function(emitter) {
  330. self
  331. .query('SELECT 1+1 AS result', null, { raw: true, plain: true })
  332. .complete(function(err, result) {
  333. if (!!err) {
  334. emitter.emit('error', new Error(err))
  335. } else {
  336. emitter.emit('success')
  337. }
  338. })
  339. }).run()
  340. }
  341. Sequelize.prototype.validate = Sequelize.prototype.authenticate;
  342. Sequelize.fn = Sequelize.prototype.fn = function (fn) {
  343. return new Utils.fn(fn, Array.prototype.slice.call(arguments, 1))
  344. }
  345. Sequelize.col = Sequelize.prototype.col = function (col) {
  346. return new Utils.col(col)
  347. }
  348. Sequelize.cast = Sequelize.prototype.cast = function (val, type) {
  349. return new Utils.cast(val, type)
  350. }
  351. Sequelize.literal = Sequelize.prototype.literal = function (val) {
  352. return new Utils.literal(val)
  353. }
  354. Sequelize.asIs = Sequelize.prototype.asIs = function (val) {
  355. return new Utils.asIs(val)
  356. }
  357. Sequelize.and = Sequelize.prototype.and = function() {
  358. return new Utils.and(Array.prototype.slice.call(arguments))
  359. }
  360. Sequelize.or = Sequelize.prototype.or = function() {
  361. return new Utils.or(Array.prototype.slice.call(arguments))
  362. }
  363. Sequelize.prototype.transaction = function(_options, _callback) {
  364. var options = (typeof _options === 'function') ? {} : _options
  365. , callback = (typeof _options === 'function') ? _options : _callback
  366. , wantsError = (callback.length === 2)
  367. , transaction = new Transaction(this, options)
  368. , self = this
  369. Utils.tick(function() {
  370. if (wantsError) {
  371. transaction.error(function(err) {
  372. callback(err, transaction)
  373. })
  374. }
  375. transaction.prepareEnvironment(function() {
  376. wantsError ? callback(null, transaction) : callback(transaction)
  377. })
  378. })
  379. return transaction
  380. }
  381. return Sequelize
  382. })()