quote-sql 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +34 -4
- data/lib/quote_sql/quoter.rb +102 -16
- data/lib/quote_sql/test.rb +104 -36
- data/lib/quote_sql.rb +26 -6
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17336c46db1b966512f67b1c4dae714d612460ae61c1f24d75d0fa79153422df
|
4
|
+
data.tar.gz: 8b510daed8f21c7733e0b841f0c11f60c8634abde062e3a81dc844b8ed4cb682
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cc8dd6b2e36c5f5e976027d1f25442cf3cbee76b8bf5bf05b827ba2693d2aed952273491b1abd6404f53482b6da1c0e88e5de5cc9d25ac3af56b65f649f0aa7
|
7
|
+
data.tar.gz: 3e4a135613d5c22963030754e1bc21d8ac3d3aaf210abecdd2d8eeb47893f96c3243702c29d138321a122c28ed571670e32c0f703462cf1a1ca41e6b925f6da5
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# QuoteSql - Tool to build and run SQL queries easier
|
2
2
|
I've built this library as an addition to ActiveRecord and Arel, however you can use it with any sql database and plain Ruby.
|
3
|
+
However currently it is just used with PostgreSQL.
|
3
4
|
|
4
5
|
Creating SQL queries and proper quoting becomes complicated especially when you need advanced queries.
|
5
6
|
|
@@ -8,7 +9,7 @@ I created this library while coding for different projects, and had lots of Here
|
|
8
9
|
|
9
10
|
My strategy is to segment SQL Queries in readable junks, which can be individually tested and then combine their sql to the final query.
|
10
11
|
|
11
|
-
QuoteSql is used in production, but is still
|
12
|
+
QuoteSql is used in production, but is still bleeding edge - and there is not a fully sync between doc and code.
|
12
13
|
|
13
14
|
If you think QuoteSql is interesting, let's chat!
|
14
15
|
Also if you have problems using it, just drop me a note.
|
@@ -23,6 +24,10 @@ Best Martin
|
|
23
24
|
`QuoteSql.new("SELECT %field__text").quote(field__text: 9).to_sql`
|
24
25
|
=> SELECT 9::TEXT
|
25
26
|
|
27
|
+
### Rails models
|
28
|
+
`QuoteSql.new(Users.limit(10).select("%columns")).quote(columns: ['first_name', 'last_name').to_sql`
|
29
|
+
=> SELECT first_name, last_name FROM users LIMIT 10
|
30
|
+
|
26
31
|
### Quoting of columns and table from a model - or an object responding to table_name and column_names or columns
|
27
32
|
`QuoteSql.new("SELECT %columns FROM %table_name").quote(table: User).to_sql`
|
28
33
|
=> SELECT "id",firstname","lastname",... FROM "users"
|
@@ -47,10 +52,18 @@ Values are be ordered in sequence of columns. Missing value entries are substitu
|
|
47
52
|
ON CONFLICT ("id") DO NOTHING
|
48
53
|
|
49
54
|
### Columns from a list
|
50
|
-
`QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c:
|
51
|
-
=> SELECT "a","b"."c",jsonb_build_object('d',
|
55
|
+
`QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c: {d: field}]).to_sql`
|
56
|
+
=> SELECT "a","b"."c",jsonb_build_object('d', field) AS c
|
57
|
+
|
58
|
+
`QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c: {d: field, nil: false}]).to_sql`
|
59
|
+
=> SELECT "a","b"."c",jsonb_strip_nulls(jsonb_build_object('d', 1)) AS c
|
60
|
+
|
61
|
+
|
62
|
+
### Execution of a query
|
63
|
+
`QuoteSql.new("Select 1 as abc").result` => [{:abc=>1}]
|
52
64
|
|
53
|
-
|
65
|
+
|
66
|
+
## Substitution of mixins with quoted values
|
54
67
|
In the SQL matches of `%foo` or `%{foo}` or `%foo_4_bar` or `%{foo_4_bar}` the *"mixins"*
|
55
68
|
are substituted with quoted values
|
56
69
|
the values are looked up from the options given in the quotes method
|
@@ -106,6 +119,16 @@ with optional array dimension
|
|
106
119
|
- +String+ value will become the expression, the key the AS {result: "SUM(*)"} => SUM(*) AS result
|
107
120
|
- +Proc+ are executed with the +QuoteSQL::Quoter+ object as parameter and added as raw SQL
|
108
121
|
|
122
|
+
## Shortcuts and functions
|
123
|
+
- `QuoteSQL("select %abc", abc: 1)` == `QuoteSql.new("select %abc").quote(abc: 1)`
|
124
|
+
- when you have in your initializer `String.include QuoteSql::Extension` you can do e.g. `"select %abc".quote_sql(abc: 1)`
|
125
|
+
- when you have in your initializer `ActiveRecord::Relation.include QuoteSql::Extension` you can do e.g. `Profile.limit(10).select('%abc').quote_sql(abc: 1)`
|
126
|
+
|
127
|
+
## Debug and dump
|
128
|
+
If you have pg_format installed you can get the resulting query inspected:
|
129
|
+
`QuoteSql.new("select %abc").quote(abc: 1).dsql`
|
130
|
+
|
131
|
+
|
109
132
|
## Installing
|
110
133
|
`gem install quote-sql`
|
111
134
|
or in Gemfile
|
@@ -121,3 +144,10 @@ Add this to config/initializers/quote_sql.rb
|
|
121
144
|
ActiveRecord::Relation.include QuoteSql::Extension
|
122
145
|
end
|
123
146
|
|
147
|
+
## Todos
|
148
|
+
- Functionalities not yet used in my production might not work
|
149
|
+
- More documentation
|
150
|
+
- Tests missing
|
151
|
+
- Missing functionalities
|
152
|
+
- Prepare
|
153
|
+
- which other - let me know!
|
data/lib/quote_sql/quoter.rb
CHANGED
@@ -5,15 +5,23 @@ class QuoteSql
|
|
5
5
|
@key, @quotable = key, quotable
|
6
6
|
end
|
7
7
|
|
8
|
+
def quotes
|
9
|
+
@qsql.quotes
|
10
|
+
end
|
11
|
+
|
8
12
|
attr_reader :key, :quotable
|
9
13
|
|
14
|
+
def name
|
15
|
+
@key.sub(/_[^_]+$/, '')
|
16
|
+
end
|
17
|
+
|
10
18
|
def to_sql
|
11
19
|
return @quotable.call(self) if @quotable.is_a? Proc
|
12
20
|
case key.to_s
|
13
21
|
when /(?:^|(.*)_)table$/i
|
14
22
|
table
|
15
23
|
when /(?:^|(.*)_)columns?$/i
|
16
|
-
|
24
|
+
columns
|
17
25
|
when /(?:^|(.*)_)(table_name?s?)$/i
|
18
26
|
table_name
|
19
27
|
when /(?:^|(.*)_)(column_name?s?)$/i
|
@@ -22,10 +30,12 @@ class QuoteSql
|
|
22
30
|
ident_name
|
23
31
|
when /(?:^|(.*)_)constraints?$/i
|
24
32
|
quotable.to_s
|
25
|
-
when /(?:^|(.*)_)(raw|sql)$/
|
33
|
+
when /(?:^|(.*)_)(raw|sql)$/i
|
26
34
|
quotable.to_s
|
27
|
-
when
|
28
|
-
|
35
|
+
when /^(.+)_values$/i
|
36
|
+
data_values
|
37
|
+
when /values$/i
|
38
|
+
insert_values
|
29
39
|
else
|
30
40
|
quote
|
31
41
|
end
|
@@ -56,24 +66,75 @@ class QuoteSql
|
|
56
66
|
|
57
67
|
end
|
58
68
|
|
59
|
-
def
|
69
|
+
def data_values(item = @quotable)
|
70
|
+
item = Array(item).compact
|
71
|
+
column_names = @qsql.quotes[:"#{name}_columns"].dup
|
72
|
+
if column_names.is_a? Hash
|
73
|
+
types = column_names.values.map { "::#{_1.upcase}" if _1 }
|
74
|
+
column_names = column_names.keys
|
75
|
+
end
|
76
|
+
if item.all? { _1.is_a?(Hash) }
|
77
|
+
column_names ||= item.flat_map { _1.keys.sort }.uniq
|
78
|
+
item.map! { _1.fetch_values(*column_names) {} }
|
79
|
+
end
|
80
|
+
if item.all? { _1.is_a?(Array) }
|
81
|
+
length, overflow = item.map { _1.length }.uniq
|
82
|
+
raise ArgumentError, "all values need to have the same length" if overflow
|
83
|
+
column_names ||= (1..length).map{"column#{_1}"}
|
84
|
+
raise ArgumentError, "#{name}_columns and value lengths need to be the same" if column_names.length != length
|
85
|
+
values = item.map { value(_1) }
|
86
|
+
else
|
87
|
+
raise ArgumentError, "Either all type Hash or Array"
|
88
|
+
end
|
89
|
+
if types.present?
|
90
|
+
value = values[0][1..-2].split(/\s*,\s*/)
|
91
|
+
types.each_with_index { value[_2] << _1 || ""}
|
92
|
+
values[0] = "(" + value.join(",") + ")"
|
93
|
+
end
|
94
|
+
# values[0] { _1 << types[_1] || ""}
|
95
|
+
"(VALUES #{values.join(",")}) AS #{ident_name name} (#{ident_name column_names})"
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def insert_values(item = @quotable)
|
60
100
|
case item
|
61
101
|
when Arel::Nodes::SqlLiteral
|
62
102
|
item = Arel.sql("(#{item})") unless item[/^\s*\(/] and item[/\)\s*$/]
|
63
103
|
return item
|
64
104
|
when Array
|
105
|
+
item.compact!
|
106
|
+
column_names = (@qsql.quotes[:columns] || @qsql.quotes[:column_names]).dup
|
107
|
+
types = []
|
108
|
+
if column_names.is_a? Hash
|
109
|
+
types = column_names.values.map { "::#{_1.upcase}" if _1 }
|
110
|
+
column_names = column_names.keys
|
111
|
+
elsif column_names.is_a? Array
|
112
|
+
column_names = column_names.map do |column|
|
113
|
+
types << column.respond_to?(:sql_type) ? "::#{column.sql_type}" : nil
|
114
|
+
column.respond_to?(:name) ? column.name : column
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if item.all? { _1.is_a?(Hash) }
|
119
|
+
column_names ||= item.flat_map { _1.keys.sort }.uniq
|
120
|
+
item.map! { _1.fetch_values(*column_names) {} }
|
121
|
+
end
|
65
122
|
|
66
|
-
|
67
|
-
|
68
|
-
|
123
|
+
if item.all? { _1.is_a?(Array) }
|
124
|
+
length, overflow = item.map { _1.length }.uniq
|
125
|
+
raise ArgumentError, "all values need to have the same length" if overflow
|
126
|
+
raise ArgumentError, "#{name}_columns and value lengths need to be the same" if column_names and column_names.length != length
|
127
|
+
values = item.map { value(_1) }
|
128
|
+
else
|
129
|
+
raise ArgumentError, "Either all type Hash or Array"
|
130
|
+
end
|
131
|
+
if column_names.present?
|
132
|
+
"(#{ident_name column_names}) VALUES #{values.join(",")}"
|
69
133
|
else
|
70
|
-
|
134
|
+
"VALUES #{values.join(",")}"
|
71
135
|
end
|
72
136
|
when Hash
|
73
137
|
value([item])
|
74
|
-
else
|
75
|
-
return item.to_sql if item.respond_to? :to_sql
|
76
|
-
"(" + _quote(item) + ")"
|
77
138
|
end
|
78
139
|
end
|
79
140
|
|
@@ -121,6 +182,20 @@ class QuoteSql
|
|
121
182
|
end
|
122
183
|
end
|
123
184
|
|
185
|
+
def columns(item = @quotable)
|
186
|
+
if item.respond_to?(:column_names)
|
187
|
+
item = item.column_names
|
188
|
+
elsif item.class.respond_to?(:column_names)
|
189
|
+
item = item.class.column_names
|
190
|
+
elsif item.is_a?(Array)
|
191
|
+
if item.all?{ _1.respond_to?(:name) }
|
192
|
+
item = item.map(&:name)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
@qsql.column_names ||= item
|
196
|
+
ident_name(item)
|
197
|
+
end
|
198
|
+
|
124
199
|
def column_names(item = @quotable)
|
125
200
|
if item.respond_to?(:column_names)
|
126
201
|
item = item.column_names
|
@@ -133,6 +208,13 @@ class QuoteSql
|
|
133
208
|
ident_name(item)
|
134
209
|
end
|
135
210
|
|
211
|
+
def json_build_object(h)
|
212
|
+
compact = h.delete(nil) == false
|
213
|
+
rv = "jsonb_build_object(" + h.map { "'#{_1}',#{_2}" }.join(",") + ")"
|
214
|
+
return rv unless compact
|
215
|
+
"jsonb_strip_nulls(#{rv})"
|
216
|
+
end
|
217
|
+
|
136
218
|
def ident_name(item = @quotable)
|
137
219
|
case item
|
138
220
|
when Array
|
@@ -147,14 +229,18 @@ class QuoteSql
|
|
147
229
|
end
|
148
230
|
end.join(",")
|
149
231
|
when Hash
|
150
|
-
item.map do
|
151
|
-
case
|
232
|
+
item.map do |k,v|
|
233
|
+
case v
|
152
234
|
when Symbol
|
153
|
-
_quote_column_name(
|
235
|
+
_quote_column_name(k, v)
|
154
236
|
when String
|
155
|
-
"#{
|
237
|
+
"#{v} AS #{k}"
|
156
238
|
when Proc
|
157
239
|
item.call(self)
|
240
|
+
when Hash
|
241
|
+
"#{json_build_object(v)} AS #{k}"
|
242
|
+
else
|
243
|
+
raise ArgumentError
|
158
244
|
end
|
159
245
|
end.join(",")
|
160
246
|
else
|
data/lib/quote_sql/test.rb
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
module QuoteSql::Test
|
2
2
|
def self.all
|
3
|
+
@success = []
|
4
|
+
@fail = []
|
3
5
|
methods(false).grep(/^test_/).each do |name|
|
4
|
-
run(name)
|
5
|
-
puts
|
6
|
+
run(name, true)
|
6
7
|
end
|
7
|
-
|
8
|
+
@success.each { STDOUT.puts(*_1, nil) }
|
9
|
+
@fail.each { STDOUT.puts(*_1, nil) }
|
10
|
+
puts
|
8
11
|
end
|
9
12
|
|
10
|
-
def self.run(name)
|
13
|
+
def self.run(name, all)
|
11
14
|
name = name.to_s.sub(/^test_/, "")
|
12
15
|
@expected = nil
|
13
16
|
@test = send("test_#{name}")
|
14
|
-
|
15
|
-
|
17
|
+
|
18
|
+
if sql.gsub(/\s+/, "")&.downcase&.strip == expected&.gsub(/\s+/, "")&.downcase&.strip
|
19
|
+
rv = [name, @test.original, @test.quotes.inspect, "✅ #{expected}"]
|
20
|
+
@success << rv if @success
|
16
21
|
else
|
17
|
-
|
22
|
+
rv = [name, @test.inspect, sql, "❌ #{expected}"]
|
23
|
+
@fail << rv if @fail
|
18
24
|
end
|
25
|
+
STDOUT.puts rv unless @fail or @success
|
19
26
|
end
|
20
27
|
|
21
28
|
def self.expected(v = nil)
|
@@ -42,7 +49,7 @@ module QuoteSql::Test
|
|
42
49
|
|
43
50
|
class << self
|
44
51
|
def test_columns_and_table_name_simple
|
45
|
-
expected
|
52
|
+
expected %(SELECT "a","b"."c" FROM "my_table")
|
46
53
|
QuoteSql.new("SELECT %columns FROM %table_name").quote(
|
47
54
|
columns: [:a, b: :c],
|
48
55
|
table_name: "my_table"
|
@@ -50,7 +57,7 @@ module QuoteSql::Test
|
|
50
57
|
end
|
51
58
|
|
52
59
|
def test_columns_and_table_name_complex
|
53
|
-
expected
|
60
|
+
expected %(SELECT "a","b"."c" FROM "table1","table2")
|
54
61
|
QuoteSql.new("SELECT %columns FROM %table_names").quote(
|
55
62
|
columns: [:a, b: :c],
|
56
63
|
table_names: ["table1", "table2"]
|
@@ -58,7 +65,7 @@ module QuoteSql::Test
|
|
58
65
|
end
|
59
66
|
|
60
67
|
def test_recursive_injects
|
61
|
-
expected
|
68
|
+
expected %(SELECT TRUE FROM "table1")
|
62
69
|
QuoteSql.new("SELECT %raw FROM %table_names").quote(
|
63
70
|
raw: "%recurse1_raw",
|
64
71
|
recurse1_raw: "%recurse2",
|
@@ -68,7 +75,9 @@ module QuoteSql::Test
|
|
68
75
|
end
|
69
76
|
|
70
77
|
def test_values
|
71
|
-
expected
|
78
|
+
expected <<~SQL
|
79
|
+
SELECT 'a text', 123, 'text' AS abc FROM "my_table"
|
80
|
+
SQL
|
72
81
|
QuoteSql.new("SELECT %text, %{number}, %aliased_with_hash FROM %table_name").quote(
|
73
82
|
text: "a text",
|
74
83
|
number: 123,
|
@@ -80,33 +89,92 @@ module QuoteSql::Test
|
|
80
89
|
end
|
81
90
|
|
82
91
|
def test_binds
|
83
|
-
expected
|
84
|
-
|
85
|
-
|
86
|
-
|
92
|
+
expected <<~SQL
|
93
|
+
SELECT $1, $2, $1 AS get_bind_1_again FROM "my_table"
|
94
|
+
SQL
|
95
|
+
QuoteSql.new("SELECT %bind, %bind__uuid, %bind1 AS get_bind_1_again FROM %table_name").quote(
|
96
|
+
table_name: "my_table"
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_from_values_array
|
101
|
+
expected <<~SQL
|
102
|
+
SELECT * FROM (VALUES ('a',1,TRUE,NULL)) AS "x" ("column1","column2","column3","column4")
|
103
|
+
SQL
|
104
|
+
"SELECT * FROM %x_values".quote_sql(x_values: [['a', 1, true, nil]])
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_from_values_hash_no_columns
|
108
|
+
expected <<~SQL
|
109
|
+
SELECT * FROM (VALUES ('a', 1, true, NULL), ('a', 1, true, NULL), (NULL, 1, NULL, 2)) AS "y" ("a", "b", "c", "d")
|
110
|
+
SQL
|
111
|
+
"SELECT * FROM %y_values".quote_sql(y_values: [
|
112
|
+
{ a: 'a', b: 1, c: true, d: nil },
|
113
|
+
{ d: nil, a: 'a', c: true, b: 1 },
|
114
|
+
{ d: 2, b: 1 }
|
115
|
+
])
|
87
116
|
end
|
88
117
|
|
89
|
-
def
|
90
|
-
expected
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
(2,NULL,'c','[1,2,3]','{"a":3}')
|
95
|
-
ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
|
96
|
-
SQL
|
97
|
-
|
98
|
-
QuoteSql.new(<<-SQL).
|
99
|
-
INSERT INTO %table (%columns) VALUES %values
|
100
|
-
ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
|
101
|
-
SQL
|
102
|
-
quote(
|
103
|
-
table: Response,
|
104
|
-
values: [
|
105
|
-
[nil, true, "A", [5, 5], { a: 1 }],
|
106
|
-
[1, false, "B", [], { a: 2 }],
|
107
|
-
[2, nil, "c", [1, 2, 3], { a: 3 }]
|
108
|
-
]
|
109
|
-
)
|
118
|
+
def test_from_values_hash_with_columns
|
119
|
+
expected <<~SQL
|
120
|
+
SELECT * FROM (VALUES (NULL, true, 1, 'a')) AS "x" ("d","c","b","a")
|
121
|
+
SQL
|
122
|
+
"SELECT * FROM %x_values".quote_sql(x_columns: %i[d c b a], x_values: [{ a: 'a', b: 1, c: true, d: nil }])
|
110
123
|
end
|
124
|
+
|
125
|
+
def test_from_values_hash_with_type_columns
|
126
|
+
expected <<~SQL
|
127
|
+
SELECT * FROM (VALUES ('a'::TEXT, 1::INTEGER, true::BOOLEAN, NULL::FLOAT), ('a', 1, true, NULL), (NULL, 1, NULL, 2)) AS "x" ("a", "b", "c", "d")
|
128
|
+
SQL
|
129
|
+
"SELECT * FROM %x_values".quote_sql(
|
130
|
+
x_columns: {
|
131
|
+
a: "text",
|
132
|
+
b: "integer",
|
133
|
+
c: "boolean",
|
134
|
+
d: "float"
|
135
|
+
},
|
136
|
+
x_values: [
|
137
|
+
{ a: 'a', b: 1, c: true, d: nil },
|
138
|
+
{ d: nil, a: 'a', c: true, b: 1 },
|
139
|
+
{ d: 2, b: 1 }
|
140
|
+
])
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_insert_values_array
|
144
|
+
expected <<~SQL
|
145
|
+
INSERT INTO x VALUES ('a', 1, true, NULL)
|
146
|
+
SQL
|
147
|
+
"INSERT INTO x %values".quote_sql(values: [['a', 1, true, nil]])
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_insert_values_hash
|
151
|
+
expected <<~SQL
|
152
|
+
INSERT INTO x ("a", "b", "c", "d") VALUES ('a', 1, true, NULL)
|
153
|
+
SQL
|
154
|
+
"INSERT INTO x %values".quote_sql(values: [{ a: 'a', b: 1, c: true, d: nil }])
|
155
|
+
end
|
156
|
+
|
157
|
+
# def test_q3
|
158
|
+
# expected Arel.sql(<<-SQL)
|
159
|
+
# INSERT INTO "responses" ("id","type","task_id","index","data","parts","value","created_at","updated_at")
|
160
|
+
# VALUES (NULL,TRUE,'A','[5,5]','{"a":1}'),
|
161
|
+
# (1,FALSE,'B','[]','{"a":2}'),
|
162
|
+
# (2,NULL,'c','[1,2,3]','{"a":3}')
|
163
|
+
# ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
|
164
|
+
# SQL
|
165
|
+
#
|
166
|
+
# QuoteSql.new(<<-SQL).
|
167
|
+
# INSERT INTO %table (%columns) VALUES %values
|
168
|
+
# ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
|
169
|
+
# SQL
|
170
|
+
# quote(
|
171
|
+
# table: Response,
|
172
|
+
# values: [
|
173
|
+
# [nil, true, "A", [5, 5], { a: 1 }],
|
174
|
+
# [1, false, "B", [], { a: 2 }],
|
175
|
+
# [2, nil, "c", [1, 2, 3], { a: 3 }]
|
176
|
+
# ]
|
177
|
+
# )
|
178
|
+
# end
|
111
179
|
end
|
112
180
|
end
|
data/lib/quote_sql.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Dir.glob(__FILE__.sub(/\.rb$/, "/*.rb")).each { require(_1) unless _1[/test\.rb$/] }
|
1
|
+
Dir.glob(__FILE__.sub(/\.rb$/, "/*.rb")).each { require(_1) unless _1[/(deprecated|test)\.rb$/] }
|
2
2
|
|
3
3
|
# Tool to build and run SQL queries easier
|
4
4
|
#
|
@@ -151,18 +151,29 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
|
|
151
151
|
end
|
152
152
|
|
153
153
|
class Error < ::RuntimeError
|
154
|
-
def initialize(quote_sql)
|
154
|
+
def initialize(quote_sql, errors)
|
155
155
|
@object = quote_sql
|
156
|
+
@errors = errors
|
156
157
|
end
|
157
158
|
|
159
|
+
attr_reader :object, :errors
|
160
|
+
|
161
|
+
def sql
|
162
|
+
@object.original.inspect
|
163
|
+
end
|
164
|
+
|
165
|
+
# def inspect
|
166
|
+
# super + errors.flat_map { [_1.inspect, _1.backtrace] }
|
167
|
+
# end
|
168
|
+
|
158
169
|
def message
|
159
|
-
super + %Q@<QuoteSql #{
|
170
|
+
super + %Q@<QuoteSql #{sql} #{@object.errors.inspect}>@
|
160
171
|
end
|
161
172
|
end
|
162
173
|
|
163
174
|
def to_sql
|
164
175
|
mixin!
|
165
|
-
raise Error.new(self) if errors?
|
176
|
+
raise Error.new(self, errors) if errors?
|
166
177
|
return Arel.sql @sql if defined? Arel
|
167
178
|
@sql
|
168
179
|
end
|
@@ -224,8 +235,8 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
|
|
224
235
|
def errors
|
225
236
|
@quotes.to_h do |k, v|
|
226
237
|
r = @resolved[k]
|
227
|
-
next [nil, nil]
|
228
|
-
[k,
|
238
|
+
next [nil, nil] if r.nil? or not r.is_a?(Exception)
|
239
|
+
[k, {@quotes[k].inspect => v.inspect, exc: r, backtrace: r.backtrace}]
|
229
240
|
end.compact
|
230
241
|
end
|
231
242
|
|
@@ -294,6 +305,15 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
|
|
294
305
|
|
295
306
|
extend Quoting
|
296
307
|
|
308
|
+
def self.test
|
309
|
+
require __dir__ + "/quote_sql/test.rb"
|
310
|
+
Test
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def QuoteSQL(sql, **options)
|
315
|
+
rv = QuoteSql.new(sql)
|
316
|
+
options.any? ? rv.quote(**options) : rv
|
297
317
|
end
|
298
318
|
|
299
319
|
QuoteSql.include QuoteSql::Formater
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quote-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Kufner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'QuoteSql helps you creating SQL queries and proper quoting especially
|
14
14
|
with advanced queries.
|
@@ -39,9 +39,9 @@ require_paths:
|
|
39
39
|
- lib
|
40
40
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
|
-
- - "
|
42
|
+
- - "~>"
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '0'
|
44
|
+
version: '3.0'
|
45
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - ">="
|