quote-sql 0.0.4 โ†’ 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70f787b04560e3b297b4a392eb72c0e85be57e349a0533366056fe485f339401
4
- data.tar.gz: ad8a2e347c97e12b78a264e67d0e12649e838127a0079d8ff96bf5ac51764b40
3
+ metadata.gz: cb47e85b4c7c4e1f945748295390c9e1bd49b7e0ba5291e0f7310058f6f91ddc
4
+ data.tar.gz: 0f5bde1afc31eb573bb3e24727b530bf5909048284114d45c56c1b24a63e97aa
5
5
  SHA512:
6
- metadata.gz: 7d2a6819ad0d7fc752a70a04efb48c7583759a1b6cd07eb93c0bacd5deec9845b4abf578ae902c94aaf68ba4cf41720b1f05aea8dc6950b468c8cc8f42c34c42
7
- data.tar.gz: e84a311154ef05f71b8af81c17cac1b4923f067fb48556f501aa075ab8fca1c112d494c8b0b58f1c8350d7d7467c10ba99f74591755beb206415dbe78756c7ff
6
+ metadata.gz: 6596b9bb50ee373030e401dcb9d0067ae31857b6467fdd7b0341e21b3dcbe8fb0e78714ec24f56615618bbf965849307474a4030978e6c98d2d728166e235151
7
+ data.tar.gz: f38366de3e2227835fd11f512a9c9d2a99d130d896a17f6b0a58788dd8ed7ebd13f46682992b762e2d50f09716c3bd5654d525dc4b03f350d3a52cc6b19b322e
data/README.md CHANGED
@@ -9,13 +9,15 @@ I created this library while coding for different projects, and had lots of Here
9
9
 
10
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.
11
11
 
12
- QuoteSql is used in production, but is still bleeding edge - and there is not a fully sync between doc and code.
13
-
14
12
  If you think QuoteSql is interesting, let's chat!
15
13
  Also if you have problems using it, just drop me a note.
16
14
 
17
15
  Best Martin
18
16
 
17
+ ## Caveats & Notes
18
+ - QuoteSql is used in production, but is still bleeding edge - and there is not a fully sync between doc and code.
19
+ - Just for my examples and in the docs, I'm using for Yajl for JSON parsing, and changed in my environments the standard parse output to *symbolized keys*.
20
+
19
21
  ## Examples
20
22
  ### Simple quoting
21
23
  `QuoteSql.new("SELECT %field").quote(field: "abc").to_sql`
@@ -119,6 +121,12 @@ with optional array dimension
119
121
  - +String+ value will become the expression, the key the AS {result: "SUM(*)"} => SUM(*) AS result
120
122
  - +Proc+ are executed with the +QuoteSQL::Quoter+ object as parameter and added as raw SQL
121
123
 
124
+ ## Executing
125
+ ### Getting the results
126
+ ### Binds
127
+ `v = {a: 1, b: "foo", c: true};QuoteSQL(%q{Select * From %x_json}, x_json: 1, x_casts: {a: "int", b: "text", c: "boolean"}).result(v.to_json)`
128
+ => Select * From json_to_recordset($1) AS "x"("a" int,"b" text,"c" boolean) => [{a: 1, b: "foo", c: true}]
129
+
122
130
  ## Shortcuts and functions
123
131
  - `QuoteSQL("select %abc", abc: 1)` == `QuoteSql.new("select %abc").quote(abc: 1)`
124
132
  - when you have in your initializer `String.include QuoteSql::Extension` you can do e.g. `"select %abc".quote_sql(abc: 1)`
@@ -37,9 +37,13 @@ class QuoteSql
37
37
  def ident_columns(name = nil)
38
38
  item = columns(name || self.name)
39
39
  unless item
40
- table = self.table(name || self.name)
41
- raise ArgumntError, "No columns or table given" unless table&.respond_to? :column_names
42
- item = table.column_names
40
+ unless item = casts(name || self.name)&.keys
41
+ if (table = self.table(name || self.name))&.respond_to? :column_names
42
+ item = table.column_names
43
+ else
44
+ raise ArgumntError, "No columns, casts or table given for #{name}" unless table&.respond_to? :column_names
45
+ end
46
+ end
43
47
  end
