do_postgres 0.2.4 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|