do_postgres 0.2.4 → 0.9.2
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.
- data/README +3 -3
- data/Rakefile +46 -25
- data/TODO +4 -0
- data/ext/do_postgres_ext.c +614 -0
- data/ext/extconf.rb +18 -6
- data/ext/type-oids.h +76 -0
- data/lib/do_postgres.rb +4 -245
- data/lib/do_postgres/transaction.rb +37 -0
- data/spec/integration/do_postgres_spec.rb +202 -0
- data/spec/integration/logging_spec.rb +47 -0
- data/spec/integration/quoting_spec.rb +19 -0
- data/spec/integration/timezone_spec.rb +58 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/unit/transaction_spec.rb +28 -0
- metadata +62 -48
- data/ext/postgres_c.c +0 -8185
- data/ext/postgres_c.i +0 -73
data/ext/extconf.rb
CHANGED
@@ -1,7 +1,18 @@
|
|
1
|
-
|
1
|
+
if RUBY_PLATFORM =~ /darwin/
|
2
|
+
ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
|
3
|
+
|
4
|
+
# On PowerPC the defaults are fine
|
5
|
+
ENV["RC_ARCHS"] = '' if `uname -m` =~ /^Power Macintosh/
|
6
|
+
end
|
2
7
|
|
3
8
|
require 'mkmf'
|
4
9
|
|
10
|
+
# be polite: you can't force existance of uname functionality on all
|
11
|
+
# platforms.
|
12
|
+
if RUBY_PLATFORM =~ /darwin/
|
13
|
+
ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
|
14
|
+
end
|
15
|
+
|
5
16
|
def config_value(type)
|
6
17
|
ENV["POSTGRES_#{type.upcase}"] || pg_config(type)
|
7
18
|
end
|
@@ -11,7 +22,8 @@ def pg_config(type)
|
|
11
22
|
end
|
12
23
|
|
13
24
|
def have_build_env
|
14
|
-
have_library('pq')
|
25
|
+
(have_library('pq') || have_library('libpq')) &&
|
26
|
+
have_header('libpq-fe.h') && have_header('libpq/libpq-fs.h')
|
15
27
|
end
|
16
28
|
|
17
29
|
dir_config('pgsql', config_value('include'), config_value('lib'))
|
@@ -23,9 +35,9 @@ compat_functions = %w(PQescapeString PQexecParams)
|
|
23
35
|
if have_build_env
|
24
36
|
required_libraries.each(&method(:have_library))
|
25
37
|
desired_functions.each(&method(:have_func))
|
26
|
-
$CFLAGS << ' -Wall '
|
27
|
-
|
28
|
-
create_makefile("postgres_c")
|
38
|
+
$CFLAGS << ' -Wall ' unless RUBY_PLATFORM =~ /mswin/
|
39
|
+
create_makefile("do_postgres_ext")
|
29
40
|
else
|
30
41
|
puts 'Could not find PostgreSQL build environment (libraries & headers): Makefile not created'
|
31
|
-
|
42
|
+
exit(1)
|
43
|
+
end
|
data/ext/type-oids.h
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#define BOOLOID 16
|
2
|
+
#define BYTEAOID 17
|
3
|
+
#define CHAROID 18
|
4
|
+
#define NAMEOID 19
|
5
|
+
#define INT8OID 20
|
6
|
+
#define INT2OID 21
|
7
|
+
#define INT2VECTOROID 22
|
8
|
+
#define INT4OID 23
|
9
|
+
#define REGPROCOID 24
|
10
|
+
#define TEXTOID 25
|
11
|
+
#define OIDOID 26
|
12
|
+
#define TIDOID 27
|
13
|
+
#define XIDOID 28
|
14
|
+
#define CIDOID 29
|
15
|
+
#define OIDVECTOROID 30
|
16
|
+
#define PG_TYPE_RELTYPE_OID 71
|
17
|
+
#define PG_ATTRIBUTE_RELTYPE_OID 75
|
18
|
+
#define PG_PROC_RELTYPE_OID 81
|
19
|
+
#define PG_CLASS_RELTYPE_OID 83
|
20
|
+
#define XMLOID 142
|
21
|
+
#define POINTOID 600
|
22
|
+
#define LSEGOID 601
|
23
|
+
#define PATHOID 602
|
24
|
+
#define BOXOID 603
|
25
|
+
#define POLYGONOID 604
|
26
|
+
#define LINEOID 628
|
27
|
+
#define FLOAT4OID 700
|
28
|
+
#define FLOAT8OID 701
|
29
|
+
#define ABSTIMEOID 702
|
30
|
+
#define RELTIMEOID 703
|
31
|
+
#define TINTERVALOID 704
|
32
|
+
#define UNKNOWNOID 705
|
33
|
+
#define CIRCLEOID 718
|
34
|
+
#define CASHOID 790
|
35
|
+
#define MACADDROID 829
|
36
|
+
#define INETOID 869
|
37
|
+
#define CIDROID 650
|
38
|
+
#define INT4ARRAYOID 1007
|
39
|
+
#define FLOAT4ARRAYOID 1021
|
40
|
+
#define ACLITEMOID 1033
|
41
|
+
#define CSTRINGARRAYOID 1263
|
42
|
+
#define BPCHAROID 1042
|
43
|
+
#define VARCHAROID 1043
|
44
|
+
#define DATEOID 1082
|
45
|
+
#define TIMEOID 1083
|
46
|
+
#define TIMESTAMPOID 1114
|
47
|
+
#define TIMESTAMPTZOID 1184
|
48
|
+
#define INTERVALOID 1186
|
49
|
+
#define TIMETZOID 1266
|
50
|
+
#define BITOID 1560
|
51
|
+
#define VARBITOID 1562
|
52
|
+
#define NUMERICOID 1700
|
53
|
+
#define REFCURSOROID 1790
|
54
|
+
#define REGPROCEDUREOID 2202
|
55
|
+
#define REGOPEROID 2203
|
56
|
+
#define REGOPERATOROID 2204
|
57
|
+
#define REGCLASSOID 2205
|
58
|
+
#define REGTYPEOID 2206
|
59
|
+
#define REGTYPEARRAYOID 2211
|
60
|
+
#define TSVECTOROID 3614
|
61
|
+
#define GTSVECTOROID 3642
|
62
|
+
#define TSQUERYOID 3615
|
63
|
+
#define REGCONFIGOID 3734
|
64
|
+
#define REGDICTIONARYOID 3769
|
65
|
+
#define RECORDOID 2249
|
66
|
+
#define CSTRINGOID 2275
|
67
|
+
#define ANYOID 2276
|
68
|
+
#define ANYARRAYOID 2277
|
69
|
+
#define VOIDOID 2278
|
70
|
+
#define TRIGGEROID 2279
|
71
|
+
#define LANGUAGE_HANDLEROID 2280
|
72
|
+
#define INTERNALOID 2281
|
73
|
+
#define OPAQUEOID 2282
|
74
|
+
#define ANYELEMENTOID 2283
|
75
|
+
#define ANYNONARRAYOID 2776
|
76
|
+
#define ANYENUMOID 3500
|
data/lib/do_postgres.rb
CHANGED
@@ -1,246 +1,5 @@
|
|
1
|
-
require 'postgres_c'
|
2
|
-
require 'data_objects'
|
3
|
-
|
4
|
-
module DataObject
|
5
|
-
module Postgres
|
6
|
-
TYPES = Hash[*Postgres_c.constants.select {|x| x.include?("OID")}.map {|x| [Postgres_c.const_get(x), x.gsub(/_?OID$/, "")]}.flatten]
|
7
|
-
QUOTE_STRING = "'"
|
8
|
-
QUOTE_COLUMN = "\""
|
9
|
-
|
10
|
-
class Connection < DataObject::Connection
|
11
|
-
attr_reader :db
|
12
|
-
|
13
|
-
def initialize(connection_string)
|
14
|
-
@state = STATE_CLOSED
|
15
|
-
@connection_string = connection_string
|
16
|
-
end
|
17
|
-
|
18
|
-
def open
|
19
|
-
@db = Postgres_c.PQconnectdb(@connection_string)
|
20
|
-
if Postgres_c.PQstatus(@db) != Postgres_c::CONNECTION_OK
|
21
|
-
raise ConnectionFailed, "Unable to connect to database with provided connection string. \n#{Postgres_c.PQerrorMessage(@db)}"
|
22
|
-
end
|
23
|
-
@state = STATE_OPEN
|
24
|
-
true
|
25
|
-
end
|
26
|
-
|
27
|
-
def close
|
28
|
-
Postgres_c.PQfinish(@db)
|
29
|
-
@state = STATE_CLOSED
|
30
|
-
true
|
31
|
-
end
|
32
|
-
|
33
|
-
def create_command(text)
|
34
|
-
Command.new(self, text)
|
35
|
-
end
|
36
|
-
|
37
|
-
def begin_transaction
|
38
|
-
Transaction.new(self)
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
class Transaction < Connection
|
44
|
-
|
45
|
-
attr_reader :connection
|
46
|
-
|
47
|
-
def initialize(conn)
|
48
|
-
@connection = conn
|
49
|
-
exec_sql("BEGIN")
|
50
|
-
end
|
51
|
-
|
52
|
-
# Commits the transaction
|
53
|
-
def commit
|
54
|
-
exec_sql("COMMIT")
|
55
|
-
end
|
56
1
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# Creates a savepoint for rolling back later (not commonly supported)
|
63
|
-
def save(name)
|
64
|
-
exec_sql("SAVEPOINT #{name}")
|
65
|
-
end
|
66
|
-
|
67
|
-
def create_command(*args)
|
68
|
-
@connection.create_command(*args)
|
69
|
-
end
|
70
|
-
|
71
|
-
protected
|
72
|
-
|
73
|
-
def exec_sql(sql)
|
74
|
-
@connection.logger.debug(sql)
|
75
|
-
Postgres_c.PQexec(@connection.db, "COMMIT")
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
class Reader < DataObject::Reader
|
81
|
-
|
82
|
-
def initialize(db, reader)
|
83
|
-
@reader = reader
|
84
|
-
case Postgres_c.PQresultStatus(reader)
|
85
|
-
when Postgres_c::PGRES_COMMAND_OK
|
86
|
-
@records_affected = Postgres_c.PQcmdTuples(reader).to_i
|
87
|
-
close
|
88
|
-
when Postgres_c::PGRES_TUPLES_OK
|
89
|
-
@fields, @field_types = [], []
|
90
|
-
@field_count = Postgres_c.PQnfields(@reader)
|
91
|
-
i = 0
|
92
|
-
while(i < @field_count)
|
93
|
-
@field_types.push(Postgres_c.PQftype(@reader, i))
|
94
|
-
@fields.push(Postgres_c.PQfname(@reader, i))
|
95
|
-
i += 1
|
96
|
-
end
|
97
|
-
@rows = Postgres_c.PQntuples(@reader)
|
98
|
-
@has_rows = @rows > 0
|
99
|
-
@cursor = 0
|
100
|
-
@state = STATE_OPEN
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def real_close
|
105
|
-
Postgres_c.PQclear(@reader)
|
106
|
-
end
|
107
|
-
|
108
|
-
def data_type_name(col)
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
def name(col)
|
113
|
-
super
|
114
|
-
Postgres_c.PQfname(@reader, col)
|
115
|
-
end
|
116
|
-
|
117
|
-
def get_index(name)
|
118
|
-
super
|
119
|
-
@fields.index(name)
|
120
|
-
end
|
121
|
-
|
122
|
-
def null?(idx)
|
123
|
-
super
|
124
|
-
Postgres_c.PQgetisnull(@reader, @cursor, idx) != 0
|
125
|
-
end
|
126
|
-
|
127
|
-
def item(idx)
|
128
|
-
super
|
129
|
-
val = Postgres_c.PQgetvalue(@reader, @cursor, idx)
|
130
|
-
typecast(val, @field_types[idx])
|
131
|
-
end
|
132
|
-
|
133
|
-
def each
|
134
|
-
return unless has_rows?
|
135
|
-
|
136
|
-
while(true) do
|
137
|
-
yield
|
138
|
-
break unless self.next
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def next
|
143
|
-
super
|
144
|
-
if @cursor >= @rows - 1
|
145
|
-
@cursor = nil
|
146
|
-
close
|
147
|
-
return nil
|
148
|
-
end
|
149
|
-
@cursor += 1
|
150
|
-
true
|
151
|
-
end
|
152
|
-
|
153
|
-
protected
|
154
|
-
def native_type(col)
|
155
|
-
TYPES[Postgres_c.PQftype(@reader, col)]
|
156
|
-
end
|
157
|
-
|
158
|
-
def typecast(val, field_type)
|
159
|
-
return nil if val.nil? || val == "NULL"
|
160
|
-
case TYPES[field_type]
|
161
|
-
when "BOOL"
|
162
|
-
val == "t"
|
163
|
-
when "INT2", "INT4", "OID", "TID", "XID", "CID", "INT8"
|
164
|
-
val == '' ? nil : val.to_i
|
165
|
-
when "FLOAT4", "FLOAT8", "NUMERIC", "CASH"
|
166
|
-
val.to_f
|
167
|
-
when "TIMESTAMP", "TIMETZ", "TIMESTAMPTZ"
|
168
|
-
DateTime.parse(val) rescue nil
|
169
|
-
when "TIME"
|
170
|
-
DateTime.parse(val).to_time rescue nil
|
171
|
-
when "DATE"
|
172
|
-
Date.parse(val) rescue nil
|
173
|
-
else
|
174
|
-
val
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
end
|
179
|
-
|
180
|
-
class ResultData < DataObject::ResultData
|
181
|
-
|
182
|
-
def last_insert_row
|
183
|
-
@last_insert_row ||= begin
|
184
|
-
reader = @conn.create_command("select lastval()").execute_reader
|
185
|
-
reader.item(0).to_i
|
186
|
-
rescue QueryError
|
187
|
-
raise NoInsertError, "You tried to get the last inserted row without doing an insert\n#{Postgres_c.PQerrorMessage(@conn.db)}"
|
188
|
-
ensure
|
189
|
-
reader and reader.close
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
end
|
194
|
-
|
195
|
-
class Command < DataObject::Command
|
196
|
-
|
197
|
-
def execute_reader(*args)
|
198
|
-
super
|
199
|
-
sql = escape_sql(args)
|
200
|
-
@connection.logger.debug { sql }
|
201
|
-
ptr = Postgres_c.PQexec(@connection.db, sql)
|
202
|
-
unless [Postgres_c::PGRES_COMMAND_OK, Postgres_c::PGRES_TUPLES_OK].include?(Postgres_c.PQresultStatus(ptr))
|
203
|
-
raise QueryError, "Your query failed.\n#{Postgres_c.PQerrorMessage(@connection.db)}QUERY: \"#{sql}\""
|
204
|
-
else
|
205
|
-
reader = Reader.new(@connection.db, ptr)
|
206
|
-
if block_given?
|
207
|
-
return_value = yield(reader)
|
208
|
-
reader.close
|
209
|
-
return_value
|
210
|
-
else
|
211
|
-
reader
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def execute_non_query(*args)
|
217
|
-
super
|
218
|
-
sql = escape_sql(args)
|
219
|
-
@connection.logger.debug { sql }
|
220
|
-
results = Postgres_c.PQexec(@connection.db, sql)
|
221
|
-
status = Postgres_c.PQresultStatus(results)
|
222
|
-
if status == Postgres_c::PGRES_TUPLES_OK
|
223
|
-
Postgres_c.PQclear(results)
|
224
|
-
raise QueryError, "Your query failed or you tried to execute a SELECT query through execute_non_reader\n#{Postgres_c.PQerrorMessage(@connection.db)}\nQUERY: \"#{sql}\""
|
225
|
-
elsif status != Postgres_c::PGRES_COMMAND_OK
|
226
|
-
Postgres_c.PQclear(results)
|
227
|
-
raise QueryError, "Your query failed.\n#{Postgres_c.PQerrorMessage(@connection.db)}\nQUERY: \"#{sql}\""
|
228
|
-
end
|
229
|
-
rows_affected = Postgres_c.PQcmdTuples(results).to_i
|
230
|
-
Postgres_c.PQclear(results)
|
231
|
-
ResultData.new(@connection, rows_affected)
|
232
|
-
end
|
233
|
-
|
234
|
-
def quote_string(value)
|
235
|
-
if value =~ /[\x00-\x80]/
|
236
|
-
raise "String cannot contain $Text$ in the body" if value.include?("$Text$")
|
237
|
-
"$Text$#{value}$Text$"
|
238
|
-
else
|
239
|
-
super
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
end
|
2
|
+
require 'rubygems'
|
3
|
+
require 'data_objects'
|
4
|
+
require 'do_postgres_ext'
|
5
|
+
require 'do_postgres/transaction'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module DataObjects
|
3
|
+
|
4
|
+
module Postgres
|
5
|
+
|
6
|
+
class Transaction < DataObjects::Transaction
|
7
|
+
|
8
|
+
def begin
|
9
|
+
cmd = "BEGIN"
|
10
|
+
connection.create_command(cmd).execute_non_query
|
11
|
+
end
|
12
|
+
|
13
|
+
def commit
|
14
|
+
cmd = "COMMIT PREPARED '#{id}'"
|
15
|
+
connection.create_command(cmd).execute_non_query
|
16
|
+
end
|
17
|
+
|
18
|
+
def rollback
|
19
|
+
cmd = "ROLLBACK"
|
20
|
+
connection.create_command(cmd).execute_non_query
|
21
|
+
end
|
22
|
+
|
23
|
+
def rollback_prepared
|
24
|
+
cmd = "ROLLBACK PREPARED '#{id}'"
|
25
|
+
connection.create_command(cmd).execute_non_query
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare
|
29
|
+
cmd = "PREPARE TRANSACTION '#{id}'"
|
30
|
+
connection.create_command(cmd).execute_non_query
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
3
|
+
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# Create a postgres db named do_test that accepts connections
|
7
|
+
# from localhost from your current user (without password) to enable this spec.
|
8
|
+
#
|
9
|
+
# You also need to allow passwordless access from localhost-
|
10
|
+
# locate the following line in your pg_hba.conf file:
|
11
|
+
#
|
12
|
+
# # IPv4 local connections:
|
13
|
+
# host all all 127.0.0.1/32 md5
|
14
|
+
#
|
15
|
+
# and replace 'md5' with 'trust' for these specs to work
|
16
|
+
#
|
17
|
+
#
|
18
|
+
|
19
|
+
describe "DataObjects::Postgres::Connection" do
|
20
|
+
include PostgresSpecHelpers
|
21
|
+
|
22
|
+
it "should connect to the db" do
|
23
|
+
connection = DataObjects::Connection.new("postgres://localhost/do_test")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "DataObjects::Postgres::Command" do
|
28
|
+
include PostgresSpecHelpers
|
29
|
+
|
30
|
+
before :all do
|
31
|
+
@connection = ensure_users_table_and_return_connection
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create a command" do
|
35
|
+
@connection.create_command("CREATE TABLE users").should be_a_kind_of(DataObjects::Postgres::Command)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should set types" do
|
39
|
+
command = @connection.create_command("SELECT id, name FROM users")
|
40
|
+
command.set_types [Integer, String]
|
41
|
+
command.instance_variable_get("@field_types").should == [Integer, String]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should execute a non query" do
|
45
|
+
command = @connection.create_command("INSERT INTO users (name) VALUES ('Test')")
|
46
|
+
result = command.execute_non_query
|
47
|
+
result.should be_a_kind_of(DataObjects::Postgres::Result)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should execute a reader" do
|
51
|
+
command = @connection.create_command("SELECT * FROM users")
|
52
|
+
reader = command.execute_reader
|
53
|
+
reader.should be_a_kind_of(DataObjects::Postgres::Reader)
|
54
|
+
reader.close.should == true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "DataObjects::Postgres::Result" do
|
59
|
+
include PostgresSpecHelpers
|
60
|
+
|
61
|
+
before :all do
|
62
|
+
@connection = ensure_users_table_and_return_connection
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise errors on bad queries" do
|
66
|
+
command = @connection.create_command("INSER INTO users (name) VALUES ('Test')")
|
67
|
+
lambda { command.execute_non_query }.should raise_error
|
68
|
+
command = @connection.create_command("INSERT INTO users (non_existant_field) VALUES ('Test')")
|
69
|
+
lambda { command.execute_non_query }.should raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not have an insert_id without RETURNING" do
|
73
|
+
command = @connection.create_command("INSERT INTO users (name) VALUES ('Test')")
|
74
|
+
result = command.execute_non_query
|
75
|
+
result.insert_id.should == 0;
|
76
|
+
result.to_i.should == 1;
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should have an insert_id when RETURNING" do
|
80
|
+
command = @connection.create_command("INSERT INTO users (name) VALUES ('Test') RETURNING id")
|
81
|
+
result = command.execute_non_query
|
82
|
+
result.insert_id.should_not == 0;
|
83
|
+
result.to_i.should == 1;
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "DataObjects::Postgres::Reader" do
|
88
|
+
include PostgresSpecHelpers
|
89
|
+
|
90
|
+
before :all do
|
91
|
+
@connection = ensure_users_table_and_return_connection
|
92
|
+
@connection.create_command("INSERT INTO users (name) VALUES ('Test')").execute_non_query
|
93
|
+
@connection.create_command("INSERT INTO users (name) VALUES ('Test')").execute_non_query
|
94
|
+
@connection.create_command("INSERT INTO users (name) VALUES ('Test')").execute_non_query
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should raise errors on bad queries" do
|
98
|
+
command = @connection.create_command("SELT * FROM users")
|
99
|
+
lambda { command.execute_reader }.should raise_error
|
100
|
+
command = @connection.create_command("SELECT * FROM non_existant_table")
|
101
|
+
lambda { command.execute_reader }.should raise_error
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should open and close a reader" do
|
105
|
+
command = @connection.create_command("SELECT * FROM users LIMIT 3")
|
106
|
+
command.set_types [Integer, String]
|
107
|
+
reader = command.execute_reader
|
108
|
+
reader.close
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should typecast a value from the postgres type" do
|
112
|
+
command = @connection.create_command("SELECT id, name, registered, money FROM users ORDER BY id DESC LIMIT 3")
|
113
|
+
reader = command.execute_reader
|
114
|
+
reader.send(:instance_variable_get, "@field_count").should == 4
|
115
|
+
reader.send(:instance_variable_get, "@row_count").should == 3
|
116
|
+
while ( reader.next!)
|
117
|
+
reader.values[0].should be_a_kind_of(Integer)
|
118
|
+
reader.values[1].should be_a_kind_of(String)
|
119
|
+
reader.values[2].should == false
|
120
|
+
reader.values[3].should == 1908.56
|
121
|
+
end
|
122
|
+
reader.close
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should typecast from set_types" do
|
126
|
+
command = @connection.create_command("SELECT id, name FROM users ORDER BY id LIMIT 1")
|
127
|
+
command.set_types [Integer, String]
|
128
|
+
reader = command.execute_reader
|
129
|
+
reader.next!
|
130
|
+
reader.values[0].should be_a_kind_of(Integer)
|
131
|
+
reader.values[1].should be_a_kind_of(String)
|
132
|
+
reader.close
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should handle a null value" do
|
136
|
+
id = insert("INSERT INTO users (name) VALUES (NULL)")
|
137
|
+
select("SELECT name from users WHERE name is null") do |reader|
|
138
|
+
reader.values[0].should == nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should not convert empty strings to null" do
|
143
|
+
id = insert("INSERT INTO users (name) VALUES ('')")
|
144
|
+
select("SELECT name FROM users WHERE id = ?", [String], id) do |reader|
|
145
|
+
reader.values.first.should == ''
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should typecast a date field" do
|
150
|
+
command = @connection.create_command("SELECT created_on FROM users WHERE created_on is not null LIMIT 1")
|
151
|
+
reader = command.execute_reader
|
152
|
+
reader.next!
|
153
|
+
reader.values[0].should be_a_kind_of(Date)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should typecast a BigDecimal field" do
|
157
|
+
money_in_the_bank = BigDecimal.new('1.29')
|
158
|
+
|
159
|
+
id = insert("INSERT INTO users (name, money) VALUES (?, ?)", "MC Hammer", money_in_the_bank)
|
160
|
+
select("SELECT money FROM users WHERE id = ?", [BigDecimal], id) do |reader|
|
161
|
+
reader.values.first.should == money_in_the_bank
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should typecast a timestamp field" do
|
166
|
+
command = @connection.create_command("SELECT created_at FROM users WHERE created_at is not null LIMIT 1")
|
167
|
+
reader = command.execute_reader
|
168
|
+
reader.next!
|
169
|
+
dt = reader.values[0]
|
170
|
+
reader.values[0].should be_a_kind_of(DateTime)
|
171
|
+
|
172
|
+
command = @connection.create_command("SELECT created_at::date as date FROM users WHERE created_at is not null LIMIT 1")
|
173
|
+
reader = command.execute_reader
|
174
|
+
reader.next!
|
175
|
+
reader.values[0].should be_a_kind_of(Date)
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should work on a join" do
|
180
|
+
|
181
|
+
user = @connection.create_command("SELECT * FROM users WHERE id = 1").execute_reader
|
182
|
+
user.next!
|
183
|
+
|
184
|
+
@connection.create_command("INSERT INTO companies (name) VALUES ('ELEC')").execute_non_query
|
185
|
+
reader = @connection.create_command(<<-EOF).execute_reader
|
186
|
+
SELECT u.* FROM users AS u
|
187
|
+
LEFT JOIN companies AS c
|
188
|
+
ON u.company_id=c.id
|
189
|
+
WHERE c.name='ELEC'
|
190
|
+
EOF
|
191
|
+
reader.next!
|
192
|
+
reader.values.should == user.values
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should typecast a time field" do
|
196
|
+
pending "Time fields have no date information, and don't work with Time"
|
197
|
+
command = @connection.create_command("SELECT born_at FROM users LIMIT 1")
|
198
|
+
reader = command.execute_reader
|
199
|
+
reader.next!
|
200
|
+
reader.values[0].should be_a_kind_of(Time)
|
201
|
+
end
|
202
|
+
end
|