44
48
  if item.is_a?(Array)
45
49
  if item.all? { _1.respond_to?(:name) }
@@ -143,11 +147,15 @@ class QuoteSql
143
147
  casts = self.casts(name)
144
148
  columns = self.columns(name) || casts&.keys
145
149
  column_cast = columns&.map { "#{QuoteSql.quote_column_name(_1)} #{casts&.dig(_1) || "TEXT"}" }
146
- item = [item].flatten.compact.as_json.map{_1.slice(*columns.map(&:to_s))}
147
- Raw.sql "json_to_recordset('#{item.to_json.gsub(/'/,"''")}') AS #{QuoteSql.quote_column_name name}(#{column_cast.join(',')})"
150
+ if item.is_a? Integer
151
+ rv = "$#{item}"
152
+ else
153
+ item = [item].flatten.compact.as_json.map { _1.slice(*columns.map(&:to_s)) }
154
+ rv = "'#{item.to_json.gsub(/'/, "''")}'"
155
+ end
156
+ Raw.sql "json_to_recordset(#{rv}) AS #{QuoteSql.quote_column_name name}(#{column_cast.join(',')})"
148
157
  end
149
158
 
150
-
151
159
  def data_values(item = @quotable)
152
160
  item = Array(item).compact
153
161
  column_names = columns(name)
@@ -1,59 +1,5 @@
1
1
  class QuoteSql::Test
2
-
3
- def all
4
- @success = []
5
- @fail = []
6
- private_methods(false).grep(/^test_/).each { run(_1, true) }
7
- @success.each { STDOUT.puts(*_1, nil) }
8
- @fail.each { STDOUT.puts(*_1, nil) }
9
- puts
10
- end
11
-
12
- def run(name, all = false)
13
- name = name.to_s.sub(/^test_/, "")
14
- rv = ["๐Ÿงช #{name}"]
15
- @expected = nil
16
- @test = send("test_#{name}")
17
- if sql.gsub(/\s+/, "")&.downcase&.strip == expected&.gsub(/\s+/, "")&.downcase&.strip
18
- tables = @test.tables.to_h { [[_1, "table"].compact.join("_"), _2] }
19
- columns = @test.instance_variable_get(:@columns).to_h { [[_1, "columns"].compact.join("_"), _2] }
20
- rv += [@test.original, { **tables, **columns, **@test.quotes }.inspect, "๐ŸŽฏ #{expected}", "โœ… #{sql}"]
21
- @success << rv if @success
22
- else
23
- rv += [@test.inspect, "๐ŸŽฏ #{expected}", "โŒ #{sql}"]
24
- @fail << rv if @fail
25
- end
26
- rescue => exc
27
- rv += [@test.inspect, "๐ŸŽฏ #{expected}", "โŒ #{sql}", exc.message]
28
- @fail << rv if @fail
29
- ensure
30
- STDOUT.puts(*rv) unless @fail or @success
31
- end
32
-
33
- def expected(v = nil)
34
- @expected ||= v
35
- end
36
-
37
- def sql
38
- @test.to_sql
39
- end
40
-
41
- class PseudoActiveRecord
42
- def self.table_name
43
- "pseudo_active_records"
44
- end
45
-
46
- def self.column_names
47
- %w(id column1 column2)
48
- end
49
-
50
- def to_qsl
51
- "SELECT * FROM #{self.class.table_name}"
52
- end
53
- end
54
-
55
2
  private
56
-
57
3
  def test_columns
58
4
  expected <<~SQL
59
5
  SELECT x, "a", "b", "c", "d"
@@ -189,6 +135,20 @@ class QuoteSql::Test
189
135
  "INSERT INTO users (name, color) SELECT * from %x_json".quote_sql(x_casts: {name: "text", color: "text"}, x_json:)
190
136
  end
191
137
 
