sequel 4.9.0 → 4.10.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.
- checksums.yaml +4 -4
- data/CHANGELOG +79 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +2 -12
- data/bin/sequel +1 -0
- data/doc/advanced_associations.rdoc +82 -25
- data/doc/association_basics.rdoc +21 -22
- data/doc/core_extensions.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/4.10.0.txt +226 -0
- data/doc/security.rdoc +1 -0
- data/doc/testing.rdoc +7 -7
- data/doc/transactions.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +160 -168
- data/lib/sequel/adapters/jdbc/db2.rb +17 -18
- data/lib/sequel/adapters/jdbc/derby.rb +5 -28
- data/lib/sequel/adapters/jdbc/h2.rb +11 -22
- data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
- data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
- data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
- data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
- data/lib/sequel/adapters/openbase.rb +1 -7
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +3 -6
- data/lib/sequel/adapters/shared/cubrid.rb +24 -9
- data/lib/sequel/adapters/shared/db2.rb +13 -5
- data/lib/sequel/adapters/shared/firebird.rb +16 -16
- data/lib/sequel/adapters/shared/informix.rb +2 -5
- data/lib/sequel/adapters/shared/mssql.rb +72 -63
- data/lib/sequel/adapters/shared/mysql.rb +72 -40
- data/lib/sequel/adapters/shared/oracle.rb +27 -15
- data/lib/sequel/adapters/shared/postgres.rb +24 -44
- data/lib/sequel/adapters/shared/progress.rb +1 -5
- data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
- data/lib/sequel/adapters/shared/sqlite.rb +21 -6
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
- data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
- data/lib/sequel/core.rb +14 -9
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +12 -0
- data/lib/sequel/database/query.rb +4 -1
- data/lib/sequel/database/schema_methods.rb +3 -2
- data/lib/sequel/database/transactions.rb +47 -17
- data/lib/sequel/dataset/features.rb +12 -2
- data/lib/sequel/dataset/mutation.rb +2 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
- data/lib/sequel/dataset/prepared_statements.rb +6 -0
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +132 -70
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/null_dataset.rb +8 -4
- data/lib/sequel/extensions/pg_array.rb +4 -4
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/model/associations.rb +468 -188
- data/lib/sequel/model/base.rb +88 -13
- data/lib/sequel/plugins/association_pks.rb +23 -64
- data/lib/sequel/plugins/auto_validations.rb +3 -2
- data/lib/sequel/plugins/dataset_associations.rb +1 -3
- data/lib/sequel/plugins/many_through_many.rb +18 -65
- data/lib/sequel/plugins/pg_array_associations.rb +97 -86
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
- data/lib/sequel/plugins/rcte_tree.rb +12 -16
- data/lib/sequel/plugins/sharding.rb +21 -3
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
- data/lib/sequel/plugins/subclasses.rb +1 -9
- data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
- data/lib/sequel/plugins/tree.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +57 -15
- data/spec/adapters/mysql_spec.rb +11 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +38 -4
- data/spec/core/dataset_spec.rb +45 -7
- data/spec/core/placeholder_literalizer_spec.rb +17 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/active_model_spec.rb +18 -9
- data/spec/extensions/association_pks_spec.rb +20 -18
- data/spec/extensions/association_proxies_spec.rb +9 -9
- data/spec/extensions/auto_validations_spec.rb +6 -0
- data/spec/extensions/columns_introspection_spec.rb +1 -0
- data/spec/extensions/constraint_validations_spec.rb +3 -1
- data/spec/extensions/many_through_many_spec.rb +191 -111
- data/spec/extensions/pg_array_associations_spec.rb +133 -103
- data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
- data/spec/extensions/rcte_tree_spec.rb +35 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
- data/spec/extensions/sharding_spec.rb +2 -2
- data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
- data/spec/extensions/to_dot_spec.rb +1 -0
- data/spec/extensions/touch_spec.rb +2 -2
- data/spec/integration/associations_test.rb +130 -37
- data/spec/integration/dataset_test.rb +17 -0
- data/spec/integration/model_test.rb +17 -0
- data/spec/integration/schema_test.rb +14 -0
- data/spec/integration/transaction_test.rb +25 -1
- data/spec/model/association_reflection_spec.rb +63 -24
- data/spec/model/associations_spec.rb +104 -57
- data/spec/model/base_spec.rb +14 -1
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +221 -74
- data/spec/model/model_spec.rb +119 -1
- metadata +4 -2
data/doc/security.rdoc
CHANGED
@@ -18,6 +18,7 @@ could conceivably be abused to do so:
|
|
18
18
|
|
19
19
|
* Sequel::Schema::CreateTableGenerator.add_type_method
|
20
20
|
* Sequel::Dataset.def_mutation_method
|
21
|
+
* Sequel::Dataset.def_sql_method
|
21
22
|
* Sequel::Model::Plugins.def_dataset_methods
|
22
23
|
* Sequel.def_adapter_method (private)
|
23
24
|
* Sequel::SQL::Expression.to_s_method (private)
|
data/doc/testing.rdoc
CHANGED
@@ -11,7 +11,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
11
11
|
class Spec::Example::ExampleGroup
|
12
12
|
def execute(*args, &block)
|
13
13
|
result = nil
|
14
|
-
Sequel::Model.db.transaction(:rollback=>:always){result = super(*args, &block)}
|
14
|
+
Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super(*args, &block)}
|
15
15
|
result
|
16
16
|
end
|
17
17
|
end
|
@@ -24,7 +24,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
24
24
|
def self.inherited(subclass)
|
25
25
|
super
|
26
26
|
subclass.around do |example|
|
27
|
-
Sequel::Model.db.transaction(:rollback=>:always){example.call}
|
27
|
+
Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){example.call}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -34,7 +34,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
34
34
|
# Global around filters should work
|
35
35
|
RSpec.configure do |c|
|
36
36
|
c.around(:each) do |example|
|
37
|
-
DB.transaction(:rollback=>:always){example.run}
|
37
|
+
DB.transaction(:rollback=>:always, :auto_savepoint=>true){example.run}
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -44,7 +44,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
44
44
|
class SequelTestCase < Test::Unit::TestCase
|
45
45
|
def run(*args, &block)
|
46
46
|
result = nil
|
47
|
-
Sequel::Model.db.transaction(:rollback=>:always){result = super}
|
47
|
+
Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super}
|
48
48
|
result
|
49
49
|
end
|
50
50
|
end
|
@@ -55,7 +55,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
55
55
|
|
56
56
|
def run(*args, &block)
|
57
57
|
result = nil
|
58
|
-
Sequel::Model.db.transaction(:rollback => :always) do
|
58
|
+
Sequel::Model.db.transaction(:rollback => :always, :auto_savepoint=>true) do
|
59
59
|
result = _original_run(*args, &block)
|
60
60
|
end
|
61
61
|
result
|
@@ -69,7 +69,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
69
69
|
class SequelTestCase < MiniTest::Unit::TestCase
|
70
70
|
def run(*args, &block)
|
71
71
|
result = nil
|
72
|
-
Sequel::Model.db.transaction(:rollback=>:always){result = super}
|
72
|
+
Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super}
|
73
73
|
result
|
74
74
|
end
|
75
75
|
end
|
@@ -80,7 +80,7 @@ These run each test in its own transaction, the recommended way to test.
|
|
80
80
|
|
81
81
|
def run(*args, &block)
|
82
82
|
result = nil
|
83
|
-
Sequel::Model.db.transaction(:rollback => :always) do
|
83
|
+
Sequel::Model.db.transaction(:rollback => :always, :auto_savepoint=>true) do
|
84
84
|
result = _original_run(*args, &block)
|
85
85
|
end
|
86
86
|
result
|
data/doc/transactions.rdoc
CHANGED
@@ -91,6 +91,14 @@ You can use the <tt>:savepoint => true</tt> option in the inner transaction to e
|
|
91
91
|
end # RELEASE SAVEPOINT
|
92
92
|
end # COMMIT
|
93
93
|
|
94
|
+
You can use the <tt>:auto_savepoint => true</tt> option in the outer transaction to explicitly use a savepoint in the inner transaction (if the database supports it):
|
95
|
+
|
96
|
+
DB.transaction(:auto_savepoint => true) do # BEGIN
|
97
|
+
DB.transaction do # SAVEPOINT
|
98
|
+
DB[:foo].insert(1) # INSERT
|
99
|
+
end # RELEASE SAVEPOINT
|
100
|
+
end # COMMIT
|
101
|
+
|
94
102
|
If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
|
95
103
|
|
96
104
|
DB.transaction do # BEGIN
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -180,6 +180,86 @@ module Sequel
|
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
183
|
+
class TypeConvertor
|
184
|
+
%w'Boolean Float Double Int Long Short'.each do |meth|
|
185
|
+
class_eval("def #{meth}(r, i) v = r.get#{meth}(i); v unless r.wasNull end", __FILE__, __LINE__)
|
186
|
+
end
|
187
|
+
%w'Object Array String Time Date Timestamp BigDecimal Blob Bytes Clob'.each do |meth|
|
188
|
+
class_eval("def #{meth}(r, i) r.get#{meth}(i) end", __FILE__, __LINE__)
|
189
|
+
end
|
190
|
+
def RubyTime(r, i)
|
191
|
+
if v = r.getTime(i)
|
192
|
+
Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
def RubyDate(r, i)
|
196
|
+
if v = r.getDate(i)
|
197
|
+
Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
def RubyTimestamp(r, i)
|
201
|
+
if v = r.getTimestamp(i)
|
202
|
+
Sequel.database_to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
def RubyBigDecimal(r, i)
|
206
|
+
if v = r.getBigDecimal(i)
|
207
|
+
BigDecimal.new(v.to_string)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
def RubyBlob(r, i)
|
211
|
+
if v = r.getBytes(i)
|
212
|
+
Sequel::SQL::Blob.new(String.from_java_bytes(v))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
def RubyClob(r, i)
|
216
|
+
if v = r.getClob(i)
|
217
|
+
v.getSubString(1, v.length)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
INSTANCE = new
|
222
|
+
o = INSTANCE
|
223
|
+
MAP = Hash.new(o.method(:Object))
|
224
|
+
types = Java::JavaSQL::Types
|
225
|
+
|
226
|
+
{
|
227
|
+
:ARRAY => :Array,
|
228
|
+
:BOOLEAN => :Boolean,
|
229
|
+
:CHAR => :String,
|
230
|
+
:DOUBLE => :Double,
|
231
|
+
:FLOAT => :Double,
|
232
|
+
:INTEGER => :Int,
|
233
|
+
:LONGNVARCHAR => :String,
|
234
|
+
:LONGVARCHAR => :String,
|
235
|
+
:NCHAR => :String,
|
236
|
+
:REAL => :Float,
|
237
|
+
:SMALLINT => :Short,
|
238
|
+
:TINYINT => :Short,
|
239
|
+
:VARCHAR => :String,
|
240
|
+
}.each do |type, meth|
|
241
|
+
MAP[types.const_get(type)] = o.method(meth)
|
242
|
+
end
|
243
|
+
BASIC_MAP = MAP.dup
|
244
|
+
|
245
|
+
{
|
246
|
+
:BINARY => :Blob,
|
247
|
+
:BLOB => :Blob,
|
248
|
+
:CLOB => :Clob,
|
249
|
+
:DATE => :Date,
|
250
|
+
:DECIMAL => :BigDecimal,
|
251
|
+
:LONGVARBINARY => :Blob,
|
252
|
+
:NCLOB => :Clob,
|
253
|
+
:NUMERIC => :BigDecimal,
|
254
|
+
:TIME => :Time,
|
255
|
+
:TIMESTAMP => :Timestamp,
|
256
|
+
:VARBINARY => :Blob,
|
257
|
+
}.each do |type, meth|
|
258
|
+
BASIC_MAP[types.const_get(type)] = o.method(meth)
|
259
|
+
MAP[types.const_get(type)] = o.method(:"Ruby#{meth}")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
183
263
|
# JDBC Databases offer a fairly uniform interface that does not change
|
184
264
|
# much based on the sub adapter.
|
185
265
|
class Database < Sequel::Database
|
@@ -196,6 +276,16 @@ module Sequel
|
|
196
276
|
# fetching rows.
|
197
277
|
attr_accessor :convert_types
|
198
278
|
|
279
|
+
# The fetch size to use for JDBC Statement objects created by this database.
|
280
|
+
# By default, this is nil so a fetch size is not set explicitly.
|
281
|
+
attr_accessor :fetch_size
|
282
|
+
|
283
|
+
# Map of JDBC type ids to callable objects that return appropriate ruby values.
|
284
|
+
attr_reader :type_convertor_map
|
285
|
+
|
286
|
+
# Map of JDBC type ids to callable objects that return appropriate ruby or java values.
|
287
|
+
attr_reader :basic_type_convertor_map
|
288
|
+
|
199
289
|
# Execute the given stored procedure with the give name. If a block is
|
200
290
|
# given, the stored procedure should return rows.
|
201
291
|
def call_sproc(name, opts = OPTS)
|
@@ -277,6 +367,9 @@ module Sequel
|
|
277
367
|
synchronize(opts[:server]) do |conn|
|
278
368
|
statement(conn) do |stmt|
|
279
369
|
if block
|
370
|
+
if size = fetch_size
|
371
|
+
stmt.setFetchSize(size)
|
372
|
+
end
|
280
373
|
yield log_yield(sql){stmt.executeQuery(sql)}
|
281
374
|
else
|
282
375
|
case opts[:type]
|
@@ -374,6 +467,7 @@ module Sequel
|
|
374
467
|
def adapter_initialize
|
375
468
|
@connection_prepared_statements = {}
|
376
469
|
@connection_prepared_statements_mutex = Mutex.new
|
470
|
+
@fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
|
377
471
|
@convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
|
378
472
|
raise(Error, "No connection string specified") unless uri
|
379
473
|
|
@@ -384,6 +478,8 @@ module Sequel
|
|
384
478
|
else
|
385
479
|
@opts[:driver]
|
386
480
|
end
|
481
|
+
|
482
|
+
setup_type_convertor_map
|
387
483
|
end
|
388
484
|
|
389
485
|
# Yield the native prepared statements hash for the given connection
|
@@ -439,6 +535,9 @@ module Sequel
|
|
439
535
|
else
|
440
536
|
log_yield("CLOSE #{name}"){cps[1].close} if cps
|
441
537
|
cps = log_yield("PREPARE#{" #{name}:" if name} #{sql}"){prepare_jdbc_statement(conn, sql, opts)}
|
538
|
+
if size = fetch_size
|
539
|
+
cps.setFetchSize(size)
|
540
|
+
end
|
442
541
|
cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
|
443
542
|
end
|
444
543
|
i = 0
|
@@ -480,6 +579,12 @@ module Sequel
|
|
480
579
|
def execute_statement_insert(stmt, sql)
|
481
580
|
stmt.executeUpdate(sql)
|
482
581
|
end
|
582
|
+
|
583
|
+
# The default fetch size to use for statements. Nil by default, so that the
|
584
|
+
# default for the JDBC driver is used.
|
585
|
+
def default_fetch_size
|
586
|
+
nil
|
587
|
+
end
|
483
588
|
|
484
589
|
# Gets the connection from JNDI.
|
485
590
|
def get_connection_from_jndi
|
@@ -642,6 +747,11 @@ module Sequel
|
|
642
747
|
h[:table_schem] == 'INFORMATION_SCHEMA'
|
643
748
|
end
|
644
749
|
|
750
|
+
def setup_type_convertor_map
|
751
|
+
@type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>timestamp_convertor)
|
752
|
+
@basic_type_convertor_map = TypeConvertor::BASIC_MAP
|
753
|
+
end
|
754
|
+
|
645
755
|
# Yield a new statement object, and ensure that it is closed before returning.
|
646
756
|
def statement(conn)
|
647
757
|
stmt = conn.createStatement
|
@@ -651,6 +761,16 @@ module Sequel
|
|
651
761
|
ensure
|
652
762
|
stmt.close if stmt
|
653
763
|
end
|
764
|
+
|
765
|
+
# A conversion proc for timestamp columns. This is used to make sure timestamps are converted using the
|
766
|
+
# correct timezone.
|
767
|
+
def timestamp_convertor
|
768
|
+
lambda do |r, i|
|
769
|
+
if v = r.getTimestamp(i)
|
770
|
+
to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
654
774
|
end
|
655
775
|
|
656
776
|
class Dataset < Sequel::Dataset
|
@@ -729,195 +849,67 @@ module Sequel
|
|
729
849
|
end
|
730
850
|
ps
|
731
851
|
end
|
732
|
-
|
733
|
-
private
|
734
852
|
|
735
|
-
#
|
736
|
-
|
737
|
-
|
738
|
-
JAVA_SQL_DATE = Java::JavaSQL::Date
|
739
|
-
JAVA_SQL_BLOB = Java::JavaSQL::Blob
|
740
|
-
JAVA_SQL_CLOB = Java::JavaSQL::Clob
|
741
|
-
JAVA_BUFFERED_READER = Java::JavaIo::BufferedReader
|
742
|
-
JAVA_BIG_DECIMAL = Java::JavaMath::BigDecimal
|
743
|
-
JAVA_BYTE_ARRAY = Java::byte[]
|
744
|
-
JAVA_UUID = Java::JavaUtil::UUID
|
745
|
-
JAVA_HASH_MAP = Java::JavaUtil::HashMap
|
746
|
-
|
747
|
-
# Handle type conversions for common Java types.
|
748
|
-
class TYPE_TRANSLATOR
|
749
|
-
LF = "\n".freeze
|
750
|
-
def time(v) Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}") end
|
751
|
-
def date(v) Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate) end
|
752
|
-
def decimal(v) BigDecimal.new(v.to_string) end
|
753
|
-
def byte_array(v) Sequel::SQL::Blob.new(String.from_java_bytes(v)) end
|
754
|
-
def blob(v) Sequel::SQL::Blob.new(String.from_java_bytes(v.getBytes(1, v.length))) end
|
755
|
-
def clob(v) v.getSubString(1, v.length) end
|
756
|
-
def buffered_reader(v)
|
757
|
-
lines = ""
|
758
|
-
c = false
|
759
|
-
while(line = v.read_line) do
|
760
|
-
lines << LF if c
|
761
|
-
lines << line
|
762
|
-
c ||= true
|
763
|
-
end
|
764
|
-
lines
|
765
|
-
end
|
766
|
-
def uuid(v) v.to_string end
|
767
|
-
def hash_map(v) v.to_hash end
|
768
|
-
end
|
769
|
-
TYPE_TRANSLATOR_INSTANCE = tt = TYPE_TRANSLATOR.new
|
770
|
-
|
771
|
-
# Cache type translator methods so that duplicate Method
|
772
|
-
# objects are not created.
|
773
|
-
DECIMAL_METHOD = tt.method(:decimal)
|
774
|
-
TIME_METHOD = tt.method(:time)
|
775
|
-
DATE_METHOD = tt.method(:date)
|
776
|
-
BUFFERED_READER_METHOD = tt.method(:buffered_reader)
|
777
|
-
BYTE_ARRAY_METHOD = tt.method(:byte_array)
|
778
|
-
BLOB_METHOD = tt.method(:blob)
|
779
|
-
CLOB_METHOD = tt.method(:clob)
|
780
|
-
UUID_METHOD = tt.method(:uuid)
|
781
|
-
HASH_MAP_METHOD = tt.method(:hash_map)
|
782
|
-
|
783
|
-
# Convert the given Java timestamp to an instance of Sequel.datetime_class.
|
784
|
-
def convert_type_timestamp(v)
|
785
|
-
db.to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
|
786
|
-
end
|
787
|
-
|
788
|
-
# Return a callable object that will convert any value of <tt>v</tt>'s
|
789
|
-
# class to a ruby object. If no callable object can handle <tt>v</tt>'s
|
790
|
-
# class, return false so that the negative lookup is cached.
|
791
|
-
def convert_type_proc(v, ctn=nil)
|
792
|
-
case v
|
793
|
-
when JAVA_BIG_DECIMAL
|
794
|
-
DECIMAL_METHOD
|
795
|
-
when JAVA_SQL_TIMESTAMP
|
796
|
-
method(:convert_type_timestamp)
|
797
|
-
when JAVA_SQL_TIME
|
798
|
-
TIME_METHOD
|
799
|
-
when JAVA_SQL_DATE
|
800
|
-
DATE_METHOD
|
801
|
-
when JAVA_BUFFERED_READER
|
802
|
-
BUFFERED_READER_METHOD
|
803
|
-
when JAVA_BYTE_ARRAY
|
804
|
-
BYTE_ARRAY_METHOD
|
805
|
-
when JAVA_SQL_BLOB
|
806
|
-
BLOB_METHOD
|
807
|
-
when JAVA_SQL_CLOB
|
808
|
-
CLOB_METHOD
|
809
|
-
when JAVA_UUID
|
810
|
-
UUID_METHOD
|
811
|
-
when JAVA_HASH_MAP
|
812
|
-
HASH_MAP_METHOD
|
813
|
-
else
|
814
|
-
false
|
815
|
-
end
|
853
|
+
# Set the fetch size on JDBC ResultSets created from this dataset.
|
854
|
+
def with_fetch_size(size)
|
855
|
+
clone(:fetch_size=>size)
|
816
856
|
end
|
857
|
+
|
858
|
+
private
|
817
859
|
|
818
|
-
#
|
819
|
-
def
|
820
|
-
|
860
|
+
# Whether we should convert Java types to ruby types for this dataset.
|
861
|
+
def convert_types?
|
862
|
+
ct = @convert_types
|
863
|
+
ct.nil? ? db.convert_types : ct
|
821
864
|
end
|
822
865
|
|
823
|
-
# By default, if using column info, assume the info needed is the column's type name.
|
824
|
-
def convert_type_proc_column_info(meta, i)
|
825
|
-
meta.column_type_name(i)
|
826
|
-
end
|
827
|
-
|
828
866
|
# Extend the dataset with the JDBC stored procedure methods.
|
829
867
|
def prepare_extend_sproc(ds)
|
830
868
|
ds.extend(StoredProcedureMethods)
|
831
869
|
end
|
832
|
-
|
870
|
+
|
871
|
+
# The type conversion proc to use for the given column number i,
|
872
|
+
# given the type conversion map and the ResultSetMetaData.
|
873
|
+
def type_convertor(map, meta, type, i)
|
874
|
+
map[type]
|
875
|
+
end
|
876
|
+
|
877
|
+
# The basic type conversion proc to use for the given column number i,
|
878
|
+
# given the type conversion map and the ResultSetMetaData.
|
879
|
+
#
|
880
|
+
# This is implemented as a separate method so that subclasses can
|
881
|
+
# override the methods separately.
|
882
|
+
def basic_type_convertor(map, meta, type, i)
|
883
|
+
map[type]
|
884
|
+
end
|
885
|
+
|
833
886
|
# Split out from fetch rows to allow processing of JDBC result sets
|
834
887
|
# that don't come from issuing an SQL string.
|
835
|
-
def process_result_set(result
|
836
|
-
# get column names
|
888
|
+
def process_result_set(result)
|
837
889
|
meta = result.getMetaData
|
890
|
+
if fetch_size = opts[:fetch_size]
|
891
|
+
result.setFetchSize(fetch_size)
|
892
|
+
end
|
838
893
|
cols = []
|
839
894
|
i = 0
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
if ct
|
844
|
-
use_column_info = convert_type_proc_uses_column_info?
|
845
|
-
# When converting types, four values are associated with every column:
|
846
|
-
# 1) column name symbol
|
847
|
-
# 2) index (starting at 1, as JDBC does)
|
848
|
-
# 3) database-specific value usable by convert_type_proc to determine the conversion proc to use for column
|
849
|
-
# 4) nil (later updated with the actual conversion proc during the lookup process)
|
850
|
-
meta.getColumnCount.times do
|
851
|
-
i += 1
|
852
|
-
cols << [output_identifier(meta.getColumnLabel(i)), i, (convert_type_proc_column_info(meta, i) if use_column_info), nil]
|
853
|
-
end
|
854
|
-
@columns = cols.map{|c| c.at(0)}
|
855
|
-
process_result_set_convert(cols, result, &block)
|
856
|
-
else
|
857
|
-
# When not converting types, only the type name and the index are used.
|
858
|
-
meta.getColumnCount.times do
|
859
|
-
i += 1
|
860
|
-
cols << [output_identifier(meta.getColumnLabel(i)), i]
|
861
|
-
end
|
862
|
-
@columns = cols.map{|c| c.at(0)}
|
863
|
-
process_result_set_no_convert(cols, result, &block)
|
864
|
-
end
|
865
|
-
ensure
|
866
|
-
result.close
|
867
|
-
end
|
895
|
+
convert = convert_types?
|
896
|
+
map = convert ? db.type_convertor_map : db.basic_type_convertor_map
|
868
897
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
# * check if the value is truthy (not false/nil)
|
873
|
-
# * if not truthy, return object
|
874
|
-
# * otherwise, see if a conversion method exists for
|
875
|
-
# the column. All columns start with a nil conversion proc,
|
876
|
-
# since unlike other adapters, Sequel doesn't get the type of
|
877
|
-
# the column when parsing the column metadata.
|
878
|
-
# * if a conversion proc is not false/nil, call it with the object
|
879
|
-
# and return the result.
|
880
|
-
# * if a conversion proc has already been looked up and doesn't
|
881
|
-
# exist (false value), return object.
|
882
|
-
# * if a conversion proc hasn't been looked up yet (nil value),
|
883
|
-
# call convert_type_proc to get the conversion method. Cache
|
884
|
-
# the result of as the column's conversion proc to speed up
|
885
|
-
# later processing. If the conversion proc exists, call it
|
886
|
-
# and return the result, otherwise, return the object.
|
887
|
-
def process_result_set_convert(cols, result)
|
888
|
-
while result.next
|
889
|
-
row = {}
|
890
|
-
cols.each do |n, i, ctn, p|
|
891
|
-
v = result.getObject(i)
|
892
|
-
row[n] = if v
|
893
|
-
if p
|
894
|
-
p.call(v)
|
895
|
-
elsif p.nil?
|
896
|
-
cols[i-1][3] = p = convert_type_proc(v, ctn)
|
897
|
-
if p
|
898
|
-
p.call(v)
|
899
|
-
else
|
900
|
-
v
|
901
|
-
end
|
902
|
-
else
|
903
|
-
v
|
904
|
-
end
|
905
|
-
else
|
906
|
-
v
|
907
|
-
end
|
908
|
-
end
|
909
|
-
yield row
|
898
|
+
meta.getColumnCount.times do
|
899
|
+
i += 1
|
900
|
+
cols << [output_identifier(meta.getColumnLabel(i)), i, convert ? type_convertor(map, meta, meta.getColumnType(i), i) : basic_type_convertor(map, meta, meta.getColumnType(i), i)]
|
910
901
|
end
|
911
|
-
|
902
|
+
@columns = cols.map{|c| c.at(0)}
|
912
903
|
|
913
|
-
# Yield rows without calling any conversion procs. This
|
914
|
-
# may yield Java values and not ruby values.
|
915
|
-
def process_result_set_no_convert(cols, result)
|
916
904
|
while result.next
|
917
905
|
row = {}
|
918
|
-
cols.each
|
906
|
+
cols.each do |n, i, pr|
|
907
|
+
row[n] = pr.call(result, i)
|
908
|
+
end
|
919
909
|
yield row
|
920
910
|
end
|
911
|
+
ensure
|
912
|
+
result.close
|
921
913
|
end
|
922
914
|
end
|
923
915
|
end
|