ydbi 0.5.0 → 0.5.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.
- checksums.yaml +4 -4
- data/ChangeLog +4 -0
- data/build/Rakefile.dbi.rb +4 -4
- data/lib/dbi/version.rb +1 -1
- data/test/dbi/tc_dbi.rb +1 -1
- metadata +9 -124
- data/.gitignore +0 -6
- data/Gemfile +0 -4
- data/Rakefile +0 -8
- data/TODO +0 -44
- data/bench/bench.rb +0 -79
- data/build/rake_task_lib.rb +0 -187
- data/doc/DBD_SPEC.rdoc +0 -88
- data/doc/DBI_SPEC.rdoc +0 -157
- data/doc/homepage/contact.html +0 -62
- data/doc/homepage/development.html +0 -124
- data/doc/homepage/index.html +0 -83
- data/doc/homepage/ruby-dbi.css +0 -91
- data/lib/dbd/Mysql.rb +0 -137
- data/lib/dbd/ODBC.rb +0 -89
- data/lib/dbd/Pg.rb +0 -188
- data/lib/dbd/SQLite.rb +0 -97
- data/lib/dbd/SQLite3.rb +0 -124
- data/lib/dbd/mysql/database.rb +0 -405
- data/lib/dbd/mysql/driver.rb +0 -125
- data/lib/dbd/mysql/statement.rb +0 -188
- data/lib/dbd/odbc/database.rb +0 -128
- data/lib/dbd/odbc/driver.rb +0 -38
- data/lib/dbd/odbc/statement.rb +0 -137
- data/lib/dbd/pg/database.rb +0 -516
- data/lib/dbd/pg/exec.rb +0 -47
- data/lib/dbd/pg/statement.rb +0 -160
- data/lib/dbd/pg/tuples.rb +0 -121
- data/lib/dbd/pg/type.rb +0 -209
- data/lib/dbd/sqlite/database.rb +0 -151
- data/lib/dbd/sqlite/statement.rb +0 -125
- data/lib/dbd/sqlite3/database.rb +0 -201
- data/lib/dbd/sqlite3/statement.rb +0 -78
- data/prototypes/types2.rb +0 -237
- data/setup.rb +0 -1585
- data/test/DBD_TESTS +0 -50
- data/test/TESTING +0 -16
- data/test/dbd/general/test_database.rb +0 -206
- data/test/dbd/general/test_statement.rb +0 -326
- data/test/dbd/general/test_types.rb +0 -296
- data/test/dbd/mysql/base.rb +0 -26
- data/test/dbd/mysql/down.sql +0 -19
- data/test/dbd/mysql/test_blob.rb +0 -18
- data/test/dbd/mysql/test_new_methods.rb +0 -7
- data/test/dbd/mysql/test_patches.rb +0 -111
- data/test/dbd/mysql/up.sql +0 -28
- data/test/dbd/odbc/base.rb +0 -30
- data/test/dbd/odbc/down.sql +0 -19
- data/test/dbd/odbc/test_new_methods.rb +0 -12
- data/test/dbd/odbc/test_ping.rb +0 -10
- data/test/dbd/odbc/test_statement.rb +0 -44
- data/test/dbd/odbc/test_transactions.rb +0 -58
- data/test/dbd/odbc/up.sql +0 -33
- data/test/dbd/postgresql/base.rb +0 -31
- data/test/dbd/postgresql/down.sql +0 -31
- data/test/dbd/postgresql/test_arrays.rb +0 -179
- data/test/dbd/postgresql/test_async.rb +0 -121
- data/test/dbd/postgresql/test_blob.rb +0 -36
- data/test/dbd/postgresql/test_bytea.rb +0 -87
- data/test/dbd/postgresql/test_ping.rb +0 -10
- data/test/dbd/postgresql/test_timestamp.rb +0 -77
- data/test/dbd/postgresql/test_transactions.rb +0 -58
- data/test/dbd/postgresql/testdbipg.rb +0 -307
- data/test/dbd/postgresql/up.sql +0 -60
- data/test/dbd/sqlite/base.rb +0 -32
- data/test/dbd/sqlite/test_database.rb +0 -30
- data/test/dbd/sqlite/test_driver.rb +0 -68
- data/test/dbd/sqlite/test_statement.rb +0 -112
- data/test/dbd/sqlite/up.sql +0 -25
- data/test/dbd/sqlite3/base.rb +0 -32
- data/test/dbd/sqlite3/test_database.rb +0 -77
- data/test/dbd/sqlite3/test_driver.rb +0 -67
- data/test/dbd/sqlite3/test_statement.rb +0 -88
- data/test/dbd/sqlite3/up.sql +0 -33
- data/test/ts_dbd.rb +0 -131
- data/ydbi.gemspec +0 -24
data/lib/dbd/sqlite/statement.rb
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# See DBI::BaseStatement.
|
3
|
-
#
|
4
|
-
class DBI::DBD::SQLite::Statement < DBI::BaseStatement
|
5
|
-
DBI_TYPE_MAP = [
|
6
|
-
[ /^INT(EGER)?$/i, DBI::SQL_INTEGER ],
|
7
|
-
[ /^(OID|ROWID|_ROWID_)$/i, DBI::SQL_OTHER ],
|
8
|
-
[ /^FLOAT$/i, DBI::SQL_FLOAT ],
|
9
|
-
[ /^REAL$/i, DBI::SQL_REAL ],
|
10
|
-
[ /^DOUBLE$/i, DBI::SQL_DOUBLE ],
|
11
|
-
[ /^DECIMAL/i, DBI::SQL_DECIMAL ],
|
12
|
-
[ /^(BOOL|BOOLEAN)$/i, DBI::SQL_BOOLEAN ],
|
13
|
-
[ /^TIME$/i, DBI::SQL_TIME ],
|
14
|
-
[ /^DATE$/i, DBI::SQL_DATE ],
|
15
|
-
[ /^TIMESTAMP$/i, DBI::SQL_TIMESTAMP ],
|
16
|
-
[ /^(VARCHAR|TEXT)/i, DBI::SQL_VARCHAR ],
|
17
|
-
[ /^CHAR$/i, DBI::SQL_CHAR ],
|
18
|
-
]
|
19
|
-
|
20
|
-
def initialize(stmt, dbh)
|
21
|
-
@dbh = dbh
|
22
|
-
@statement = DBI::SQL::PreparedStatement.new(@dbh, stmt)
|
23
|
-
@attr = { }
|
24
|
-
@params = [ ]
|
25
|
-
@rows = [ ]
|
26
|
-
@result_set = nil
|
27
|
-
@dbh.open_handles += 1
|
28
|
-
end
|
29
|
-
|
30
|
-
#
|
31
|
-
# See DBI::BaseStatement#bind_param. This method will also raise
|
32
|
-
# DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
|
33
|
-
# binding.
|
34
|
-
#
|
35
|
-
def bind_param(param, value, attributes=nil)
|
36
|
-
unless param.kind_of? Fixnum
|
37
|
-
raise DBI::InterfaceError, "Only numeric parameters are supported"
|
38
|
-
end
|
39
|
-
|
40
|
-
@params[param-1] = value
|
41
|
-
|
42
|
-
# FIXME what to do with attributes? are they important in SQLite?
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
# See DBI::BaseStatement#execute.
|
47
|
-
#
|
48
|
-
# In the event AutoCommit is off and no transaction is currently executing,
|
49
|
-
# one will be opened at this point. It is your responsibility to #finish,
|
50
|
-
# #cancel, #rollback, or #commit.
|
51
|
-
#
|
52
|
-
def execute
|
53
|
-
sql = @statement.bind(@params)
|
54
|
-
# XXX sqlite re-escapes this for us automatically, it's causing trouble with everything else.
|
55
|
-
# this will probably break in a horrible manner and I will be forced to "fix" it again.
|
56
|
-
sql.gsub!(/\\\\/) { '\\' }
|
57
|
-
DBI::DBD::SQLite.check_sql(sql)
|
58
|
-
|
59
|
-
begin
|
60
|
-
unless @dbh.db.transaction_active?
|
61
|
-
@dbh.db.transaction
|
62
|
-
end
|
63
|
-
@result_set = @dbh.db.query(sql)
|
64
|
-
@dbh.commit if @dbh["AutoCommit"]
|
65
|
-
rescue Exception => e
|
66
|
-
raise DBI::DatabaseError, e.message
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
alias :finish :cancel
|
71
|
-
|
72
|
-
def finish
|
73
|
-
# nil out the result set
|
74
|
-
@result_set.close if @result_set
|
75
|
-
@result_set = nil
|
76
|
-
@rows = nil
|
77
|
-
@dbh.open_handles -= 1
|
78
|
-
end
|
79
|
-
|
80
|
-
def fetch
|
81
|
-
return nil if @result_set.eof?
|
82
|
-
|
83
|
-
row = @result_set.next
|
84
|
-
return nil unless row
|
85
|
-
|
86
|
-
return row
|
87
|
-
end
|
88
|
-
|
89
|
-
def column_info
|
90
|
-
columns = [ ]
|
91
|
-
|
92
|
-
# FIXME this shit should *really* be abstracted into DBI
|
93
|
-
# FIXME this still doesn't handle nullable/unique/default stuff.
|
94
|
-
@result_set.columns.each_with_index do |name, i|
|
95
|
-
columns[i] = { } unless columns[i]
|
96
|
-
columns[i]["name"] = name
|
97
|
-
type_name = @result_set.types[i]
|
98
|
-
|
99
|
-
if type_name
|
100
|
-
m = DBI::DBD::SQLite.parse_type(type_name)
|
101
|
-
|
102
|
-
columns[i]["type_name"] = m[1]
|
103
|
-
columns[i]["precision"] = m[3].to_i if m[3]
|
104
|
-
columns[i]["scale"] = m[5].to_i if m[5]
|
105
|
-
DBI_TYPE_MAP.each do |map|
|
106
|
-
if columns[i]["type_name"] =~ map[0]
|
107
|
-
columns[i]["sql_type"] = map[1]
|
108
|
-
break
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
case columns[i]["type_name"]
|
113
|
-
when 'double'
|
114
|
-
columns[i]["dbi_type"] = DBI::Type::Float
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
return columns
|
120
|
-
end
|
121
|
-
|
122
|
-
def rows
|
123
|
-
return @dbh.db.changes
|
124
|
-
end
|
125
|
-
end
|
data/lib/dbd/sqlite3/database.rb
DELETED
@@ -1,201 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# See DBI::BaseDatabase.
|
3
|
-
#
|
4
|
-
class DBI::DBD::SQLite3::Database < DBI::BaseDatabase
|
5
|
-
#
|
6
|
-
# Constructor. Valid attributes:
|
7
|
-
#
|
8
|
-
# * AutoCommit: Commit after every statement execution.
|
9
|
-
#
|
10
|
-
# The following attributes go directly to the low-level SQLite3 driver.
|
11
|
-
# Please consult it's documentation for more information.
|
12
|
-
#
|
13
|
-
# * auto_vacuum
|
14
|
-
# * cache_size
|
15
|
-
# * default_cache_size
|
16
|
-
# * default_synchronous
|
17
|
-
# * default_temp_store
|
18
|
-
# * full_column_names
|
19
|
-
# * synchronous
|
20
|
-
# * temp_store
|
21
|
-
# * type_translation
|
22
|
-
#
|
23
|
-
def initialize(dbname, attr)
|
24
|
-
@db = ::SQLite3::Database.new(dbname)
|
25
|
-
|
26
|
-
@db.type_translation = false
|
27
|
-
|
28
|
-
@attr = {'AutoCommit' => true}
|
29
|
-
if attr then
|
30
|
-
attr.each_pair do |key, value|
|
31
|
-
begin
|
32
|
-
self[key] = value
|
33
|
-
rescue DBI::NotSupportedError
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
__generate_attr__
|
38
|
-
end
|
39
|
-
|
40
|
-
def disconnect()
|
41
|
-
@db.rollback if @db.transaction_active?
|
42
|
-
@db.close
|
43
|
-
end
|
44
|
-
|
45
|
-
def prepare(statement)
|
46
|
-
DBI::DBD::SQLite3::Statement.new(statement, @db)
|
47
|
-
end
|
48
|
-
|
49
|
-
def database_name
|
50
|
-
st = DBI::DBD::SQLite3::Statement.new('PRAGMA database_list', @db)
|
51
|
-
st.execute
|
52
|
-
row = st.fetch
|
53
|
-
st.finish
|
54
|
-
|
55
|
-
return row[2]
|
56
|
-
end
|
57
|
-
|
58
|
-
def ping()
|
59
|
-
not @db.closed?
|
60
|
-
end
|
61
|
-
|
62
|
-
def commit()
|
63
|
-
if @db.transaction_active?
|
64
|
-
@db.commit
|
65
|
-
@db.transaction
|
66
|
-
else
|
67
|
-
raise DBI::ProgrammingError.new("No active transaction.")
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
#
|
72
|
-
# See DBI::BaseDatabase#rollback.
|
73
|
-
#
|
74
|
-
# If all statements were not closed before the rollback occurs, a
|
75
|
-
# DBI::Warning may be raised if the database encounters an error because of
|
76
|
-
# it.
|
77
|
-
#
|
78
|
-
# This method will also raise DBI::ProgrammingError if not in a
|
79
|
-
# transaction.
|
80
|
-
#
|
81
|
-
def rollback()
|
82
|
-
if @db.transaction_active?
|
83
|
-
begin
|
84
|
-
@db.rollback
|
85
|
-
@db.transaction
|
86
|
-
rescue Exception => e
|
87
|
-
raise DBI::Warning, "Statements were not closed prior to rollback"
|
88
|
-
end
|
89
|
-
else
|
90
|
-
raise DBI::ProgrammingError.new("No active transaction.")
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def tables()
|
95
|
-
ret = []
|
96
|
-
result = @db.execute(%q(
|
97
|
-
SELECT name FROM sqlite_master WHERE type IN ('table', 'view')
|
98
|
-
UNION ALL
|
99
|
-
SELECT name FROM sqlite_temp_master WHERE type in ('table', 'view') ORDER BY 1
|
100
|
-
))
|
101
|
-
result.each{|row| ret.push(row[0])}
|
102
|
-
ret
|
103
|
-
end
|
104
|
-
|
105
|
-
#
|
106
|
-
# See DBI::BaseDatabase#columns.
|
107
|
-
#
|
108
|
-
# Additional Attributes:
|
109
|
-
#
|
110
|
-
# * sql_type: XOPEN integer SQL Type.
|
111
|
-
# * nullable: true if NULL is allowed in this column.
|
112
|
-
# * default: the value that will be used in new rows if this column
|
113
|
-
# receives no data.
|
114
|
-
#
|
115
|
-
def columns(table)
|
116
|
-
@db.type_translation = false
|
117
|
-
ret =
|
118
|
-
@db.table_info(table).map do |hash|
|
119
|
-
m = DBI::DBD::SQLite3.parse_type(hash['type'])
|
120
|
-
h = {
|
121
|
-
'name' => hash['name'],
|
122
|
-
'type_name' => m[1],
|
123
|
-
'sql_type' =>
|
124
|
-
begin
|
125
|
-
DBI.const_get('SQL_'+hash['type'].upcase)
|
126
|
-
rescue NameError
|
127
|
-
DBI::SQL_OTHER
|
128
|
-
end,
|
129
|
-
'nullable' => (hash['notnull'] == '0'),
|
130
|
-
'default' => (@attr['type_translation'] && (not hash['dflt_value'])) ?
|
131
|
-
@db.translator.translate(hash['type'], hash['dflt_value']) :
|
132
|
-
hash['dflt_value']
|
133
|
-
}
|
134
|
-
|
135
|
-
h['precision'] = m[3].to_i if m[3]
|
136
|
-
h['scale'] = m[5].to_i if m[5]
|
137
|
-
|
138
|
-
h
|
139
|
-
end
|
140
|
-
@db.type_translation = @attr['type_translation']
|
141
|
-
ret
|
142
|
-
end
|
143
|
-
|
144
|
-
def quote(value)
|
145
|
-
::SQLite3::Database.quote(value.to_s)
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
# This method is used to aid the constructor and probably should not be
|
150
|
-
# used independently.
|
151
|
-
#
|
152
|
-
def __generate_attr__()
|
153
|
-
tt = @db.type_translation
|
154
|
-
@db.type_translation = false
|
155
|
-
[ 'auto_vacuum', 'cache_size', 'default_cache_size',
|
156
|
-
'default_synchronous', 'default_temp_store', 'full_column_names',
|
157
|
-
'synchronous', 'temp_store', 'type_translation' ].each do |key|
|
158
|
-
unless @attr.has_key?(key) then
|
159
|
-
@attr[key] = @db.__send__(key)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
@db.type_translation = tt
|
163
|
-
end
|
164
|
-
|
165
|
-
#
|
166
|
-
# See #new for valid attributes.
|
167
|
-
#
|
168
|
-
# If Autocommit is set to true, commit happens immediately if a transaction
|
169
|
-
# is open.
|
170
|
-
#
|
171
|
-
def []=(attr, value)
|
172
|
-
case attr
|
173
|
-
when 'AutoCommit'
|
174
|
-
if value
|
175
|
-
@db.commit if @db.transaction_active?
|
176
|
-
else
|
177
|
-
@db.transaction unless @db.transaction_active?
|
178
|
-
end
|
179
|
-
@attr[attr] = value
|
180
|
-
when 'auto_vacuum', 'cache_size', 'count_changes',
|
181
|
-
'default_cache_size', 'encoding', 'full_column_names',
|
182
|
-
'page_size', 'short_column_names', 'synchronous',
|
183
|
-
'temp_store', 'temp_store_directory'
|
184
|
-
@db.__send__((attr+'='), value)
|
185
|
-
@attr[attr] = @db.__send__(attr)
|
186
|
-
when 'busy_timeout'
|
187
|
-
@db.busy_timeout(value)
|
188
|
-
@attr[attr] = value
|
189
|
-
when 'busy_handler'
|
190
|
-
@db.busy_timeout(&value)
|
191
|
-
@attr[attr] = value
|
192
|
-
when 'type_translation'
|
193
|
-
@db.type_translation = value
|
194
|
-
@attr[attr] = value
|
195
|
-
else
|
196
|
-
raise DBI::NotSupportedError
|
197
|
-
end
|
198
|
-
|
199
|
-
return value
|
200
|
-
end
|
201
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# See DBI::BaseStatement.
|
3
|
-
#
|
4
|
-
class DBI::DBD::SQLite3::Statement < DBI::BaseStatement
|
5
|
-
def initialize(sql, db)
|
6
|
-
sql.gsub!(/\\\\/) { '\\' } # sqlite underneath does this for us automatically, and it's causing trouble with the rest of the system.
|
7
|
-
@sql = sql
|
8
|
-
@db = db
|
9
|
-
@stmt = db.prepare(sql)
|
10
|
-
@result = nil
|
11
|
-
rescue ::SQLite3::Exception, RuntimeError => err
|
12
|
-
raise DBI::ProgrammingError.new(err.message)
|
13
|
-
end
|
14
|
-
|
15
|
-
#
|
16
|
-
# See DBI::BaseStatement#bind_param. This method will also raise
|
17
|
-
# DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
|
18
|
-
# binding.
|
19
|
-
#
|
20
|
-
def bind_param(param, value, attribs=nil)
|
21
|
-
raise DBI::InterfaceError, "Bound parameter must be an integer" unless param.kind_of? Fixnum
|
22
|
-
@stmt.bind_param(param, value)
|
23
|
-
end
|
24
|
-
|
25
|
-
def execute()
|
26
|
-
@result = @stmt.execute
|
27
|
-
@rows = DBI::SQL.query?(@sql) ? 0 : @db.changes
|
28
|
-
end
|
29
|
-
|
30
|
-
def finish()
|
31
|
-
@stmt.close rescue nil
|
32
|
-
@result = nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def fetch()
|
36
|
-
ret = @result.next
|
37
|
-
return ret unless ret
|
38
|
-
[ret].flatten
|
39
|
-
end
|
40
|
-
|
41
|
-
def column_info()
|
42
|
-
@stmt.columns.zip(@stmt.types).map{|name, type_name|
|
43
|
-
m = DBI::DBD::SQLite3.parse_type(type_name)
|
44
|
-
h = {
|
45
|
-
'name' => name,
|
46
|
-
'type_name' => m[1],
|
47
|
-
'sql_type' =>
|
48
|
-
begin
|
49
|
-
DBI.const_get('SQL_'+m[1].upcase)
|
50
|
-
rescue NameError
|
51
|
-
DBI::SQL_OTHER
|
52
|
-
end,
|
53
|
-
}
|
54
|
-
h['precision'] = m[3].to_i if m[3]
|
55
|
-
h['scale'] = m[5].to_i if m[5]
|
56
|
-
|
57
|
-
case h['type_name']
|
58
|
-
when 'double'
|
59
|
-
h['dbi_type'] = DBI::Type::Float
|
60
|
-
end
|
61
|
-
|
62
|
-
h
|
63
|
-
}
|
64
|
-
end
|
65
|
-
|
66
|
-
def rows()
|
67
|
-
@rows
|
68
|
-
end
|
69
|
-
|
70
|
-
def bind_params(*bindvars)
|
71
|
-
@stmt.bind_params(bindvars)
|
72
|
-
end
|
73
|
-
|
74
|
-
def cancel()
|
75
|
-
@result = nil
|
76
|
-
@index = 0
|
77
|
-
end
|
78
|
-
end
|
data/prototypes/types2.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
require 'date'
|
2
|
-
require 'time'
|
3
|
-
|
4
|
-
#
|
5
|
-
# General example, these would be types that would exist in DBI proper
|
6
|
-
#
|
7
|
-
|
8
|
-
module DBI
|
9
|
-
class DBI::Type
|
10
|
-
def self.parse(obj, type=nil, dbh=nil)
|
11
|
-
if type
|
12
|
-
sym = ( "to_" + type.to_s ).to_sym
|
13
|
-
begin
|
14
|
-
return self.__send__(sym, obj)
|
15
|
-
rescue ::NoMethodError
|
16
|
-
self.to_type(obj)
|
17
|
-
end
|
18
|
-
else
|
19
|
-
return self.to_type(obj)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.coerce(obj, type=nil, dbh=nil)
|
24
|
-
if type
|
25
|
-
sym = ( "from_" + type.to_s ).to_sym
|
26
|
-
begin
|
27
|
-
return self.__send__(sym, obj)
|
28
|
-
rescue ::NoMethodError
|
29
|
-
self.from_type(obj)
|
30
|
-
end
|
31
|
-
else
|
32
|
-
return self.from_type(obj)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.from_type(obj)
|
37
|
-
obj.to_s rescue obj.to_str rescue obj
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.to_type(obj)
|
41
|
-
obj
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class DBI::Type::Null < DBI::Type
|
46
|
-
def self.to_type(obj)
|
47
|
-
return obj unless obj
|
48
|
-
return nil if obj.to_s.match(/^null$/i)
|
49
|
-
return obj
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.from_type(obj)
|
53
|
-
obj
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class DBI::Type::Integer < DBI::Type::Null
|
58
|
-
def self.parse(obj)
|
59
|
-
obj = super
|
60
|
-
return obj unless obj
|
61
|
-
return obj.to_i if obj.respond_to? :to_i
|
62
|
-
return obj
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class DBI::Type::Timestamp < DBI::Type::Null
|
67
|
-
def self.to_type(obj)
|
68
|
-
obj = super
|
69
|
-
return obj unless obj
|
70
|
-
|
71
|
-
case obj
|
72
|
-
when ::DateTime
|
73
|
-
return obj
|
74
|
-
when ::Date
|
75
|
-
return ::DateTime.strptime(obj.to_s, "%Y-%m-%d")
|
76
|
-
when ::Time
|
77
|
-
return ::DateTime.parse(obj.to_s)
|
78
|
-
when ::Integer
|
79
|
-
return ::DateTime.parse(::Time.at(obj).to_s)
|
80
|
-
else
|
81
|
-
return ::DateTime.parse(obj.to_s) if obj.respond_to? :to_s
|
82
|
-
return ::DateTime.parse(obj.to_str) if obj.respond_to? :to_str
|
83
|
-
return obj
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.from_type(obj)
|
88
|
-
obj = super
|
89
|
-
return obj unless obj
|
90
|
-
|
91
|
-
case obj
|
92
|
-
when ::DateTime
|
93
|
-
return obj.to_s # produces ISO8601
|
94
|
-
when ::Time
|
95
|
-
return obj.iso8601
|
96
|
-
when ::Integer
|
97
|
-
return ::Time.at(obj).iso8601
|
98
|
-
else
|
99
|
-
return obj
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
module DBI::DBD
|
106
|
-
class Pg
|
107
|
-
|
108
|
-
#
|
109
|
-
# during connect time, after DatabaseHandle initialization, the hash
|
110
|
-
# that DatabaseHandle#type_map provides would be tweaked to take
|
111
|
-
# advantage of the available date formats.
|
112
|
-
#
|
113
|
-
# See 'PgDatabaseHandle' below for a mock.
|
114
|
-
#
|
115
|
-
|
116
|
-
class Type
|
117
|
-
class Timestamp < DBI::Type::Timestamp
|
118
|
-
def self.from_dmy(obj)
|
119
|
-
return obj if DBI::Type::Null.parse(obj).nil?
|
120
|
-
|
121
|
-
case obj
|
122
|
-
when ::DateTime, ::Time
|
123
|
-
obj.strftime("%d/%m/%Y %H:%M:%S")
|
124
|
-
when ::Integer
|
125
|
-
::Time.at(obj).strftime("%d/%m/%Y %H:%M:%S")
|
126
|
-
else
|
127
|
-
# punt... this will actually try the baseline
|
128
|
-
# conversion at this point
|
129
|
-
raise "Crap!"
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def self.to_dmy(obj)
|
134
|
-
return obj if DBI::Type::Null.parse(obj).nil?
|
135
|
-
|
136
|
-
# realistically all there needs to be is a check for the
|
137
|
-
# type ruby-pg typically returns and string, but to be
|
138
|
-
# complete I'm showing how it could be done if the type was
|
139
|
-
# less clear.
|
140
|
-
|
141
|
-
case obj
|
142
|
-
when ::DateTime
|
143
|
-
return obj
|
144
|
-
when ::Time
|
145
|
-
return ::DateTime.parse(obj.to_s)
|
146
|
-
else
|
147
|
-
return ::DateTime.strptime(obj, "%d/%m/%Y %H:%M:%S")
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
#
|
156
|
-
# this is just used to emulate the methods a DatabaseHandle would have to
|
157
|
-
# faciliate this.. certainly not a full (or correct) mirroring of the DBI API.
|
158
|
-
#
|
159
|
-
class DatabaseHandle
|
160
|
-
|
161
|
-
attr_accessor :columns
|
162
|
-
|
163
|
-
def outbound_type_map
|
164
|
-
{
|
165
|
-
'timestamp' => [DBI::Type::Timestamp]
|
166
|
-
}
|
167
|
-
end
|
168
|
-
|
169
|
-
def inbound_type_map
|
170
|
-
{
|
171
|
-
::DateTime => [DBI::Type::Timestamp],
|
172
|
-
::Time => [DBI::Type::Timestamp]
|
173
|
-
}
|
174
|
-
end
|
175
|
-
|
176
|
-
# humor me while I completely break DBI for the sake of brevity..
|
177
|
-
def execute(*bindvars)
|
178
|
-
bindvars.collect do |var|
|
179
|
-
type_info = inbound_type_map[var.class]
|
180
|
-
type_info[0].coerce(var, type_info[1], self)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def fetch(*bindvars)
|
185
|
-
ret = []
|
186
|
-
|
187
|
-
bindvars.each_with_index do |var, i|
|
188
|
-
type_info = outbound_type_map[columns[i]]
|
189
|
-
ret.push type_info[0].parse(var, type_info[1], self)
|
190
|
-
end
|
191
|
-
|
192
|
-
return ret
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
class PgDatabaseHandle < DatabaseHandle
|
197
|
-
def outbound_type_map
|
198
|
-
{
|
199
|
-
'timestamp' => [DBI::DBD::Pg::Type::Timestamp, :dmy]
|
200
|
-
}
|
201
|
-
end
|
202
|
-
|
203
|
-
def inbound_type_map
|
204
|
-
{
|
205
|
-
::DateTime => [DBI::DBD::Pg::Type::Timestamp, :dmy],
|
206
|
-
::Time => [DBI::DBD::Pg::Type::Timestamp, :dmy]
|
207
|
-
}
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# ok! now for the functional example:
|
212
|
-
|
213
|
-
if __FILE__ == $0
|
214
|
-
|
215
|
-
dbh = DatabaseHandle.new
|
216
|
-
dbh.columns = %w(timestamp timestamp)
|
217
|
-
# this would go TO the database..
|
218
|
-
p dbh.execute(DateTime.now, Time.now)
|
219
|
-
# this would come FROM the database...
|
220
|
-
p dbh.fetch(Time.now.iso8601, DateTime.now.to_s)
|
221
|
-
|
222
|
-
# now the Pg example:
|
223
|
-
dbh = PgDatabaseHandle.new
|
224
|
-
dbh.columns = %w(timestamp timestamp)
|
225
|
-
|
226
|
-
# this would go TO the database..
|
227
|
-
p dbh.execute(DateTime.now, Time.now)
|
228
|
-
# this would come FROM the database...
|
229
|
-
p dbh.fetch(Time.now.strftime("%d/%m/%Y %H:%M:%S"), DateTime.now.strftime("%d/%m/%Y %H:%M:%S"))
|
230
|
-
|
231
|
-
# this should fail appropriately
|
232
|
-
begin
|
233
|
-
dbh.fetch(Time.now.iso8601, DateTime.now.to_s)
|
234
|
-
rescue Exception
|
235
|
-
puts "this failed like it should"
|
236
|
-
end
|
237
|
-
end
|