138
+ def test_from_json_bind
139
+ expected <<~SQL
140
+ Select * From json_to_recordset($1) AS "x"("a" int,"b" text,"c" boolean)
141
+ SQL
142
+ QuoteSQL("Select * From %x_json", x_json: 1, x_casts: {a: "int", b: "text", c: "boolean"})
143
+ end
144
+
145
+ def test_insert_json_bind
146
+ expected <<~SQL
147
+ INSERT INTO table ("a","b","c") Select * From json_to_recordset($1) AS "x"("a" int,"b" text,"c" boolean)
148
+ SQL
149
+ QuoteSQL("INSERT INTO table (%x_columns) Select * From %x_json", x_json: 1, x_casts: {a: "int", b: "text", c: "boolean"})
150
+ end
151
+
192
152
  # def test_q3
193
153
  # expected Arel.sql(<<-SQL)
194
154
  # INSERT INTO "responses" ("id","type","task_id","index","data","parts","value","created_at","updated_at")
@@ -212,4 +172,60 @@ class QuoteSql::Test
212
172
  # )
213
173
  # end
214
174
 
175
+
176
+ public
177
+
178
+ def all
179
+ @success = []
180
+ @fail = []
181
+ private_methods(false).grep(/^test_/).each { run(_1, true) }
182
+ @success.each { STDOUT.puts(*_1, nil) }
183
+ @fail.each { STDOUT.puts(*_1, nil) }
184
+ puts
185
+ end
186
+
187
+ def run(name, all = false)
188
+ name = name.to_s.sub(/^test_/, "")
189
+ rv = ["๐Ÿงช #{name}"]
190
+ @expected = nil
191
+ @test = send("test_#{name}")
192
+ if sql.gsub(/\s+/, "")&.downcase&.strip == expected&.gsub(/\s+/, "")&.downcase&.strip
193
+ tables = @test.tables.to_h { [[_1, "table"].compact.join("_"), _2] }
194
+ columns = @test.instance_variable_get(:@columns).to_h { [[_1, "columns"].compact.join("_"), _2] }
195
+ rv += [
196
+ "QuoteSql.new(\"#{@test.original}\").quote(#{{**tables, **columns, **@test.quotes }.inspect}).to_sql", "๐ŸŽฏ #{expected}", "โœ… #{sql}"]
197
+ @success << rv if @success
198
+ else
199
+ rv += [@test.inspect, "๐ŸŽฏ #{expected}", "โŒ #{sql}"]
200
+ @fail << rv if @fail
201
+ end
202
+ rescue => exc
203
+ rv += [@test.inspect, "๐ŸŽฏ #{expected}", "โŒ #{sql}", exc.message]
204
+ @fail << rv if @fail
205
+ ensure
206
+ STDOUT.puts(*rv) unless @fail or @success
207
+ end
208
+
209
+ def expected(v = nil)
210
+ @expected ||= v
211
+ end
212
+
213
+ def sql
214
+ @test.to_sql
215
+ end
216
+
217
+ class PseudoActiveRecord
218
+ def self.table_name
219
+ "pseudo_active_records"
220
+ end
221
+
222
+ def self.column_names
223
+ %w(id column1 column2)
224
+ end
225
+
226
+ def to_qsl
227
+ "SELECT * FROM #{self.class.table_name}"
228
+ end
229
+ end
230
+
215
231
  end
@@ -1,3 +1,3 @@
1
1
  class QuoteSql
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/quote_sql.rb CHANGED
@@ -82,14 +82,14 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
82
82
  @sql
83
83
  end
84
84
 
85
- def result(binds = [], prepare: false, async: false)
85
+ def result(*binds, prepare: false, async: false)
86
86
  sql = to_sql
87
- if binds.present? and sql.scan(/(?<=\$)\d+/).map(&:to_i).max + 1 != binds.length
87
+ if binds.present? and sql.scan(/(?<=\$)\d+/).map(&:to_i).max != binds.length
88
88
  raise ArgumentError, "Wrong number of binds"
89
89
  end
90
90
  _exec(sql, binds, prepare: false, async: false)
91
91
  rescue => exc
92
- STDERR.puts exc.sql
92
+ STDERR.puts exc.inspect, self.inspect
93
93
  raise exc
94
94
  end
95
95
 
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
4
+ version: 0.0.5
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-26 00:00:00.000000000 Z
11
+ date: 2024-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: niceql