rdo-postgres 0.0.1

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.
@@ -0,0 +1,285 @@
1
+ require "spec_helper"
2
+ require "uri"
3
+
4
+ describe RDO::Postgres::Driver do
5
+ let(:options) { connection_uri }
6
+ let(:connection) { RDO.connect(options) }
7
+
8
+ after(:each) { connection.close rescue nil }
9
+
10
+ describe "#initialize" do
11
+ context "with valid settings" do
12
+ it "opens a connection to the server" do
13
+ connection.should be_open
14
+ end
15
+ end
16
+
17
+ context "with invalid settings" do
18
+ let(:options) { URI.parse(connection_uri).tap{|u| u.user = "bad_user"}.to_s }
19
+
20
+ it "raises a RDO::Exception" do
21
+ expect { connection }.to raise_error(RDO::Exception)
22
+ end
23
+
24
+ it "provides a meaningful error message" do
25
+ begin
26
+ connection && fail("RDO::Exception should be raised")
27
+ rescue RDO::Exception => e
28
+ e.message.should =~ /\bbad_user\b/
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#close" do
35
+ it "closes the connection to the database" do
36
+ connection.close
37
+ connection.should_not be_open
38
+ end
39
+
40
+ it "returns true" do
41
+ connection.close.should be_true
42
+ end
43
+
44
+ context "called multiple times" do
45
+ it "has no negative side-effects" do
46
+ 5.times { connection.close }
47
+ connection.should_not be_open
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#open" do
53
+ it "re-opens the connection to the database" do
54
+ connection.close && connection.open
55
+ connection.should be_open
56
+ end
57
+
58
+ it "returns true" do
59
+ connection.close
60
+ connection.open.should be_true
61
+ end
62
+
63
+ context "called multiple times" do
64
+ it "has no negative side-effects" do
65
+ connection.close
66
+ 5.times { connection.open }
67
+ connection.should be_open
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "#execute" do
73
+ after(:each) do
74
+ connection.execute("DROP SCHEMA IF EXISTS rdo_test CASCADE")
75
+ end
76
+
77
+ context "with DDL" do
78
+ let(:result) do
79
+ connection.execute("CREATE SCHEMA rdo_test")
80
+ end
81
+
82
+ it "returns a RDO::Result" do
83
+ result.should be_a_kind_of(RDO::Result)
84
+ end
85
+ end
86
+
87
+ context "with a bad query" do
88
+ let(:command) { connection.execute("SOME GIBBERISH") }
89
+
90
+ it "raises a RDO::Exception" do
91
+ expect { command }.to raise_error(RDO::Exception)
92
+ end
93
+
94
+ it "provides a meaningful error message" do
95
+ begin
96
+ command && fail("RDO::Exception should be raised")
97
+ rescue RDO::Exception => e
98
+ e.message.should =~ /\bSOME\b/
99
+ end
100
+ end
101
+ end
102
+
103
+ context "with an INSERT" do
104
+ before(:each) do
105
+ connection.execute("CREATE SCHEMA rdo_test")
106
+ connection.execute("SET search_path = rdo_test")
107
+ connection.execute(
108
+ "CREATE TABLE users (id serial primary key, name text)"
109
+ )
110
+ end
111
+
112
+ context "returning rows" do
113
+ let(:result) do
114
+ connection.execute(
115
+ "INSERT INTO users (name) VALUES ('bob') RETURNING *"
116
+ )
117
+ end
118
+
119
+ it "returns a RDO::Result" do
120
+ result.should be_a_kind_of(RDO::Result)
121
+ end
122
+
123
+ it "provides the return values" do
124
+ result.first[:id].should == 1
125
+ result.first[:name].should == "bob"
126
+ end
127
+
128
+ it "provides the #insert_id" do
129
+ result.insert_id.should == 1
130
+ end
131
+
132
+ it "provides the number of #affected_rows" do
133
+ result.affected_rows.should == 1
134
+ end
135
+ end
136
+
137
+ context "not returning" do
138
+ let(:result) do
139
+ connection.execute("INSERT INTO users (name) VALUES ('bob')")
140
+ end
141
+
142
+ it "returns a RDO::Result" do
143
+ result.should be_a_kind_of(RDO::Result)
144
+ end
145
+
146
+ it "has a nil #insert_id" do
147
+ result.insert_id.should be_nil
148
+ end
149
+
150
+ it "provides the number of #affected_rows" do
151
+ result.affected_rows.should == 1
152
+ end
153
+ end
154
+
155
+ context "using bind parameters" do
156
+ let(:result) do
157
+ connection.execute("INSERT INTO users (name) VALUES (?)", "bob")
158
+ end
159
+
160
+ it "returns a RDO::Result" do
161
+ result.should be_a_kind_of(RDO::Result)
162
+ end
163
+
164
+ it "provides the number of #affected_rows" do
165
+ result.affected_rows.should == 1
166
+ end
167
+ end
168
+ end
169
+
170
+ context "with a SELECT" do
171
+ before(:each) do
172
+ connection.execute("CREATE SCHEMA rdo_test")
173
+ connection.execute("SET search_path = rdo_test")
174
+ end
175
+
176
+ context "returning no rows" do
177
+ let(:result) { connection.execute("SELECT unnest('{}'::text[])") }
178
+
179
+ it "returns a RDO::Result" do
180
+ result.should be_a_kind_of(RDO::Result)
181
+ end
182
+
183
+ it "has no tuples" do
184
+ result.count.should == 0
185
+ end
186
+
187
+ it "can be converted to an empty array" do
188
+ result.to_a.should == []
189
+ end
190
+ end
191
+
192
+ context "returning rows" do
193
+ before(:each) do
194
+ connection.execute(
195
+ "CREATE TABLE users (id serial primary key, name text)"
196
+ )
197
+ connection.execute("INSERT INTO users (name) VALUES ('bob'), ('barry')")
198
+ end
199
+
200
+ let(:result) { connection.execute("SELECT * FROM users") }
201
+
202
+ it "returns a RDO::Result" do
203
+ result.should be_a_kind_of(RDO::Result)
204
+ end
205
+
206
+ it "provides the row count" do
207
+ result.count.should == 2
208
+ end
209
+
210
+ it "allows enumeration of the rows" do
211
+ rows = []
212
+ result.each{|row| rows << row }
213
+ rows.should == [{id: 1, name: "bob"}, {id: 2, name: "barry"}]
214
+ end
215
+
216
+ context "using bind parameters" do
217
+ let(:result) do
218
+ connection.execute("SELECT * FROM users WHERE name = ?", "barry")
219
+ end
220
+
221
+ it "returns a RDO::Result" do
222
+ result.should be_a_kind_of(RDO::Result)
223
+ end
224
+
225
+ it "provides the correct count" do
226
+ result.count.should == 1
227
+ end
228
+
229
+ it "allows enumeration of the rows" do
230
+ rows = []
231
+ result.each{|row| rows << row}
232
+ rows.should == [{id: 2, name: "barry"}]
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "string encoding" do
240
+ context "with utf-8" do
241
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=utf-8"}.to_s }
242
+
243
+ it "returns utf-8 strings" do
244
+ connection.execute("SELECT 'foo'::text").first_value.encoding.should ==
245
+ Encoding.find("utf-8")
246
+ end
247
+ end
248
+
249
+ context "with iso-8859-1" do
250
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=iso-8859-1"}.to_s }
251
+
252
+ it "returns iso-8859-1 strings" do
253
+ connection.execute("SELECT 'foo'::text").first_value.encoding.should ==
254
+ Encoding.find("iso-8859-1")
255
+ end
256
+ end
257
+
258
+ context "with latin1" do
259
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=latin1"}.to_s }
260
+
261
+ it "returns ascii-8bit strings (ruby doesn't know this charset)" do
262
+ connection.execute("SELECT 'foo'::text").first_value.encoding.should ==
263
+ Encoding.find("binary")
264
+ end
265
+ end
266
+ end
267
+
268
+ describe "#quote" do
269
+ it "quotes a string literal for insertion into the SQL" do
270
+ connection.quote("what's this?").should == "what''s this?"
271
+ end
272
+ end
273
+
274
+ describe "#prepare" do
275
+ it "returns a RDO::Statement" do
276
+ connection.prepare("SELECT ?::text").should be_a_kind_of(RDO::Statement)
277
+ end
278
+
279
+ it "can be executed multiple times" do
280
+ stmt = connection.prepare("SELECT ?::integer + ?")
281
+ stmt.execute(4, 7).first_value.should == 11
282
+ stmt.execute(5, 22).first_value.should == 27
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,272 @@
1
+ require "spec_helper"
2
+
3
+ describe RDO::Postgres::Driver, "type casting" do
4
+ let(:options) { connection_uri }
5
+ let(:connection) { RDO.connect(options) }
6
+ let(:value) { connection.execute(sql).first_value }
7
+
8
+ after(:each) { connection.close rescue nil }
9
+
10
+ describe "null cast" do
11
+ let(:sql) { "SELECT null" }
12
+
13
+ it "returns nil" do
14
+ value.should be_nil
15
+ end
16
+ end
17
+
18
+ describe "integer cast" do
19
+ let(:sql) { "SELECT 42::integer" }
20
+
21
+ it "returns a Fixnum" do
22
+ value.should == 42
23
+ end
24
+
25
+ context "using smallint" do
26
+ let(:sql) { "SELECT 42::smallint" }
27
+
28
+ it "returns a Fixnum" do
29
+ value.should == 42
30
+ end
31
+ end
32
+
33
+ context "using bigint" do
34
+ let(:sql) { "SELECT 42::bigint" }
35
+
36
+ it "returns a Fixnum" do
37
+ value.should == 42
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "text cast" do
43
+ let(:sql) { "SELECT 42::text" }
44
+
45
+ it "returns a String" do
46
+ value.should == "42"
47
+ end
48
+ end
49
+
50
+ describe "varchar cast" do
51
+ let(:sql) { "SELECT 'a very long string'::varchar(10)" }
52
+
53
+ it "returns a String" do
54
+ value.should == "a very lon"
55
+ end
56
+ end
57
+
58
+ describe "char cast" do
59
+ let(:sql) { "SELECT 'a very long string'::char(10)" }
60
+
61
+ it "returns a String" do
62
+ value.should == "a very lon"
63
+ end
64
+ end
65
+
66
+ describe "boolean cast" do
67
+ describe "true" do
68
+ let(:sql) { "SELECT true" }
69
+
70
+ it "returns true" do
71
+ value.should == true
72
+ end
73
+ end
74
+
75
+ describe "false" do
76
+ let(:sql) { "SELECT false" }
77
+
78
+ it "returns false" do
79
+ value.should == false
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "bytea cast" do
85
+ let(:sql) { "SELECT decode('00112233', 'hex')::bytea" }
86
+
87
+ context "using bytea_output = hex" do
88
+ before(:each) { connection.execute("SET bytea_output = hex") }
89
+
90
+ it "returns a String" do
91
+ value.should == "\x00\x11\x22\x33"
92
+ end
93
+ end
94
+
95
+ context "using bytea_output = escape" do
96
+ before(:each) do
97
+ # don't error on older versions of postgresql
98
+ connection.execute("SET bytea_output = escape") rescue nil
99
+ end
100
+
101
+ it "returns a String" do
102
+ value.should == "\x00\x11\x22\x33"
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "float cast" do
108
+ let(:sql) { "SELECT 1.2::float" }
109
+
110
+ it "returns a Float" do
111
+ value.should be_a_kind_of(Float)
112
+ value.should <= 1.201
113
+ value.should >= 1.199
114
+ end
115
+
116
+ context "using real" do
117
+ let(:sql) { "SELECT 1.2::real" }
118
+
119
+ it "returns a Float" do
120
+ value.should be_a_kind_of(Float)
121
+ value.should <= 1.201
122
+ value.should >= 1.199
123
+ end
124
+ end
125
+
126
+ context "using double precision" do
127
+ let(:sql) { "SELECT 1.2::double precision" }
128
+
129
+ it "returns a Float" do
130
+ value.should be_a_kind_of(Float)
131
+ value.should <= 1.201
132
+ value.should >= 1.199
133
+ end
134
+ end
135
+
136
+ context "for 'NaN'" do
137
+ let(:sql) { "SELECT 'NaN'::float" }
138
+
139
+ it "returns Float::NAN" do
140
+ value.should be_a_kind_of(Float)
141
+ value.should be_nan
142
+ end
143
+ end
144
+
145
+ context "for 'Infinity'" do
146
+ let(:sql) { "SELECT 'Infinity'::float" }
147
+
148
+ it "returns Float::INFINITY" do
149
+ value.should == Float::INFINITY
150
+ end
151
+ end
152
+
153
+ context "for '-Infinity'" do
154
+ let(:sql) { "SELECT '-Infinity'::float" }
155
+
156
+ it "returns -Float::INFINITY" do
157
+ value.should == -Float::INFINITY
158
+ end
159
+ end
160
+
161
+ context "for '1E-06'" do
162
+ let(:sql) { "SELECT '1E-06'::float" }
163
+
164
+ it "returns 1e-06" do
165
+ value.should be_a_kind_of(Float)
166
+ value.should >= Float("1E-06") - 0.01
167
+ value.should <= Float("1E-06") + 0.01
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "decimal cast" do
173
+ require "bigdecimal"
174
+
175
+ %w[decimal numeric].each do |type|
176
+ context "as #{type}" do
177
+ context "using a max precision and scale" do
178
+ let(:sql) { "SELECT '124.36'::#{type}(5,2)" }
179
+
180
+ it "returns a BigDecimal" do
181
+ value.should == BigDecimal("124.36")
182
+ end
183
+ end
184
+
185
+ context "using a max precision only" do
186
+ let(:sql) { "SELECT '124.36'::#{type}(5)" }
187
+
188
+ it "returns a BigDecimal" do
189
+ value.should == BigDecimal("124")
190
+ end
191
+ end
192
+
193
+ context "using no precision or scale" do
194
+ let(:sql) { "SELECT '124.36'::#{type}" }
195
+
196
+ it "returns a BigDecimal" do
197
+ value.should == BigDecimal("124.36")
198
+ end
199
+ end
200
+
201
+ context "for 'NaN'" do
202
+ let(:sql) { "SELECT 'NaN'::#{type}" }
203
+
204
+ it "returns a BigDecimal" do
205
+ value.should be_a_kind_of(BigDecimal)
206
+ value.should be_nan
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ describe "date cast" do
214
+ context "with YYYY-MM-DD format date" do
215
+ let(:sql) { "SELECT '2012-09-22'::date" }
216
+
217
+ it "returns a Date" do
218
+ value.should == Date.new(2012, 9, 22)
219
+ end
220
+ end
221
+
222
+ context "with YYY-MM-DD AD format date" do
223
+ let(:sql) { "SELECT '432-09-22 AD'::date" }
224
+
225
+ it "returns a Date" do
226
+ value.should == Date.new(432, 9, 22)
227
+ end
228
+ end
229
+
230
+ context "with YYY-MM-DD BC format date" do
231
+ let(:sql) { "SELECT '432-09-22 BC'::date" }
232
+
233
+ it "returns a Date" do
234
+ value.should == Date.new(-431, 9, 22)
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "timestamp cast" do
240
+ context "without a time zone" do
241
+ let(:sql) { "SELECT '2012-09-22 04:26:34'::timestamp" }
242
+
243
+ it "returns a DateTime in the local time zone" do
244
+ value.should == DateTime.new(2012, 9, 22, 4, 26, 34, DateTime.now.zone)
245
+ end
246
+ end
247
+
248
+ context "with a time zone" do
249
+ let(:sql) { "SELECT '2012-09-22 04:26:34'::timestamptz" }
250
+
251
+ it "returns a DateTime, in the server's time zone" do
252
+ value.should == DateTime.new(2012, 9, 22, 4, 26, 34, DateTime.now.zone)
253
+ end
254
+
255
+ context "specifying an alternate time zone" do
256
+ let(:sql) { "SELECT '2012-09-22 04:26:34'::timestamptz at time zone 'UTC'" }
257
+
258
+ it "returns a DateTime at the given time zone" do
259
+ value.should == DateTime.new(2012, 9, 21, 18, 26, 34, DateTime.now.zone)
260
+ end
261
+ end
262
+
263
+ context "with a different time zone set on the connection" do
264
+ before(:each) { connection.execute("SET timezone = 'UTC'") }
265
+
266
+ it "returns a DateTime with the conversion done accordingly" do
267
+ value.should == DateTime.new(2012, 9, 22, 14, 26, 34, DateTime.now.zone)
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end