1
1
'use strict'
2
2
3
+ const lsp = str => ( / ^ [ \s \n \r ] / . test ( str ) ? str : ' ' + str )
4
+ const push = ( str , val , statement , spaced ) => {
5
+ const { strings } = statement
6
+ str [ str . length - 1 ] += spaced ? lsp ( strings [ 0 ] ) : strings [ 0 ]
7
+ str . push ( ...strings . slice ( 1 ) )
8
+ val . push ( ...( statement . values || statement . bind ) )
9
+ }
10
+
3
11
class SQLStatement {
4
12
/**
5
13
* @param {string[] } strings
@@ -25,20 +33,19 @@ class SQLStatement {
25
33
* @returns {this }
26
34
*/
27
35
append ( statement ) {
36
+ const { strings } = this
28
37
if ( statement instanceof SQLStatement ) {
29
- this . strings [ this . strings . length - 1 ] += statement . strings [ 0 ]
30
- this . strings . push . apply ( this . strings , statement . strings . slice ( 1 ) )
31
- const list = this . values || this . bind
32
- list . push . apply ( list , statement . values )
38
+ push ( strings , this . values || this . bind , statement , true )
33
39
} else {
34
- this . strings [ this . strings . length - 1 ] += statement
40
+ strings [ strings . length - 1 ] += lsp ( statement )
35
41
}
36
42
return this
37
43
}
38
44
39
45
/**
40
46
* Use a prepared statement with Sequelize.
41
- * Makes `query` return a query with `$n` syntax instead of `?` and switches the `values` key name to `bind`
47
+ * Makes `query` return a query with `$n` syntax instead of `?`
48
+ * and switches the `values` key name to `bind`
42
49
* @param {boolean } [value=true] value If omitted, defaults to `true`
43
50
* @returns this
44
51
*/
@@ -74,16 +81,54 @@ Object.defineProperty(SQLStatement.prototype, 'sql', {
74
81
} ,
75
82
} )
76
83
84
+ class SQLQuote {
85
+ constructor ( chr ) {
86
+ this . char = chr
87
+ this . escape = new RegExp ( chr , 'g' )
88
+ }
89
+ }
90
+
77
91
/**
78
92
* @param {string[] } strings
79
93
* @param {...any } values
80
94
* @returns {SQLStatement }
81
95
*/
82
- function SQL ( strings ) {
83
- return new SQLStatement ( strings . slice ( 0 ) , Array . from ( arguments ) . slice ( 1 ) )
96
+ function SQL ( tpl , ...val ) {
97
+ const strings = [ tpl [ 0 ] ]
98
+ const values = [ ]
99
+ for ( let { length } = tpl , prev = tpl [ 0 ] , j = 0 , i = 1 ; i < length ; i ++ ) {
100
+ const current = tpl [ i ]
101
+ const value = val [ i - 1 ]
102
+ if ( / ^ ( ' | " ) / . test ( current ) && RegExp . $1 === prev . slice ( - 1 ) ) {
103
+ if ( this instanceof SQLQuote ) {
104
+ strings [ j ] = [
105
+ strings [ j ] . slice ( 0 , - 1 ) ,
106
+ String ( value ) . replace ( this . escape , '\\$&' ) ,
107
+ current . slice ( 1 )
108
+ ] . join ( this . char )
109
+ } else {
110
+ throw new Error ( `unable to escape ${ value } ` )
111
+ }
112
+ } else {
113
+ if ( value instanceof SQLStatement ) {
114
+ push ( strings , values , value , false )
115
+ j = strings . length - 1
116
+ strings [ j ] += current
117
+ } else {
118
+ values . push ( value )
119
+ j = strings . push ( current ) - 1
120
+ }
121
+ prev = strings [ j ]
122
+ }
123
+ }
124
+ return new SQLStatement ( strings , values )
84
125
}
85
126
127
+ SQL . withQuote = quote => {
128
+ const sqlQuote = new SQLQuote ( quote )
129
+ return ( ...args ) => SQL . apply ( sqlQuote , args )
130
+ }
131
+ SQL . SQL = SQL
132
+ SQL . default = SQL
133
+ SQL . SQLStatement = SQLStatement
86
134
module . exports = SQL
87
- module . exports . SQL = SQL
88
- module . exports . default = SQL
89
- module . exports . SQLStatement = SQLStatement
0 commit comments