1 | var Query = module.exports = function Query(db, opts, cb) {
|
2 |
|
3 | var self = this
|
4 | if (!(self instanceof Query)) {
|
5 | return new Query(db, opts, cb)
|
6 | }
|
7 |
|
8 | self.opts = opts
|
9 | self.db = db
|
10 |
|
11 |
|
12 | self.writable = []
|
13 | self.readOnly = []
|
14 |
|
15 | if (typeof cb !== 'function') cb = function() {}
|
16 |
|
17 | self.detectTopology(cb)
|
18 | }
|
19 |
|
20 |
|
21 | Query.prototype.getConn = function(options) {
|
22 | var self = this
|
23 | if (options.write) {
|
24 | return self.writable[randomInt(0, self.writable.length - 1)]
|
25 | }
|
26 | return self.readOnly[randomInt(0, self.readOnly.length - 1)]
|
27 | }
|
28 |
|
29 |
|
30 | Query.prototype.executeWrite = function(sql, values, options, cb) {
|
31 | options = options || {}
|
32 | options.write = true
|
33 | return this.execute(sql, values, options, cb)
|
34 | }
|
35 |
|
36 |
|
37 | Query.prototype.execute = function(sql, values, options, cb) {
|
38 | var self = this
|
39 | var conn
|
40 | var start
|
41 |
|
42 | cb = cb || self.db._promiseResolver()
|
43 |
|
44 | if (typeof options === 'function') {
|
45 | cb = options
|
46 | options = undefined
|
47 | }
|
48 |
|
49 | if (typeof sql !== 'string' && !isArray(sql)) {
|
50 | return cb(new Error('sql must be string or array multiline string'))
|
51 | }
|
52 |
|
53 | if (typeof values === 'function') {
|
54 | cb = values
|
55 | values = undefined
|
56 | }
|
57 |
|
58 | if (isArray(sql)) {
|
59 | sql = sql.join('\n')
|
60 | }
|
61 |
|
62 | if (values) {
|
63 | sql = self.queryValues(sql, values)
|
64 | }
|
65 |
|
66 | options = options || {}
|
67 | conn = self.getConn(options)
|
68 | start = Date.now()
|
69 |
|
70 | self.db._platform.getClient(conn, function(err, client, release) {
|
71 | if (err) return cb(err)
|
72 | self.db._platform.execute(client, sql, function(err, rows) {
|
73 | if (err) {
|
74 | if (!self.opts.silent) {
|
75 | console.error(sql)
|
76 | console.error('SQL ERROR:', err)
|
77 | }
|
78 | release()
|
79 | return cb(err)
|
80 | }
|
81 |
|
82 |
|
83 | release()
|
84 |
|
85 | if (self.opts.debug) {
|
86 | self.opts.debug('\n' + sql + '\n' + (Date.now() - start) + 'ms')
|
87 | }
|
88 | cb(null, rows)
|
89 | })
|
90 | })
|
91 | return cb.promise ? cb.promise : this
|
92 | }
|
93 |
|
94 |
|
95 | Query.prototype.detectTopology = function(cb) {
|
96 | var self = this
|
97 | self.db._platform.determineHosts(function(hosts) {
|
98 | if (hosts.readOnly.length === 0 && hosts.writable.length === 0) {
|
99 | return cb(new Error('Unable to connect to database.'))
|
100 | }
|
101 | if (hosts.readOnly.length === 0) {
|
102 | hosts.readOnly = hosts.writable
|
103 | }
|
104 | self.readOnly = hosts.readOnly
|
105 | self.writable = hosts.writable
|
106 | cb(null)
|
107 | })
|
108 | }
|
109 |
|
110 |
|
111 |
|
112 | Query.prototype.queryValues = function(query, values) {
|
113 | if (!values) return query;
|
114 | return query.replace(/\:(\w+)/g, function (txt, key) {
|
115 | if (values.hasOwnProperty(key)) {
|
116 | if (isObject(values[key])) {
|
117 | values[key] = JSON.stringify(values[key])
|
118 | }
|
119 | if (Array.isArray(values[key])) {
|
120 | return values[key].map(function (v) {
|
121 | return (typeof v === 'string') ? "'" + v + "'" : v
|
122 | })
|
123 | }
|
124 | if (typeof values[key] !== 'string') {
|
125 | return values[key]
|
126 | }
|
127 | return "'" + values[key].replace(/'/g, "''") + "'"
|
128 | }
|
129 | return txt
|
130 | });
|
131 | };
|
132 |
|
133 |
|
134 | function isArray(value) {
|
135 | return (Object.prototype.toString.call(value) === '[object Array]')
|
136 | }
|
137 |
|
138 | function isObject(value) {
|
139 | return (Object.prototype.toString.call(value) === '[object Object]')
|
140 | }
|
141 |
|
142 | function randomInt(min, max) {
|
143 | return Math.floor(Math.random()*(max-min+1)+min);
|
144 | }
|
145 |
|