sequel 3.28.0 → 3.29.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.
- data/CHANGELOG +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
|
@@ -14,9 +14,6 @@ module Sequel
|
|
|
14
14
|
def boolean(s) s.to_i != 0 end
|
|
15
15
|
def integer(s) s.to_i end
|
|
16
16
|
def float(s) s.to_f end
|
|
17
|
-
def date(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
|
|
18
|
-
def time(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
|
|
19
|
-
def timestamp(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
|
|
20
17
|
end.new
|
|
21
18
|
|
|
22
19
|
# Hash with integer keys and callable values for converting MySQL types.
|
|
@@ -30,52 +27,12 @@ module Sequel
|
|
|
30
27
|
k.each{|n| MYSQL_TYPES[n] = v}
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
# Modify the type translator used for the tinyint type based
|
|
34
|
-
# on the value given.
|
|
35
|
-
def self.convert_tinyint_to_bool=(v)
|
|
36
|
-
MYSQL_TYPES[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
|
|
37
|
-
@convert_tinyint_to_bool = v
|
|
38
|
-
end
|
|
39
|
-
self.convert_tinyint_to_bool = convert_tinyint_to_bool
|
|
40
|
-
|
|
41
30
|
class << self
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
# like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
|
|
45
|
-
# it returns the strings as is.
|
|
46
|
-
attr_reader :convert_invalid_date_time
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Modify the type translators for the date, time, and timestamp types
|
|
50
|
-
# depending on the value given.
|
|
51
|
-
def self.convert_invalid_date_time=(v)
|
|
52
|
-
MYSQL_TYPES[11] = (v != false) ? TYPE_TRANSLATOR.method(:time) : ::Sequel.method(:string_to_time)
|
|
53
|
-
m = (v != false) ? TYPE_TRANSLATOR.method(:date) : ::Sequel.method(:string_to_date)
|
|
54
|
-
[10, 14].each{|i| MYSQL_TYPES[i] = m}
|
|
55
|
-
m = (v != false) ? TYPE_TRANSLATOR.method(:timestamp) : ::Sequel.method(:database_to_application_timestamp)
|
|
56
|
-
[7, 12].each{|i| MYSQL_TYPES[i] = m}
|
|
57
|
-
@convert_invalid_date_time = v
|
|
31
|
+
# Whether to convert invalid date time values by default.
|
|
32
|
+
attr_accessor :convert_invalid_date_time
|
|
58
33
|
end
|
|
59
34
|
self.convert_invalid_date_time = false
|
|
60
35
|
|
|
61
|
-
# If convert_invalid_date_time is nil, :nil, or :string and
|
|
62
|
-
# the conversion raises an InvalidValue exception, return v
|
|
63
|
-
# if :string and nil otherwise.
|
|
64
|
-
def self.convert_date_time(meth, v)
|
|
65
|
-
begin
|
|
66
|
-
Sequel.send(meth, v)
|
|
67
|
-
rescue InvalidValue
|
|
68
|
-
case @convert_invalid_date_time
|
|
69
|
-
when nil, :nil
|
|
70
|
-
nil
|
|
71
|
-
when :string
|
|
72
|
-
v
|
|
73
|
-
else
|
|
74
|
-
raise
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
36
|
# Database class for MySQL databases used with Sequel.
|
|
80
37
|
class Database < Sequel::Database
|
|
81
38
|
include Sequel::MySQL::DatabaseMethods
|
|
@@ -85,7 +42,26 @@ module Sequel
|
|
|
85
42
|
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|Lost connection to MySQL server during query)/
|
|
86
43
|
|
|
87
44
|
set_adapter_scheme :mysql
|
|
45
|
+
|
|
46
|
+
# Hash of conversion procs for the current database
|
|
47
|
+
attr_reader :conversion_procs
|
|
48
|
+
#
|
|
49
|
+
# Whether to convert tinyint columns to bool for the current database
|
|
50
|
+
attr_reader :convert_tinyint_to_bool
|
|
51
|
+
|
|
52
|
+
# By default, Sequel raises an exception if in invalid date or time is used.
|
|
53
|
+
# However, if this is set to nil or :nil, the adapter treats dates
|
|
54
|
+
# like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
|
|
55
|
+
# it returns the strings as is.
|
|
56
|
+
attr_reader :convert_invalid_date_time
|
|
88
57
|
|
|
58
|
+
def initialize(opts={})
|
|
59
|
+
super
|
|
60
|
+
@conversion_procs = MYSQL_TYPES.dup
|
|
61
|
+
self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
|
|
62
|
+
self.convert_invalid_date_time = Sequel::MySQL.convert_invalid_date_time
|
|
63
|
+
end
|
|
64
|
+
|
|
89
65
|
# Connect to the database. In addition to the usual database options,
|
|
90
66
|
# the following options have effect:
|
|
91
67
|
#
|
|
@@ -155,11 +131,27 @@ module Sequel
|
|
|
155
131
|
conn
|
|
156
132
|
end
|
|
157
133
|
|
|
158
|
-
#
|
|
159
|
-
|
|
160
|
-
|
|
134
|
+
# Modify the type translators for the date, time, and timestamp types
|
|
135
|
+
# depending on the value given.
|
|
136
|
+
def convert_invalid_date_time=(v)
|
|
137
|
+
m0 = ::Sequel.method(:string_to_time)
|
|
138
|
+
@conversion_procs[11] = (v != false) ? lambda{|v| convert_date_time(v, &m0)} : m0
|
|
139
|
+
m1 = ::Sequel.method(:string_to_date)
|
|
140
|
+
m = (v != false) ? lambda{|v| convert_date_time(v, &m1)} : m1
|
|
141
|
+
[10, 14].each{|i| @conversion_procs[i] = m}
|
|
142
|
+
m2 = method(:to_application_timestamp)
|
|
143
|
+
m = (v != false) ? lambda{|v| convert_date_time(v, &m2)} : m2
|
|
144
|
+
[7, 12].each{|i| @conversion_procs[i] = m}
|
|
145
|
+
@convert_invalid_date_time = v
|
|
161
146
|
end
|
|
162
|
-
|
|
147
|
+
|
|
148
|
+
# Modify the type translator used for the tinyint type based
|
|
149
|
+
# on the value given.
|
|
150
|
+
def convert_tinyint_to_bool=(v)
|
|
151
|
+
@conversion_procs[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
|
|
152
|
+
@convert_tinyint_to_bool = v
|
|
153
|
+
end
|
|
154
|
+
|
|
163
155
|
# Return the version of the MySQL server two which we are connecting.
|
|
164
156
|
def server_version(server=nil)
|
|
165
157
|
@server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
|
|
@@ -219,6 +211,24 @@ module Sequel
|
|
|
219
211
|
:query
|
|
220
212
|
end
|
|
221
213
|
|
|
214
|
+
# If convert_invalid_date_time is nil, :nil, or :string and
|
|
215
|
+
# the conversion raises an InvalidValue exception, return v
|
|
216
|
+
# if :string and nil otherwise.
|
|
217
|
+
def convert_date_time(v)
|
|
218
|
+
begin
|
|
219
|
+
yield v
|
|
220
|
+
rescue InvalidValue
|
|
221
|
+
case @convert_invalid_date_time
|
|
222
|
+
when nil, :nil
|
|
223
|
+
nil
|
|
224
|
+
when :string
|
|
225
|
+
v
|
|
226
|
+
else
|
|
227
|
+
raise
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
222
232
|
# The MySQL adapter main error class is Mysql::Error
|
|
223
233
|
def database_error_classes
|
|
224
234
|
[Mysql::Error]
|
|
@@ -245,7 +255,7 @@ module Sequel
|
|
|
245
255
|
|
|
246
256
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
|
247
257
|
def schema_column_type(db_type)
|
|
248
|
-
|
|
258
|
+
convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
|
249
259
|
end
|
|
250
260
|
end
|
|
251
261
|
|
|
@@ -253,6 +263,8 @@ module Sequel
|
|
|
253
263
|
class Dataset < Sequel::Dataset
|
|
254
264
|
include Sequel::MySQL::DatasetMethods
|
|
255
265
|
include Sequel::MySQL::PreparedStatements::DatasetMethods
|
|
266
|
+
|
|
267
|
+
Database::DatasetClass = self
|
|
256
268
|
|
|
257
269
|
# Delete rows matching this dataset
|
|
258
270
|
def delete
|
|
@@ -265,11 +277,12 @@ module Sequel
|
|
|
265
277
|
def fetch_rows(sql, &block)
|
|
266
278
|
execute(sql) do |r|
|
|
267
279
|
i = -1
|
|
280
|
+
cps = db.conversion_procs
|
|
268
281
|
cols = r.fetch_fields.map do |f|
|
|
269
282
|
# Pretend tinyint is another integer type if its length is not 1, to
|
|
270
283
|
# avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
|
|
271
284
|
# is set.
|
|
272
|
-
type_proc = f.type == 1 && f.length != 1 ?
|
|
285
|
+
type_proc = f.type == 1 && f.length != 1 ? cps[2] : cps[f.type]
|
|
273
286
|
[output_identifier(f.name), type_proc, i+=1]
|
|
274
287
|
end
|
|
275
288
|
@columns = cols.map{|c| c.first}
|
|
@@ -10,9 +10,18 @@ module Sequel
|
|
|
10
10
|
include Sequel::MySQL::PreparedStatements::DatabaseMethods
|
|
11
11
|
|
|
12
12
|
# Mysql::Error messages that indicate the current connection should be disconnected
|
|
13
|
-
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
|
|
13
|
+
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|This connection is still waiting for a result, try again once you have the result)/
|
|
14
14
|
|
|
15
15
|
set_adapter_scheme :mysql2
|
|
16
|
+
|
|
17
|
+
# Whether to convert tinyint columns to bool for this database
|
|
18
|
+
attr_accessor :convert_tinyint_to_bool
|
|
19
|
+
|
|
20
|
+
# Set the convert_tinyint_to_bool setting based on the default value.
|
|
21
|
+
def initialize(opts={})
|
|
22
|
+
super
|
|
23
|
+
self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
|
|
24
|
+
end
|
|
16
25
|
|
|
17
26
|
# Connect to the database. In addition to the usual database options,
|
|
18
27
|
# the following options have effect:
|
|
@@ -60,11 +69,6 @@ module Sequel
|
|
|
60
69
|
conn
|
|
61
70
|
end
|
|
62
71
|
|
|
63
|
-
# Returns instance of Sequel::MySQL::Dataset with the given options.
|
|
64
|
-
def dataset(opts = nil)
|
|
65
|
-
Mysql2::Dataset.new(self, opts)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
72
|
# Return the version of the MySQL server two which we are connecting.
|
|
69
73
|
def server_version(server=nil)
|
|
70
74
|
@server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
|
|
@@ -77,7 +81,7 @@ module Sequel
|
|
|
77
81
|
# yield the connection if a block is given.
|
|
78
82
|
def _execute(conn, sql, opts)
|
|
79
83
|
begin
|
|
80
|
-
r = log_yield(sql){conn.query(sql, :symbolize_keys => true, :database_timezone =>
|
|
84
|
+
r = log_yield(sql){conn.query(sql, :symbolize_keys => true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone)}
|
|
81
85
|
if opts[:type] == :select
|
|
82
86
|
yield r if r
|
|
83
87
|
elsif block_given?
|
|
@@ -115,7 +119,7 @@ module Sequel
|
|
|
115
119
|
|
|
116
120
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
|
117
121
|
def schema_column_type(db_type)
|
|
118
|
-
|
|
122
|
+
convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
|
119
123
|
end
|
|
120
124
|
end
|
|
121
125
|
|
|
@@ -124,6 +128,8 @@ module Sequel
|
|
|
124
128
|
include Sequel::MySQL::DatasetMethods
|
|
125
129
|
include Sequel::MySQL::PreparedStatements::DatasetMethods
|
|
126
130
|
|
|
131
|
+
Database::DatasetClass = self
|
|
132
|
+
|
|
127
133
|
# Delete rows matching this dataset
|
|
128
134
|
def delete
|
|
129
135
|
execute_dui(delete_sql){|c| return c.affected_rows}
|
|
@@ -133,7 +139,7 @@ module Sequel
|
|
|
133
139
|
def fetch_rows(sql, &block)
|
|
134
140
|
execute(sql) do |r|
|
|
135
141
|
@columns = r.fields
|
|
136
|
-
r.each(:cast_booleans =>
|
|
142
|
+
r.each(:cast_booleans => db.convert_tinyint_to_bool, &block)
|
|
137
143
|
end
|
|
138
144
|
self
|
|
139
145
|
end
|
data/lib/sequel/adapters/odbc.rb
CHANGED
|
@@ -15,13 +15,16 @@ module Sequel
|
|
|
15
15
|
when 'mssql'
|
|
16
16
|
Sequel.ts_require 'adapters/odbc/mssql'
|
|
17
17
|
extend Sequel::ODBC::MSSQL::DatabaseMethods
|
|
18
|
+
@dataset_class = Sequel::ODBC::MSSQL::Dataset
|
|
18
19
|
set_mssql_unicode_strings
|
|
19
20
|
when 'progress'
|
|
20
21
|
Sequel.ts_require 'adapters/shared/progress'
|
|
21
22
|
extend Sequel::Progress::DatabaseMethods
|
|
23
|
+
extend_datasets(Sequel::Progress::DatasetMethods)
|
|
22
24
|
when 'db2'
|
|
23
25
|
Sequel.ts_require 'adapters/odbc/db2'
|
|
24
26
|
extend Sequel::ODBC::DB2::DatabaseMethods
|
|
27
|
+
@dataset_class = Sequel::ODBC::DB2::Dataset
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
@@ -45,10 +48,6 @@ module Sequel
|
|
|
45
48
|
conn
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
def dataset(opts = nil)
|
|
49
|
-
ODBC::Dataset.new(self, opts)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
51
|
def execute(sql, opts={})
|
|
53
52
|
synchronize(opts[:server]) do |conn|
|
|
54
53
|
begin
|
|
@@ -99,15 +98,23 @@ module Sequel
|
|
|
99
98
|
ODBC_DATE_FORMAT = "{d '%Y-%m-%d'}".freeze
|
|
100
99
|
TIMESTAMP_FORMAT="{ts '%Y-%m-%d %H:%M:%S'}".freeze
|
|
101
100
|
|
|
101
|
+
Database::DatasetClass = self
|
|
102
|
+
|
|
102
103
|
def fetch_rows(sql)
|
|
103
104
|
execute(sql) do |s|
|
|
104
105
|
i = -1
|
|
105
106
|
cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
|
|
106
|
-
|
|
107
|
+
columns = cols.map{|c| c.at(0)}
|
|
108
|
+
if opts[:offset] && offset_returns_row_number_column?
|
|
109
|
+
rn = row_number_column
|
|
110
|
+
columns.delete(rn)
|
|
111
|
+
end
|
|
112
|
+
@columns = columns
|
|
107
113
|
if rows = s.fetch_all
|
|
108
114
|
rows.each do |row|
|
|
109
115
|
hash = {}
|
|
110
116
|
cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
|
|
117
|
+
hash.delete(rn) if rn
|
|
111
118
|
yield hash
|
|
112
119
|
end
|
|
113
120
|
end
|
|
@@ -126,7 +133,7 @@ module Sequel
|
|
|
126
133
|
# ODBCColumn#mapSqlTypeToGenericType and Column#klass.
|
|
127
134
|
case v
|
|
128
135
|
when ::ODBC::TimeStamp
|
|
129
|
-
|
|
136
|
+
db.to_application_timestamp([v.year, v.month, v.day, v.hour, v.minute, v.second])
|
|
130
137
|
when ::ODBC::Time
|
|
131
138
|
Sequel::SQLTime.create(v.hour, v.minute, v.second)
|
|
132
139
|
when ::ODBC::Date
|
|
@@ -9,11 +9,6 @@ module Sequel
|
|
|
9
9
|
include Sequel::MSSQL::DatabaseMethods
|
|
10
10
|
LAST_INSERT_ID_SQL='SELECT SCOPE_IDENTITY()'
|
|
11
11
|
|
|
12
|
-
# Return an instance of Sequel::ODBC::MSSQL::Dataset with the given opts.
|
|
13
|
-
def dataset(opts=nil)
|
|
14
|
-
Sequel::ODBC::MSSQL::Dataset.new(self, opts)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
# Return the last inserted identity value.
|
|
18
13
|
def execute_insert(sql, opts={})
|
|
19
14
|
synchronize(opts[:server]) do |conn|
|
|
@@ -15,10 +15,6 @@ module Sequel
|
|
|
15
15
|
)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def dataset(opts = nil)
|
|
19
|
-
OpenBase::Dataset.new(self, opts)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
def execute(sql, opts={})
|
|
23
19
|
synchronize(opts[:server]) do |conn|
|
|
24
20
|
r = log_yield(sql){conn.execute(sql)}
|
|
@@ -37,6 +33,8 @@ module Sequel
|
|
|
37
33
|
|
|
38
34
|
class Dataset < Sequel::Dataset
|
|
39
35
|
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
|
|
36
|
+
|
|
37
|
+
Database::DatasetClass = self
|
|
40
38
|
|
|
41
39
|
def fetch_rows(sql)
|
|
42
40
|
execute(sql) do |result|
|
|
@@ -13,6 +13,18 @@ module Sequel
|
|
|
13
13
|
# ORA-03114: not connected to ORACLE
|
|
14
14
|
CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
|
|
15
15
|
|
|
16
|
+
ORACLE_TYPES = {:blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)}}
|
|
17
|
+
|
|
18
|
+
# Hash of conversion procs for this database.
|
|
19
|
+
attr_reader :conversion_procs
|
|
20
|
+
|
|
21
|
+
def initialize(opts={})
|
|
22
|
+
super
|
|
23
|
+
@autosequence = opts[:autosequence]
|
|
24
|
+
@primary_key_sequences = {}
|
|
25
|
+
@conversion_procs = ORACLE_TYPES.dup
|
|
26
|
+
end
|
|
27
|
+
|
|
16
28
|
def connect(server)
|
|
17
29
|
opts = server_opts(server)
|
|
18
30
|
if opts[:database]
|
|
@@ -22,6 +34,7 @@ module Sequel
|
|
|
22
34
|
dbname = opts[:host]
|
|
23
35
|
end
|
|
24
36
|
conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
|
|
37
|
+
conn.prefetch_rows = typecast_value_integer(opts[:prefetch_rows]) if opts[:prefetch_rows]
|
|
25
38
|
conn.autocommit = true
|
|
26
39
|
conn.non_blocking = true
|
|
27
40
|
|
|
@@ -36,59 +49,129 @@ module Sequel
|
|
|
36
49
|
conn.exec("ALTER SESSION SET TIME_ZONE='-00:00'")
|
|
37
50
|
end
|
|
38
51
|
|
|
52
|
+
class << conn
|
|
53
|
+
attr_reader :prepared_statements
|
|
54
|
+
end
|
|
55
|
+
conn.instance_variable_set(:@prepared_statements, {})
|
|
56
|
+
|
|
39
57
|
conn
|
|
40
58
|
end
|
|
41
|
-
|
|
42
|
-
def
|
|
43
|
-
|
|
59
|
+
|
|
60
|
+
def execute(sql, opts={}, &block)
|
|
61
|
+
_execute(nil, sql, opts, &block)
|
|
44
62
|
end
|
|
63
|
+
alias do execute
|
|
45
64
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
ds.identifier_output_method = :downcase
|
|
49
|
-
schema_and_table = "#{"#{quote_identifier(opts[:schema])}." if opts[:schema]}#{quote_identifier(table)}"
|
|
50
|
-
table_schema = []
|
|
51
|
-
metadata = transaction(opts){|conn| conn.describe_table(schema_and_table)}
|
|
52
|
-
metadata.columns.each do |column|
|
|
53
|
-
table_schema << [
|
|
54
|
-
column.name.downcase.to_sym,
|
|
55
|
-
{
|
|
56
|
-
:type => column.data_type,
|
|
57
|
-
:db_type => column.type_string.split(' ')[0],
|
|
58
|
-
:type_string => column.type_string,
|
|
59
|
-
:charset_form => column.charset_form,
|
|
60
|
-
:char_used => column.char_used?,
|
|
61
|
-
:char_size => column.char_size,
|
|
62
|
-
:data_size => column.data_size,
|
|
63
|
-
:precision => column.precision,
|
|
64
|
-
:scale => column.scale,
|
|
65
|
-
:fsprecision => column.fsprecision,
|
|
66
|
-
:lfprecision => column.lfprecision,
|
|
67
|
-
:allow_null => column.nullable?
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
end
|
|
71
|
-
table_schema
|
|
65
|
+
def execute_insert(sql, opts={})
|
|
66
|
+
_execute(:insert, sql, opts)
|
|
72
67
|
end
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def _execute(type, sql, opts={}, &block)
|
|
75
72
|
synchronize(opts[:server]) do |conn|
|
|
76
73
|
begin
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
|
75
|
+
if args = opts[:arguments]
|
|
76
|
+
r = conn.parse(sql)
|
|
77
|
+
args = cursor_bind_params(conn, r, args)
|
|
78
|
+
nr = log_yield(sql, args){r.exec}
|
|
79
|
+
r = nr unless block_given?
|
|
80
|
+
else
|
|
81
|
+
r = log_yield(sql){conn.exec(sql)}
|
|
82
|
+
end
|
|
83
|
+
if block_given?
|
|
84
|
+
begin
|
|
85
|
+
yield(r)
|
|
86
|
+
ensure
|
|
87
|
+
r.close
|
|
88
|
+
end
|
|
89
|
+
elsif type == :insert
|
|
90
|
+
last_insert_id(conn, opts)
|
|
91
|
+
else
|
|
92
|
+
r
|
|
93
|
+
end
|
|
94
|
+
rescue OCIException, RuntimeError => e
|
|
95
|
+
# ruby-oci8 is naughty and raises strings in some places
|
|
81
96
|
raise_error(e)
|
|
82
97
|
end
|
|
83
98
|
end
|
|
84
99
|
end
|
|
85
|
-
alias_method :do, :execute
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
PS_TYPES = {'string'.freeze=>String, 'integer'.freeze=>Integer, 'float'.freeze=>Float,
|
|
102
|
+
'decimal'.freeze=>Float, 'date'.freeze=>Time, 'datetime'.freeze=>Time,
|
|
103
|
+
'time'.freeze=>Time, 'boolean'.freeze=>String, 'blob'.freeze=>OCI8::BLOB}
|
|
104
|
+
def cursor_bind_params(conn, cursor, args)
|
|
105
|
+
cursor
|
|
106
|
+
i = 0
|
|
107
|
+
args.map do |arg, type|
|
|
108
|
+
i += 1
|
|
109
|
+
case arg
|
|
110
|
+
when true
|
|
111
|
+
arg = 'Y'
|
|
112
|
+
when false
|
|
113
|
+
arg = 'N'
|
|
114
|
+
when BigDecimal
|
|
115
|
+
arg = arg.to_f
|
|
116
|
+
when ::Sequel::SQL::Blob
|
|
117
|
+
raise Error, "Sequel's oracle adapter does not currently support using a blob in a bound variable"
|
|
118
|
+
end
|
|
119
|
+
if t = PS_TYPES[type]
|
|
120
|
+
cursor.bind_param(i, arg, t)
|
|
121
|
+
else
|
|
122
|
+
cursor.bind_param(i, arg, arg.class)
|
|
123
|
+
end
|
|
124
|
+
arg
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def execute_prepared_statement(conn, type, name, opts)
|
|
129
|
+
ps = prepared_statements[name]
|
|
130
|
+
sql = ps.prepared_sql
|
|
131
|
+
if cursora = conn.prepared_statements[name]
|
|
132
|
+
cursor, cursor_sql = cursora
|
|
133
|
+
if cursor_sql != sql
|
|
134
|
+
cursor.close
|
|
135
|
+
cursor = nil
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
unless cursor
|
|
139
|
+
cursor = log_yield("Preparing #{name}: #{sql}"){conn.parse(sql)}
|
|
140
|
+
conn.prepared_statements[name] = [cursor, sql]
|
|
141
|
+
end
|
|
142
|
+
args = cursor_bind_params(conn, cursor, opts[:arguments])
|
|
143
|
+
r = log_yield("Executing #{name}", args){cursor.exec}
|
|
144
|
+
if block_given?
|
|
145
|
+
yield(cursor)
|
|
146
|
+
elsif type == :insert
|
|
147
|
+
last_insert_id(conn, opts)
|
|
148
|
+
else
|
|
149
|
+
r
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def last_insert_id(conn, opts)
|
|
154
|
+
unless sequence = opts[:sequence]
|
|
155
|
+
if t = opts[:table]
|
|
156
|
+
sequence = sequence_for_table(t)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
if sequence
|
|
160
|
+
sql = "SELECT #{literal(sequence)}.currval FROM dual"
|
|
161
|
+
begin
|
|
162
|
+
cursor = log_yield(sql){conn.exec(sql)}
|
|
163
|
+
row = cursor.fetch
|
|
164
|
+
row.each{|v| return (v.to_i if v)}
|
|
165
|
+
rescue OCIError
|
|
166
|
+
nil
|
|
167
|
+
ensure
|
|
168
|
+
cursor.close if cursor
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
89
173
|
def begin_transaction(conn, opts={})
|
|
90
174
|
log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
|
|
91
|
-
conn
|
|
92
175
|
end
|
|
93
176
|
|
|
94
177
|
def commit_transaction(conn, opts={})
|
|
@@ -102,44 +185,239 @@ module Sequel
|
|
|
102
185
|
end
|
|
103
186
|
|
|
104
187
|
def disconnect_error?(e, opts)
|
|
105
|
-
super || (e.is_a?(::
|
|
188
|
+
super || (e.is_a?(::OCIError) && CONNECTION_ERROR_CODES.include?(e.code))
|
|
106
189
|
end
|
|
107
190
|
|
|
108
|
-
def
|
|
109
|
-
|
|
191
|
+
def oracle_column_type(h)
|
|
192
|
+
case h[:oci8_type]
|
|
193
|
+
when :number
|
|
194
|
+
case h[:scale]
|
|
195
|
+
when 0
|
|
196
|
+
:integer
|
|
197
|
+
when -127
|
|
198
|
+
:float
|
|
199
|
+
else
|
|
200
|
+
:decimal
|
|
201
|
+
end
|
|
202
|
+
when :date
|
|
203
|
+
:datetime
|
|
204
|
+
else
|
|
205
|
+
schema_column_type(h[:db_type])
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def remove_transaction(conn, committed)
|
|
210
|
+
conn.autocommit = true
|
|
211
|
+
ensure
|
|
110
212
|
super
|
|
111
213
|
end
|
|
112
214
|
|
|
113
215
|
def rollback_transaction(conn, opts={})
|
|
114
216
|
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
115
217
|
end
|
|
218
|
+
|
|
219
|
+
def schema_parse_table(table, opts={})
|
|
220
|
+
schema, table = schema_and_table(table)
|
|
221
|
+
schema ||= opts[:schema]
|
|
222
|
+
schema_and_table = if ds = opts[:dataset]
|
|
223
|
+
ds.literal(schema ? SQL::QualifiedIdentifier.new(schema, table) : SQL::Identifier.new(table))
|
|
224
|
+
else
|
|
225
|
+
"#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
|
|
226
|
+
end
|
|
227
|
+
table_schema = []
|
|
228
|
+
m = output_identifier_meth(ds)
|
|
229
|
+
im = input_identifier_meth(ds)
|
|
230
|
+
|
|
231
|
+
# Primary Keys
|
|
232
|
+
ds = metadata_dataset.from(:all_constraints___cons, :all_cons_columns___cols).
|
|
233
|
+
where(:cols__table_name=>im.call(table), :cons__constraint_type=>'P',
|
|
234
|
+
:cons__constraint_name=>:cols__constraint_name, :cons__owner=>:cols__owner)
|
|
235
|
+
ds = ds.where(:cons__owner=>im.call(schema)) if schema
|
|
236
|
+
pks = ds.select_map(:cols__column_name)
|
|
237
|
+
|
|
238
|
+
# Default values
|
|
239
|
+
defaults = metadata_dataset.from(:dba_tab_cols).
|
|
240
|
+
where(:table_name=>im.call(table)).
|
|
241
|
+
to_hash(:column_name, :data_default)
|
|
242
|
+
|
|
243
|
+
metadata = synchronize(opts[:server]) do |conn|
|
|
244
|
+
begin
|
|
245
|
+
log_yield("Connection.describe_table"){conn.describe_table(schema_and_table)}
|
|
246
|
+
rescue OCIError => e
|
|
247
|
+
raise_error(e)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
metadata.columns.each do |column|
|
|
251
|
+
h = {
|
|
252
|
+
:primary_key => pks.include?(column.name),
|
|
253
|
+
:default => defaults[column.name],
|
|
254
|
+
:oci8_type => column.data_type,
|
|
255
|
+
:db_type => column.type_string.split(' ')[0],
|
|
256
|
+
:type_string => column.type_string,
|
|
257
|
+
:charset_form => column.charset_form,
|
|
258
|
+
:char_used => column.char_used?,
|
|
259
|
+
:char_size => column.char_size,
|
|
260
|
+
:data_size => column.data_size,
|
|
261
|
+
:precision => column.precision,
|
|
262
|
+
:scale => column.scale,
|
|
263
|
+
:fsprecision => column.fsprecision,
|
|
264
|
+
:lfprecision => column.lfprecision,
|
|
265
|
+
:allow_null => column.nullable?
|
|
266
|
+
}
|
|
267
|
+
h[:type] = oracle_column_type(h)
|
|
268
|
+
table_schema << [m.call(column.name), h]
|
|
269
|
+
end
|
|
270
|
+
table_schema
|
|
271
|
+
end
|
|
116
272
|
end
|
|
117
273
|
|
|
118
274
|
class Dataset < Sequel::Dataset
|
|
119
275
|
include DatasetMethods
|
|
120
276
|
|
|
277
|
+
Database::DatasetClass = self
|
|
278
|
+
|
|
279
|
+
PREPARED_ARG_PLACEHOLDER = ':'.freeze
|
|
280
|
+
|
|
281
|
+
# Oracle already supports named bind arguments, so use directly.
|
|
282
|
+
module ArgumentMapper
|
|
283
|
+
include Sequel::Dataset::ArgumentMapper
|
|
284
|
+
|
|
285
|
+
protected
|
|
286
|
+
|
|
287
|
+
# Return a hash with the same values as the given hash,
|
|
288
|
+
# but with the keys converted to strings.
|
|
289
|
+
def map_to_prepared_args(bind_vars)
|
|
290
|
+
prepared_args.map{|v, t| [bind_vars[v], t]}
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
private
|
|
294
|
+
|
|
295
|
+
# Oracle uses a : before the name of the argument for named
|
|
296
|
+
# arguments.
|
|
297
|
+
def prepared_arg(k)
|
|
298
|
+
y, type = k.to_s.split("__", 2)
|
|
299
|
+
prepared_args << [y.to_sym, type]
|
|
300
|
+
i = prepared_args.length
|
|
301
|
+
LiteralString.new(":#{i}")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Always assume a prepared argument.
|
|
305
|
+
def prepared_arg?(k)
|
|
306
|
+
true
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Oracle prepared statement uses a new prepared statement each time
|
|
311
|
+
# it is called, but it does use the bind arguments.
|
|
312
|
+
module BindArgumentMethods
|
|
313
|
+
include ArgumentMapper
|
|
314
|
+
|
|
315
|
+
private
|
|
316
|
+
|
|
317
|
+
# Run execute_select on the database with the given SQL and the stored
|
|
318
|
+
# bind arguments.
|
|
319
|
+
def execute(sql, opts={}, &block)
|
|
320
|
+
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
324
|
+
def execute_dui(sql, opts={}, &block)
|
|
325
|
+
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
329
|
+
def execute_insert(sql, opts={}, &block)
|
|
330
|
+
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
module PreparedStatementMethods
|
|
335
|
+
include BindArgumentMethods
|
|
336
|
+
|
|
337
|
+
private
|
|
338
|
+
|
|
339
|
+
# Execute the stored prepared statement name and the stored bind
|
|
340
|
+
# arguments instead of the SQL given.
|
|
341
|
+
def execute(sql, opts={}, &block)
|
|
342
|
+
super(prepared_statement_name, opts, &block)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
346
|
+
def execute_dui(sql, opts={}, &block)
|
|
347
|
+
super(prepared_statement_name, opts, &block)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
351
|
+
def execute_insert(sql, opts={}, &block)
|
|
352
|
+
super(prepared_statement_name, opts, &block)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Execute the given type of statement with the hash of values.
|
|
357
|
+
def call(type, bind_vars={}, *values, &block)
|
|
358
|
+
ps = to_prepared_statement(type, values)
|
|
359
|
+
ps.extend(BindArgumentMethods)
|
|
360
|
+
ps.call(bind_vars, &block)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Prepare the given type of query with the given name and store
|
|
364
|
+
# it in the database. Note that a new native prepared statement is
|
|
365
|
+
# created on each call to this prepared statement.
|
|
366
|
+
def prepare(type, name=nil, *values)
|
|
367
|
+
ps = to_prepared_statement(type, values)
|
|
368
|
+
ps.extend(PreparedStatementMethods)
|
|
369
|
+
if name
|
|
370
|
+
ps.prepared_statement_name = name
|
|
371
|
+
db.prepared_statements[name] = ps
|
|
372
|
+
end
|
|
373
|
+
ps
|
|
374
|
+
end
|
|
375
|
+
|
|
121
376
|
def fetch_rows(sql)
|
|
122
377
|
execute(sql) do |cursor|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
378
|
+
offset = @opts[:offset]
|
|
379
|
+
rn = row_number_column
|
|
380
|
+
cps = db.conversion_procs
|
|
381
|
+
cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
|
|
382
|
+
metadata = cursor.column_metadata
|
|
383
|
+
cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
|
|
384
|
+
columns = cols.reject{|x| x == rn} if offset
|
|
385
|
+
@columns = columns
|
|
386
|
+
while r = cursor.fetch
|
|
387
|
+
row = {}
|
|
388
|
+
r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
|
|
389
|
+
row.delete(rn) if offset
|
|
390
|
+
yield row
|
|
132
391
|
end
|
|
133
392
|
end
|
|
134
393
|
self
|
|
135
394
|
end
|
|
136
395
|
|
|
396
|
+
# Create a named prepared statement that is stored in the
|
|
397
|
+
# database (and connection) for reuse.
|
|
398
|
+
def prepare(type, name=nil, *values)
|
|
399
|
+
ps = to_prepared_statement(type, values)
|
|
400
|
+
ps.extend(PreparedStatementMethods)
|
|
401
|
+
if name
|
|
402
|
+
ps.prepared_statement_name = name
|
|
403
|
+
db.prepared_statements[name] = ps
|
|
404
|
+
end
|
|
405
|
+
ps
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Oracle requires type specifiers for placeholders, at least
|
|
409
|
+
# if you ever want to use a nil/NULL value as the value for
|
|
410
|
+
# the placeholder.
|
|
411
|
+
def requires_placeholder_type_specifiers?
|
|
412
|
+
true
|
|
413
|
+
end
|
|
414
|
+
|
|
137
415
|
private
|
|
138
416
|
|
|
139
417
|
def literal_other(v)
|
|
140
418
|
case v
|
|
141
419
|
when OraDate
|
|
142
|
-
literal(
|
|
420
|
+
literal(db.to_application_timestamp(v))
|
|
143
421
|
when OCI8::CLOB
|
|
144
422
|
v.rewind
|
|
145
423
|
literal(v.read)
|
|
@@ -147,6 +425,10 @@ module Sequel
|
|
|
147
425
|
super
|
|
148
426
|
end
|
|
149
427
|
end
|
|
428
|
+
|
|
429
|
+
def prepared_arg_placeholder
|
|
430
|
+
PREPARED_ARG_PLACEHOLDER
|
|
431
|
+
end
|
|
150
432
|
end
|
|
151
433
|
end
|
|
152
434
|
end
|