adapter.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // ███████╗ █████╗ ██╗██╗ ███████╗ ███╗ ███╗██╗ ██╗███████╗ ██████╗ ██╗
  2. // ██╔════╝██╔══██╗██║██║ ██╔════╝ ████╗ ████║╚██╗ ██╔╝██╔════╝██╔═══██╗██║
  3. // ███████╗███████║██║██║ ███████╗ ██╔████╔██║ ╚████╔╝ ███████╗██║ ██║██║
  4. // ╚════██║██╔══██║██║██║ ╚════██║ ██║╚██╔╝██║ ╚██╔╝ ╚════██║██║▄▄ ██║██║
  5. // ███████║██║ ██║██║███████╗███████║ ██║ ╚═╝ ██║ ██║ ███████║╚██████╔╝███████╗
  6. // ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══▀▀═╝ ╚══════╝
  7. //
  8. // An adapter for MySQL and Waterline
  9. var _ = require('@sailshq/lodash');
  10. var async = require('async');
  11. var Helpers = require('../helpers');
  12. module.exports = (function sailsMySQL() {
  13. // Keep track of all the datastores used by the app
  14. var datastores = {};
  15. // Keep track of all the connection model definitions
  16. var modelDefinitions = {};
  17. var adapter = {
  18. identity: 'sails-mysql',
  19. // Waterline Adapter API Version
  20. adapterApiVersion: 1,
  21. defaults: {
  22. host: 'localhost',
  23. port: 3306,
  24. schema: true
  25. },
  26. // ╔═╗═╗ ╦╔═╗╔═╗╔═╗╔═╗ ┌─┐┬─┐┬┬ ┬┌─┐┌┬┐┌─┐
  27. // ║╣ ╔╩╦╝╠═╝║ ║╚═╗║╣ ├─┘├┬┘│└┐┌┘├─┤ │ ├┤
  28. // ╚═╝╩ ╚═╩ ╚═╝╚═╝╚═╝ ┴ ┴└─┴ └┘ ┴ ┴ ┴ └─┘
  29. // ┌┬┐┌─┐┌┬┐┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐┌─┐
  30. // ││├─┤ │ ├─┤└─┐ │ │ │├┬┘├┤ └─┐
  31. // ─┴┘┴ ┴ ┴ ┴ ┴└─┘ ┴ └─┘┴└─└─┘└─┘
  32. // This allows outside access to the connection manager.
  33. datastores: datastores,
  34. // ╦═╗╔═╗╔═╗╦╔═╗╔╦╗╔═╗╦═╗ ┌┬┐┌─┐┌┬┐┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐
  35. // ╠╦╝║╣ ║ ╦║╚═╗ ║ ║╣ ╠╦╝ ││├─┤ │ ├─┤└─┐ │ │ │├┬┘├┤
  36. // ╩╚═╚═╝╚═╝╩╚═╝ ╩ ╚═╝╩╚═ ─┴┘┴ ┴ ┴ ┴ ┴└─┘ ┴ └─┘┴└─└─┘
  37. // Register a datastore config and generate a connection manager for it.
  38. registerDatastore: function registerDatastore(datastoreConfig, models, cb) {
  39. var identity = datastoreConfig.identity;
  40. if (!identity) {
  41. return cb(new Error('Invalid datastore config. A datastore should contain a unique identity property.'));
  42. }
  43. try {
  44. Helpers.registerDataStore({
  45. identity: identity,
  46. config: datastoreConfig,
  47. models: models,
  48. datastores: datastores,
  49. modelDefinitions: modelDefinitions
  50. }).execSync();
  51. } catch (e) {
  52. setImmediate(function done() {
  53. return cb(e);
  54. });
  55. return;
  56. }
  57. setImmediate(function done() {
  58. return cb();
  59. });
  60. },
  61. // ╔╦╗╔═╗╔═╗╦═╗╔╦╗╔═╗╦ ╦╔╗╔ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
  62. // ║ ║╣ ╠═╣╠╦╝ ║║║ ║║║║║║║ │ │ │││││││├┤ │ │ ││ ││││
  63. // ╩ ╚═╝╩ ╩╩╚══╩╝╚═╝╚╩╝╝╚╝ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
  64. // Destroy a manager and close any connections in it's pool.
  65. teardown: function teardown(identity, cb) {
  66. var datastoreIdentities = [];
  67. // If no specific identity was sent, teardown all the datastores
  68. if (!identity || identity === null) {
  69. datastoreIdentities = datastoreIdentities.concat(_.keys(datastores));
  70. } else {
  71. datastoreIdentities.push(identity);
  72. }
  73. // Teardown each datastore identity manager
  74. async.eachSeries(datastoreIdentities, function teardownDatastore(datastoreIdentity, next) {
  75. Helpers.teardown({
  76. identity: datastoreIdentity,
  77. datastores: datastores,
  78. modelDefinitions: modelDefinitions
  79. }).switch({
  80. error: function error(err) {
  81. return next(err);
  82. },
  83. success: function success() {
  84. return next();
  85. }
  86. });
  87. }, function asyncCb(err) {
  88. cb(err);
  89. });
  90. },
  91. // ██████╗ ██████╗ ██╗
  92. // ██╔══██╗██╔═══██╗██║
  93. // ██║ ██║██║ ██║██║
  94. // ██║ ██║██║▄▄ ██║██║
  95. // ██████╔╝╚██████╔╝███████╗
  96. // ╚═════╝ ╚══▀▀═╝ ╚══════╝
  97. //
  98. // Methods related to manipulating data stored in the database.
  99. // ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ ┬─┐┌─┐┌─┐┌─┐┬─┐┌┬┐
  100. // ║ ╠╦╝║╣ ╠═╣ ║ ║╣ ├┬┘├┤ │ │ │├┬┘ ││
  101. // ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝ ┴└─└─┘└─┘└─┘┴└──┴┘
  102. // Add a new row to the table
  103. create: function create(datastoreName, query, cb) {
  104. var datastore = datastores[datastoreName];
  105. var models = modelDefinitions[datastoreName];
  106. Helpers.create({
  107. datastore: datastore,
  108. models: models,
  109. query: query
  110. }).switch({
  111. error: function error(err) {
  112. return cb(err);
  113. },
  114. notUnique: function error(errInfo) {
  115. var e = new Error(errInfo.message);
  116. e.footprint = errInfo.footprint;
  117. return cb(e);
  118. },
  119. success: function success(report) {
  120. var record = report && report.record || undefined;
  121. return cb(undefined, record);
  122. }
  123. });
  124. },
  125. // ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ ╔═╗╔═╗╔═╗╦ ╦ ┬─┐┌─┐┌─┐┌─┐┬─┐┌┬┐
  126. // ║ ╠╦╝║╣ ╠═╣ ║ ║╣ ║╣ ╠═╣║ ╠═╣ ├┬┘├┤ │ │ │├┬┘ ││
  127. // ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝ ╚═╝╩ ╩╚═╝╩ ╩ ┴└─└─┘└─┘└─┘┴└──┴┘
  128. // Add multiple new rows to the table
  129. createEach: function createEach(datastoreName, query, cb) {
  130. var datastore = datastores[datastoreName];
  131. var models = modelDefinitions[datastoreName];
  132. Helpers.createEach({
  133. datastore: datastore,
  134. models: models,
  135. query: query
  136. }).switch({
  137. error: function error(err) {
  138. return cb(err);
  139. },
  140. notUnique: function error(errInfo) {
  141. var e = new Error(errInfo.message);
  142. e.footprint = errInfo.footprint;
  143. return cb(e);
  144. },
  145. success: function success(report) {
  146. var records = report && report.records || undefined;
  147. return cb(undefined, records);
  148. }
  149. });
  150. },
  151. // ╔═╗╔═╗╦ ╔═╗╔═╗╔╦╗ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  152. // ╚═╗║╣ ║ ║╣ ║ ║ │─┼┐│ │├┤ ├┬┘└┬┘
  153. // ╚═╝╚═╝╩═╝╚═╝╚═╝ ╩ └─┘└└─┘└─┘┴└─ ┴
  154. // Select Query Logic
  155. find: function find(datastoreName, query, cb) {
  156. var datastore = datastores[datastoreName];
  157. var models = modelDefinitions[datastoreName];
  158. Helpers.select({
  159. datastore: datastore,
  160. models: models,
  161. query: query
  162. }).switch({
  163. error: function error(err) {
  164. return cb(err);
  165. },
  166. success: function success(report) {
  167. return cb(undefined, report.records);
  168. }
  169. });
  170. },
  171. // ╦ ╦╔═╗╔╦╗╔═╗╔╦╗╔═╗ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  172. // ║ ║╠═╝ ║║╠═╣ ║ ║╣ │─┼┐│ │├┤ ├┬┘└┬┘
  173. // ╚═╝╩ ═╩╝╩ ╩ ╩ ╚═╝ └─┘└└─┘└─┘┴└─ ┴
  174. // Update one or more models in the table
  175. update: function update(datastoreName, query, cb) {
  176. var datastore = datastores[datastoreName];
  177. var models = modelDefinitions[datastoreName];
  178. Helpers.update({
  179. datastore: datastore,
  180. models: models,
  181. query: query
  182. }).switch({
  183. error: function error(err) {
  184. return cb(err);
  185. },
  186. notUnique: function error(errInfo) {
  187. var e = new Error(errInfo.message);
  188. e.footprint = errInfo.footprint;
  189. return cb(e);
  190. },
  191. success: function success(report) {
  192. if (report) {
  193. return cb(undefined, report.records);
  194. }
  195. return cb();
  196. }
  197. });
  198. },
  199. // ╔╦╗╔═╗╔═╗╔╦╗╦═╗╔═╗╦ ╦ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  200. // ║║║╣ ╚═╗ ║ ╠╦╝║ ║╚╦╝ │─┼┐│ │├┤ ├┬┘└┬┘
  201. // ═╩╝╚═╝╚═╝ ╩ ╩╚═╚═╝ ╩ └─┘└└─┘└─┘┴└─ ┴
  202. // Delete one or more records in a table
  203. destroy: function destroy(datastoreName, query, cb) {
  204. var datastore = datastores[datastoreName];
  205. var models = modelDefinitions[datastoreName];
  206. Helpers.destroy({
  207. datastore: datastore,
  208. models: models,
  209. query: query
  210. }).switch({
  211. error: function error(err) {
  212. return cb(err);
  213. },
  214. success: function success(report) {
  215. if (report) {
  216. return cb(undefined, report.records);
  217. }
  218. return cb();
  219. }
  220. });
  221. },
  222. // ╔╗╔╔═╗╔╦╗╦╦ ╦╔═╗ ┬┌─┐┬┌┐┌ ┌─┐┬ ┬┌─┐┌─┐┌─┐┬─┐┌┬┐
  223. // ║║║╠═╣ ║ ║╚╗╔╝║╣ ││ │││││ └─┐│ │├─┘├─┘│ │├┬┘ │
  224. // ╝╚╝╩ ╩ ╩ ╩ ╚╝ ╚═╝ └┘└─┘┴┘└┘ └─┘└─┘┴ ┴ └─┘┴└─ ┴
  225. // Build up native joins to run on the adapter.
  226. join: function join(datastoreName, query, cb) {
  227. var datastore = datastores[datastoreName];
  228. var models = modelDefinitions[datastoreName];
  229. Helpers.join({
  230. datastore: datastore,
  231. models: models,
  232. query: query
  233. }).switch({
  234. error: function error(err) {
  235. return cb(err);
  236. },
  237. success: function success(report) {
  238. return cb(undefined, report);
  239. }
  240. });
  241. },
  242. // ╔═╗╦ ╦╔═╗ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  243. // ╠═╣╚╗╔╝║ ╦ │─┼┐│ │├┤ ├┬┘└┬┘
  244. // ╩ ╩ ╚╝ ╚═╝ └─┘└└─┘└─┘┴└─ ┴
  245. // Find out the average of the query.
  246. avg: function avg(datastoreName, query, cb) {
  247. var datastore = datastores[datastoreName];
  248. var models = modelDefinitions[datastoreName];
  249. Helpers.avg({
  250. datastore: datastore,
  251. models: models,
  252. query: query
  253. }).switch({
  254. error: function error(err) {
  255. return cb(err);
  256. },
  257. success: function success(report) {
  258. return cb(undefined, report);
  259. }
  260. });
  261. },
  262. // ╔═╗╦ ╦╔╦╗ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  263. // ╚═╗║ ║║║║ │─┼┐│ │├┤ ├┬┘└┬┘
  264. // ╚═╝╚═╝╩ ╩ └─┘└└─┘└─┘┴└─ ┴
  265. // Find out the sum of the query.
  266. sum: function sum(datastoreName, query, cb) {
  267. var datastore = datastores[datastoreName];
  268. var models = modelDefinitions[datastoreName];
  269. Helpers.sum({
  270. datastore: datastore,
  271. models: models,
  272. query: query
  273. }).switch({
  274. error: function error(err) {
  275. return cb(err);
  276. },
  277. success: function success(report) {
  278. return cb(undefined, report);
  279. }
  280. });
  281. },
  282. // ╔═╗╔═╗╦ ╦╔╗╔╔╦╗ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
  283. // ║ ║ ║║ ║║║║ ║ │─┼┐│ │├┤ ├┬┘└┬┘
  284. // ╚═╝╚═╝╚═╝╝╚╝ ╩ └─┘└└─┘└─┘┴└─ ┴
  285. // Return the number of matching records.
  286. count: function count(datastoreName, query, cb) {
  287. var datastore = datastores[datastoreName];
  288. var models = modelDefinitions[datastoreName];
  289. Helpers.count({
  290. datastore: datastore,
  291. models: models,
  292. query: query
  293. }).switch({
  294. error: function error(err) {
  295. return cb(err);
  296. },
  297. success: function success(report) {
  298. return cb(undefined, report);
  299. }
  300. });
  301. },
  302. // ██████╗ ██████╗ ██╗
  303. // ██╔══██╗██╔══██╗██║
  304. // ██║ ██║██║ ██║██║
  305. // ██║ ██║██║ ██║██║
  306. // ██████╔╝██████╔╝███████╗
  307. // ╚═════╝ ╚═════╝ ╚══════╝
  308. //
  309. // Methods related to modifying the underlying data structure of the
  310. // database.
  311. // ╔╦╗╔═╗╔═╗╔═╗╦═╗╦╔╗ ╔═╗ ┌┬┐┌─┐┌┐ ┬ ┌─┐
  312. // ║║║╣ ╚═╗║ ╠╦╝║╠╩╗║╣ │ ├─┤├┴┐│ ├┤
  313. // ═╩╝╚═╝╚═╝╚═╝╩╚═╩╚═╝╚═╝ ┴ ┴ ┴└─┘┴─┘└─┘
  314. // Describe a table and get back a normalized model schema format.
  315. // (This is used to allow Sails to do auto-migrations)
  316. describe: function describe(datastoreName, tableName, cb, meta) {
  317. var datastore = datastores[datastoreName];
  318. Helpers.describe({
  319. datastore: datastore,
  320. tableName: tableName,
  321. meta: meta
  322. }).switch({
  323. error: function error(err) {
  324. return cb(err);
  325. },
  326. success: function success(report) {
  327. // Waterline expects the result to be undefined if the table doesn't
  328. // exist.
  329. if (_.keys(report.schema).length) {
  330. return cb(undefined, report.schema);
  331. }
  332. return cb();
  333. }
  334. });
  335. },
  336. // ╔╦╗╔═╗╔═╗╦╔╗╔╔═╗ ┌┬┐┌─┐┌┐ ┬ ┌─┐
  337. // ║║║╣ ╠╣ ║║║║║╣ │ ├─┤├┴┐│ ├┤
  338. // ═╩╝╚═╝╚ ╩╝╚╝╚═╝ ┴ ┴ ┴└─┘┴─┘└─┘
  339. // Build a new table in the database.
  340. // (This is used to allow Sails to do auto-migrations)
  341. define: function define(datastoreName, tableName, definition, cb, meta) {
  342. var datastore = datastores[datastoreName];
  343. Helpers.define({
  344. datastore: datastore,
  345. tableName: tableName,
  346. definition: definition,
  347. meta: meta
  348. }).switch({
  349. error: function error(err) {
  350. return cb(err);
  351. },
  352. success: function success() {
  353. return cb();
  354. }
  355. });
  356. },
  357. // ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ ┌─┐┌─┐┬ ┬┌─┐┌┬┐┌─┐
  358. // ║ ╠╦╝║╣ ╠═╣ ║ ║╣ └─┐│ ├─┤├┤ │││├─┤
  359. // ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝ └─┘└─┘┴ ┴└─┘┴ ┴┴ ┴
  360. // Create a new Postgres Schema (namespace) in the database.
  361. createSchema: function createSchema(datastoreName, schemaName, cb, meta) {
  362. var datastore = datastores[datastoreName];
  363. Helpers.createSchema({
  364. datastore: datastore,
  365. schemaName: schemaName,
  366. meta: meta
  367. }).switch({
  368. error: function error(err) {
  369. return cb(err);
  370. },
  371. success: function success() {
  372. return cb();
  373. }
  374. });
  375. },
  376. // ╔╦╗╦═╗╔═╗╔═╗ ┌┬┐┌─┐┌┐ ┬ ┌─┐
  377. // ║║╠╦╝║ ║╠═╝ │ ├─┤├┴┐│ ├┤
  378. // ═╩╝╩╚═╚═╝╩ ┴ ┴ ┴└─┘┴─┘└─┘
  379. // Remove a table from the database.
  380. drop: function drop(datastoreName, tableName, relations, cb, meta) {
  381. var datastore = datastores[datastoreName];
  382. Helpers.drop({
  383. datastore: datastore,
  384. tableName: tableName,
  385. meta: meta
  386. }).switch({
  387. error: function error(err) {
  388. return cb(err);
  389. },
  390. badConnection: function badConnection(err) {
  391. return cb(err);
  392. },
  393. success: function success() {
  394. return cb();
  395. }
  396. });
  397. },
  398. // ╔═╗╔═╗╔╦╗ ┌─┐┌─┐┌─┐ ┬ ┬┌─┐┌┐┌┌─┐┌─┐
  399. // ╚═╗║╣ ║ └─┐├┤ │─┼┐│ │├┤ ││││ ├┤
  400. // ╚═╝╚═╝ ╩ └─┘└─┘└─┘└└─┘└─┘┘└┘└─┘└─┘
  401. // Set a sequence in an auto-incrementing primary key to a known value.
  402. setSequence: function setSequence(datastoreName, sequenceName, sequenceValue, cb, meta) {
  403. var datastore = datastores[datastoreName];
  404. Helpers.setSequence({
  405. datastore: datastore,
  406. sequenceName: sequenceName,
  407. sequenceValue: sequenceValue,
  408. meta: meta
  409. }).switch({
  410. error: function error(err) {
  411. return cb(err);
  412. },
  413. success: function success() {
  414. return cb();
  415. }
  416. });
  417. },
  418. };
  419. return adapter;
  420. })();