1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | const Base = require('../base/Base');
|
7 |
|
8 | module.exports = class MysqlBuilder extends Base {
|
9 |
|
10 | static getConstants () {
|
11 | return {
|
12 | CONDITION_BUILDERS: {
|
13 | 'AND': 'buildLogicCondition',
|
14 | 'OR': 'buildLogicCondition',
|
15 | 'EQUAL': 'buildEqualCondition',
|
16 | 'NOT EQUAL': 'buildNotEqualCondition',
|
17 | 'BETWEEN': 'buildBetweenCondition',
|
18 | 'NOT BETWEEN': 'buildNotBetweenCondition',
|
19 | 'IN': 'buildInCondition',
|
20 | 'NOT IN': 'buildNotInCondition',
|
21 | 'LIKE': 'buildLikeCondition',
|
22 | 'NOT LIKE': 'buildNotLikeCondition',
|
23 | 'ID': 'buildIdCondition',
|
24 | 'NOT ID': 'buildNotIdCondition',
|
25 | 'FALSE': 'buildFalseCondition',
|
26 | 'EMPTY': 'buildEmptyCondition',
|
27 | 'NOT EMPTY': 'buildNotEmptyCondition',
|
28 | 'NULL': 'buildNullCondition',
|
29 | 'NOT NULL': 'buildNotNullCondition',
|
30 | 'EXISTS': 'buildExistsCondition',
|
31 | 'NOT EXISTS': 'buildNotExistsCondition'
|
32 | },
|
33 | SIMPLE_OPERATORS: {
|
34 | '=': ' = ',
|
35 | '!=': ' != ',
|
36 | '>': ' > ',
|
37 | '>=': ' >= ',
|
38 | '<': ' < ',
|
39 | '<=': ' <= '
|
40 | }
|
41 | };
|
42 | }
|
43 |
|
44 | escape (value) {
|
45 | return this.db.escape(value);
|
46 | }
|
47 |
|
48 | normalizeField (field) {
|
49 | if (typeof field !== 'string') {
|
50 | throw new Error('Invalid field name');
|
51 | }
|
52 | return this.db.escapeId(field);
|
53 | }
|
54 |
|
55 | build (query) {
|
56 | query.cmd = {
|
57 | from: this.db.escapeId(query._from),
|
58 | select: this.buildSelect(query._select),
|
59 | where: this.buildWhere(query._where),
|
60 | order: this.buildOrder(query._order),
|
61 | offset: query._offset,
|
62 | limit: query._limit,
|
63 | };
|
64 | query.afterBuild();
|
65 | return query.cmd;
|
66 | }
|
67 |
|
68 | buildSelect (select) {
|
69 | return select ? this.db.escapeId(select) : '*';
|
70 | }
|
71 |
|
72 | buildOrder (order) {
|
73 | if (!order) {
|
74 | return null;
|
75 | }
|
76 | const result = [];
|
77 | for (const key of Object.keys(order)) {
|
78 | result.push(`${this.db.escapeId(key)} ${order[key] === 1 ? 'ASC' : 'DESC'}`);
|
79 | }
|
80 | return result.length ? result.join(',') : null;
|
81 | }
|
82 |
|
83 | stringify (cmd) {
|
84 | let sql = `SELECT ${cmd.select} FROM ${cmd.from}`;
|
85 | if (cmd.where) {
|
86 | sql += cmd.where;
|
87 | }
|
88 | if (cmd.order) {
|
89 | sql +=` ORDER BY ${cmd.order}`;
|
90 | }
|
91 | if (cmd.offset) {
|
92 | sql +=` LIMIT ${cmd.offset},${cmd.limit ? cmd.limit : 9999999999}`;
|
93 | } else if (cmd.limit) {
|
94 | sql +=` LIMIT ${cmd.limit}`;
|
95 | }
|
96 | return sql;
|
97 | }
|
98 |
|
99 | buildWhere (condition) {
|
100 | if (!condition) {
|
101 | return '';
|
102 | }
|
103 | if (typeof condition === 'object') {
|
104 | condition = this.buildCondition(condition);
|
105 | }
|
106 | return ' WHERE '+ condition;
|
107 | }
|
108 |
|
109 | buildCondition (data) {
|
110 | if (!Array.isArray(data)) {
|
111 | return this.buildHashCondition(data);
|
112 | }
|
113 | return Object.prototype.hasOwnProperty.call(this.CONDITION_BUILDERS, data[0])
|
114 | ? this[this.CONDITION_BUILDERS[data[0]]](...data)
|
115 | : this.buildSimpleCondition(...data);
|
116 | }
|
117 |
|
118 | buildHashCondition (data) {
|
119 | if (!data) {
|
120 | return '';
|
121 | }
|
122 | const result = [];
|
123 | for (const key of Object.keys(data)) {
|
124 | const field = this.normalizeField(key);
|
125 | if (Array.isArray(data[key])) {
|
126 | data[key].length
|
127 | ? result.push(`${field} IN (${this.escape(data[key])})`)
|
128 | : result.push(`${field} IS NULL`);
|
129 | } else if (data[key] === null) {
|
130 | result.push(`${field} IS NULL`);
|
131 | } else {
|
132 | result.push(`${field}=${this.escape(data[key])}`);
|
133 | }
|
134 | }
|
135 | return result.join(' AND ');
|
136 | }
|
137 |
|
138 | buildSimpleCondition (operator, field, value) {
|
139 | if (!Object.prototype.hasOwnProperty.call(this.SIMPLE_OPERATORS, operator)) {
|
140 | throw new Error('Invalid simple operator');
|
141 | }
|
142 | field = this.normalizeField(field);
|
143 | return value === null
|
144 | ? field + (operator === '=' ? ' IS NULL' : ' IS NOT NULL')
|
145 | : field + this.SIMPLE_OPERATORS[operator] + this.escape(value);
|
146 | }
|
147 |
|
148 | buildLogicCondition (operator, ...operands) {
|
149 | const items = [];
|
150 | for (const operand of operands) {
|
151 | items.push(this.buildCondition(operand));
|
152 | }
|
153 | return '('+ items.join(operator === 'AND' ? ') AND (' : ') OR (') +')';
|
154 | }
|
155 |
|
156 | buildEqualCondition (operator, field, value) {
|
157 | return `${this.normalizeField(field)}=${this.escape(value)}`;
|
158 | }
|
159 |
|
160 | buildNotEqualCondition (operator, field, value) {
|
161 | return Array.isArray(value)
|
162 | ? this.buildNotInCondition()
|
163 | : `${this.normalizeField(field)}!=${this.escape(value)}`;
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 | buildInCondition (operator, field, value) {
|
169 | return Array.isArray(value) && value.length === 0
|
170 | ? `${this.normalizeField(field)} IS NULL`
|
171 | : `${this.normalizeField(field)} IN (${this.escape(value)})`;
|
172 | }
|
173 |
|
174 | buildNotInCondition (operator, field, value) {
|
175 | return Array.isArray(value) && value.length === 0
|
176 | ? `${this.normalizeField(field)} IS NOT NULL`
|
177 | : `${this.normalizeField(field)} NOT IN (${this.escape(value)})`;
|
178 | }
|
179 |
|
180 |
|
181 |
|
182 | buildLikeCondition (operator, field, value) {
|
183 | return `${this.normalizeField(field)} LIKE ${this.escape(value)}`;
|
184 | }
|
185 |
|
186 | buildNotLikeCondition (operator, field, value) {
|
187 | return `${this.normalizeField(field)} NOT LIKE ${this.escape(value)}`;
|
188 | }
|
189 |
|
190 |
|
191 |
|
192 | buildBetweenCondition (operator, field, min, max) {
|
193 | return `${this.normalizeField(field)} BETWEEN ${this.escape(min)} AND ${this.escape(max)}`;
|
194 | }
|
195 |
|
196 | buildNotBetweenCondition (operator, field, min, max) {
|
197 | return `${this.normalizeField(field)} NOT BETWEEN ${this.escape(min)} AND ${this.escape(max)}`;
|
198 | }
|
199 |
|
200 |
|
201 |
|
202 | buildIdCondition (operator, field, value) {
|
203 | if (value === null || value === undefined) {
|
204 | return this.buildNullCondition(operator, field);
|
205 | }
|
206 | return Array.isArray(value)
|
207 | ? `${this.normalizeField(field)} IN (${this.escape(value)})`
|
208 | : `${this.normalizeField(field)}=${this.escape(value)}`;
|
209 | }
|
210 |
|
211 | buildNotIdCondition (operator, field, value) {
|
212 | if (value === null || value === undefined) {
|
213 | return this.buildNotNullCondition(operator, field);
|
214 | }
|
215 | return Array.isArray(value)
|
216 | ? `${this.normalizeField(field)} NOT IN (${this.escape(value)})`
|
217 | : `${this.normalizeField(field)}=${this.escape(value)}`;
|
218 | }
|
219 |
|
220 |
|
221 |
|
222 | buildFalseCondition () {
|
223 | return ' FALSE ';
|
224 | }
|
225 |
|
226 |
|
227 |
|
228 | buildEmptyCondition (operator, field) {
|
229 | field = this.normalizeField(field);
|
230 | return `${field} IS NULL OR ${field} = ''`;
|
231 | }
|
232 |
|
233 | buildNotEmptyCondition (operator, field) {
|
234 | field = this.normalizeField(field);
|
235 | return `${field} IS NOT NULL AND ${field} != ''`;
|
236 | }
|
237 |
|
238 |
|
239 |
|
240 | buildNullCondition (operator, field) {
|
241 | return `${this.normalizeField(field)} IS NULL`;
|
242 | }
|
243 |
|
244 | buildNotNullCondition (operator, field) {
|
245 | return `${this.normalizeField(field)} IS NOT NULL`;
|
246 | }
|
247 |
|
248 |
|
249 |
|
250 | buildExistsCondition (operator, field) {
|
251 | return `${this.normalizeField(field)} IS NULL`;
|
252 | }
|
253 |
|
254 | buildNotExistsCondition (operator, field) {
|
255 | return `${this.normalizeField(field)} IS NOT NULL`;
|
256 | }
|
257 | };
|
258 | module.exports.init(); |
\ | No newline at end of file |