pod4 0.7.2 → 0.8.0
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/.hgignore +1 -0
- data/.hgtags +1 -0
- data/Gemfile +3 -1
- data/Rakefile +17 -5
- data/lib/pod4/pg_interface.rb +81 -57
- data/lib/pod4/sequel_interface.rb +61 -6
- data/lib/pod4/sql_helper.rb +229 -0
- data/lib/pod4/tds_interface.rb +65 -50
- data/lib/pod4/version.rb +1 -1
- data/md/roadmap.md +81 -25
- data/spec/common/basic_model_spec.rb +5 -0
- data/spec/common/model_spec.rb +49 -7
- data/spec/common/nebulous_interface_spec.rb +2 -0
- data/spec/common/sequel_interface_pg_spec.rb +511 -0
- data/spec/common/sql_helper_spec.rb +299 -0
- data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +525 -0
- data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +520 -0
- data/spec/mri/pg_interface_spec.rb +140 -16
- data/spec/mri/sequel_interface_spec.rb +146 -13
- data/spec/mri/tds_interface_spec.rb +82 -12
- metadata +11 -4
- data/spec/jruby/pg_interface_spec.rb +0 -469
@@ -0,0 +1,299 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'pod4/sql_helper'
|
5
|
+
|
6
|
+
class SQLHelperTester1
|
7
|
+
include SQLHelper
|
8
|
+
|
9
|
+
def schema; "marco"; end
|
10
|
+
def table; "polo"; end
|
11
|
+
def id_fld; "foo"; end
|
12
|
+
end
|
13
|
+
|
14
|
+
class SQLHelperTester2
|
15
|
+
include SQLHelper
|
16
|
+
|
17
|
+
def table; "polo"; end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe "SQLHelper" do
|
22
|
+
|
23
|
+
let(:tester1) {SQLHelperTester1.new}
|
24
|
+
let(:tester2) {SQLHelperTester2.new}
|
25
|
+
|
26
|
+
|
27
|
+
describe "quoted_table" do
|
28
|
+
|
29
|
+
it "will be quoted with double quotes" do
|
30
|
+
expect( tester2.quoted_table ).to eq %Q|"polo"|
|
31
|
+
end
|
32
|
+
|
33
|
+
it "will the schema plus the table if the schema is set" do
|
34
|
+
expect( tester1.quoted_table ).to eq %Q|"marco"."polo"|
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
describe "placeholder" do
|
41
|
+
|
42
|
+
it "will be the ruby sprintf string subst string" do
|
43
|
+
expect( tester1.send :placeholder ).to eq "%s"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe "quote_field" do
|
50
|
+
|
51
|
+
it "will wrap the field in double quotes" do
|
52
|
+
expect( tester1.send :quote_field, "thing" ).to eq %Q|"thing"|
|
53
|
+
end
|
54
|
+
|
55
|
+
it "will raise an error for a non-string" do
|
56
|
+
expect{ tester1.send :quote_field, 12 }.to raise_error ArgumentError
|
57
|
+
expect{ tester1.send :quote_field }.to raise_error ArgumentError
|
58
|
+
end
|
59
|
+
|
60
|
+
it "will wrap the field in some other character if you pass that" do
|
61
|
+
expect( tester1.send :quote_field, "thing", nil ).to eq %Q|thing|
|
62
|
+
expect( tester1.send :quote_field, "thing", "x" ).to eq %Q|xthingx|
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
describe "quote" do
|
69
|
+
let(:datetime) { "2055-12-31 11:23:36+04" }
|
70
|
+
let(:dtmatch) { /'2055.12.31[T ]11.23.36 ?\+04:?00'/ }
|
71
|
+
|
72
|
+
it "returns a String wrapped in single quotes" do
|
73
|
+
expect( tester1.send :quote, "foo" ).to eq %Q|'foo'|
|
74
|
+
end
|
75
|
+
|
76
|
+
it "turns a single quote into a doubled single quote" do
|
77
|
+
expect( tester1.send :quote, "G'Kar" ).to eq %Q|'G''Kar'|
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns date wrapped in a single quote" do
|
81
|
+
dt = Date.parse("2055-12-31")
|
82
|
+
expect( tester1.send :quote, dt ).to eq %Q|'2055-12-31'|
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns datetime wrapped in a single quote" do
|
86
|
+
dtm = DateTime.parse(datetime)
|
87
|
+
expect( tester1.send :quote, dtm ).to match dtmatch
|
88
|
+
end
|
89
|
+
|
90
|
+
it "returns time wrapped in a single quote" do
|
91
|
+
tm = Time.parse(datetime)
|
92
|
+
expect( tester1.send :quote, tm ).to match dtmatch
|
93
|
+
end
|
94
|
+
|
95
|
+
it "returns a BigDecimal as a float" do
|
96
|
+
bd = BigDecimal.new("14.98")
|
97
|
+
expect( tester1.send :quote, bd ).to eq 14.98
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns nil as 'NULL'" do
|
101
|
+
expect( tester1.send :quote, nil ).to eq %Q|NULL|
|
102
|
+
end
|
103
|
+
|
104
|
+
it "will wrap the value in some other character if you pass that" do
|
105
|
+
bd = BigDecimal.new("14.98")
|
106
|
+
expect( tester1.send :quote, "thing", nil ).to eq %Q|thing|
|
107
|
+
expect( tester1.send :quote, "thing", "x" ).to eq %Q|xthingx|
|
108
|
+
expect( tester1.send :quote, bd ).to eq 14.98
|
109
|
+
expect( tester1.send :quote, "G'Kar", "a" ).to eq %Q|aG'Kaara|
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
describe "sql_where(selection)" do
|
116
|
+
|
117
|
+
it "returns an empty string and empty array for an empty selection hash" do
|
118
|
+
expect( tester1.send :sql_where, {} ).to eq( [ "", [] ] )
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
it "returns valid SQL and unchanged array of values for the selection hash" do
|
123
|
+
sql, vals = tester1.send :sql_where, {lambs: "baah", pigs: "moo"}
|
124
|
+
|
125
|
+
expect( sql ).to match %r|where\s+"lambs"\s*=\s*%s\s+and\s+"pigs"\s*=\s*%s|i
|
126
|
+
expect( vals ).to eq( [%q|baah|, %q|moo|] )
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
describe "sql_select" do
|
133
|
+
|
134
|
+
it "returns sql plus an array of values" do
|
135
|
+
sql, vals = tester1.send( :sql_select, %W|foo bar|, {one: 12} )
|
136
|
+
smatch = %r|select\s+"foo",\s*"bar"\s+from\s+"marco"\."polo"\s+where\s+"one"\s*=\s*%s\s*;|i
|
137
|
+
|
138
|
+
expect(sql).to match(smatch)
|
139
|
+
expect(vals).to eq( [12] )
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
it "copes with nil for fields to select *" do
|
144
|
+
sql, vals = tester1.send( :sql_select, nil, {one: 12} )
|
145
|
+
smatch = %r|select\s+\*\s*from\s+"marco"\."polo"\s*where\s+"one"\s*=\s*%s\s*;|
|
146
|
+
|
147
|
+
expect(sql).to match(smatch)
|
148
|
+
expect(vals).to eq( [12] )
|
149
|
+
end
|
150
|
+
|
151
|
+
it "copes with one field" do
|
152
|
+
sql, vals = tester1.send( :sql_select, "foo", {one: 12} )
|
153
|
+
smatch = %r|select\s+"foo"\s+from\s+"marco"\."polo"\s+where\s+"one"\s*=\s*%s\s*;|i
|
154
|
+
|
155
|
+
expect(sql).to match(smatch)
|
156
|
+
expect(vals).to eq( [12] )
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
it "copes with a selection list" do
|
161
|
+
sql, vals = tester1.send( :sql_select, nil, {one: 12, two: 23} )
|
162
|
+
smatch = %r|select\s+\*\s*from\s+"marco"\."polo"
|
163
|
+
\s+where\s+"one"\s*=\s*%s\s+and\s+"two"\s*=\s*%s\s*;|ix
|
164
|
+
|
165
|
+
expect(sql).to match(smatch)
|
166
|
+
expect(vals).to eq( [12,23] )
|
167
|
+
end
|
168
|
+
|
169
|
+
it "copes with no selection list" do
|
170
|
+
sql, vals = tester1.send( :sql_select, nil, nil )
|
171
|
+
smatch = %r|select\s+\*\s*from\s+"marco"\."polo"\s*;|i
|
172
|
+
|
173
|
+
expect(sql).to match(smatch)
|
174
|
+
expect(vals).to eq( [] )
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
describe "sql_insert" do
|
181
|
+
|
182
|
+
it "raises an exception if there is no column:value hash" do
|
183
|
+
expect{ tester1.send :sql_insert, {} }.to raise_error ArgumentError
|
184
|
+
end
|
185
|
+
|
186
|
+
it "returns the correct sql plus an array of values" do
|
187
|
+
sel = {bada: "bing", bar: "foop"}
|
188
|
+
smatch = %r|insert\s+into\s+"marco"\."polo"
|
189
|
+
\s+\(\s*"bada",\s*"bar"\s*\)
|
190
|
+
\s+values\(\s*%s,\s*%s\s*\)
|
191
|
+
\s+returning\s+"foo"\s*;|xi
|
192
|
+
|
193
|
+
sql, vals = tester1.send :sql_insert, sel
|
194
|
+
|
195
|
+
expect(sql).to match smatch
|
196
|
+
expect(vals).to eq( ['bing', 'foop'] )
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
describe "sql_update" do
|
203
|
+
|
204
|
+
it "raises an exception if there is no column:value hash" do
|
205
|
+
expect{ tester1.send :sql_update, {}, {} }.to raise_error ArgumentError
|
206
|
+
end
|
207
|
+
|
208
|
+
it "returns the correct SQL and values without a selection hash" do
|
209
|
+
fv = {mouse: 14, rat: "meow"}
|
210
|
+
|
211
|
+
smatch = %r|update\s+"marco"\."polo"\s+set
|
212
|
+
\s+"mouse"\s*=\s*%s\s*,
|
213
|
+
\s+"rat"\s*=\s*%s\s*;|xi
|
214
|
+
|
215
|
+
sql, vals = tester1.send :sql_update, fv, {}
|
216
|
+
|
217
|
+
expect(sql).to match smatch
|
218
|
+
expect(vals).to eq( [14, 'meow'] )
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
it "returns the correct SQL and values if there is a selection hash" do
|
223
|
+
fv = {mouse: 14, rat: "meow"}
|
224
|
+
sel = {row: 5, column: true}
|
225
|
+
|
226
|
+
smatch = %r|update\s+"marco"\."polo"\s+set
|
227
|
+
\s+"mouse"\s*=\s*%s\s*,
|
228
|
+
\s+"rat"\s*=\s*%s
|
229
|
+
\s+where\s+"row"\s*=\s*%s\s+and\s+"column"\s*=\s*%s\s*;|xi
|
230
|
+
|
231
|
+
sql, vals = tester1.send :sql_update, fv, sel
|
232
|
+
|
233
|
+
expect(sql).to match smatch
|
234
|
+
expect(vals).to eq( [14, 'meow', 5, true] )
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
describe "sql_delete(selection)" do
|
241
|
+
|
242
|
+
it "returns the correct SQL and values without a selection hash" do
|
243
|
+
sql, vals = tester1.send :sql_delete, {}
|
244
|
+
|
245
|
+
expect(sql).to match %r|delete\s+from\s+"marco"\."polo"\s*;|i
|
246
|
+
expect(vals).to eq( [] )
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
it "returns the correct SQL and values if there is a selection hash" do
|
251
|
+
sql, vals = tester1.send :sql_delete, {alice: 14, ted: "moo"}
|
252
|
+
smatch = %r|delete\s+from\s+"marco"\."polo"
|
253
|
+
\s+where\s+"alice"\s*=\s*%s\s+and\s+"ted"\s*=\s*%s\s*;|ix
|
254
|
+
|
255
|
+
expect(sql).to match smatch
|
256
|
+
expect(vals).to eq( [14, 'moo'] )
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
describe "sql_subst" do
|
263
|
+
|
264
|
+
it "raises ArgumentError unless it receives an SQL string" do
|
265
|
+
expect{ tester1.send :sql_subst, 19, "foo" }.to raise_error ArgumentError
|
266
|
+
end
|
267
|
+
|
268
|
+
it "returns the sql untouched when there are no values" do
|
269
|
+
expect( tester1.send :sql_subst, "foo" ).to eq "foo"
|
270
|
+
end
|
271
|
+
|
272
|
+
it "raises ArgumentError if the sql is blank" do
|
273
|
+
expect{ tester1.send :sql_subst, "", 'foo' }.to raise_error ArgumentError
|
274
|
+
end
|
275
|
+
|
276
|
+
it "raises ArgumentError if the # of sql markers and the # of values don't match" do
|
277
|
+
# (unless we only pass one value, see below)
|
278
|
+
expect{ tester1.send :sql_subst, %q|foo %s %s bar %s|, 1, 2 }.to raise_error ArgumentError
|
279
|
+
expect{ tester1.send :sql_subst, %q|foo %s bar %s|, 1, 2, 3 }.to raise_error ArgumentError
|
280
|
+
end
|
281
|
+
|
282
|
+
it "returns complete SQL for sql with markers and a single value" do
|
283
|
+
sql = %q|select * from "foo" where "bar" = %s and "baz" = %s;|
|
284
|
+
seq = %q|select * from "foo" where "bar" = 'boing' and "baz" = 'boing';|
|
285
|
+
|
286
|
+
expect( tester1.send :sql_subst, sql, %q|'boing'| ).to eq seq
|
287
|
+
end
|
288
|
+
|
289
|
+
it "returns complete SQL for sql with markers and the right number of values in the array" do
|
290
|
+
sql = %q|select * from "foo" where "bar" = %s and "baz" = %s;|
|
291
|
+
seq = %q|select * from "foo" where "bar" = 'boing' and "baz" = 'bop';|
|
292
|
+
|
293
|
+
expect( tester1.send :sql_subst, sql, %q|'boing'|, %q|'bop'| ).to eq seq
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|