sequel 4.9.0 → 4.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -3,6 +3,16 @@ Sequel.require 'adapters/jdbc/transactions'
|
|
3
3
|
|
4
4
|
module Sequel
|
5
5
|
module JDBC
|
6
|
+
class TypeConvertor
|
7
|
+
def DB2Clob(r, i)
|
8
|
+
if v = r.getClob(i)
|
9
|
+
v = v.getSubString(1, v.length)
|
10
|
+
v = Sequel::SQL::Blob.new(v) if ::Sequel::DB2::use_clob_as_blob
|
11
|
+
v
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
class Database
|
7
17
|
# Alias the generic JDBC versions so they can be called directly later
|
8
18
|
alias jdbc_schema_parse_table schema_parse_table
|
@@ -55,28 +65,17 @@ module Sequel
|
|
55
65
|
def primary_key_index_re
|
56
66
|
PRIMARY_KEY_INDEX_RE
|
57
67
|
end
|
68
|
+
|
69
|
+
def setup_type_convertor_map
|
70
|
+
super
|
71
|
+
map = @type_convertor_map
|
72
|
+
types = Java::JavaSQL::Types
|
73
|
+
map[types::NCLOB] = map[types::CLOB] = TypeConvertor::INSTANCE.method(:DB2Clob)
|
74
|
+
end
|
58
75
|
end
|
59
76
|
|
60
77
|
class Dataset < JDBC::Dataset
|
61
78
|
include Sequel::DB2::DatasetMethods
|
62
|
-
|
63
|
-
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
|
64
|
-
def db2_clob(v) Sequel::SQL::Blob.new(v.getSubString(1, v.length)) end
|
65
|
-
end
|
66
|
-
|
67
|
-
DB2_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:db2_clob)
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
# Return clob as blob if use_clob_as_blob is true
|
72
|
-
def convert_type_proc(v, ctn=nil)
|
73
|
-
case v
|
74
|
-
when JAVA_SQL_CLOB
|
75
|
-
::Sequel::DB2::use_clob_as_blob ? DB2_CLOB_METHOD : super
|
76
|
-
else
|
77
|
-
super
|
78
|
-
end
|
79
|
-
end
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
@@ -185,7 +185,6 @@ module Sequel
|
|
185
185
|
BOOL_FALSE_OLD = '(1 = 0)'.freeze
|
186
186
|
BOOL_TRUE = 'TRUE'.freeze
|
187
187
|
BOOL_FALSE = 'FALSE'.freeze
|
188
|
-
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
|
189
188
|
EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
|
190
189
|
|
191
190
|
# Derby doesn't support an expression between CASE and WHEN,
|
@@ -240,23 +239,10 @@ module Sequel
|
|
240
239
|
|
241
240
|
private
|
242
241
|
|
243
|
-
|
244
|
-
|
245
|
-
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
|
246
|
-
def derby_clob(v) v.getSubString(1, v.length) end
|
242
|
+
def empty_from_sql
|
243
|
+
DEFAULT_FROM
|
247
244
|
end
|
248
245
|
|
249
|
-
DERBY_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:derby_clob)
|
250
|
-
|
251
|
-
# Handle clobs on Derby as strings.
|
252
|
-
def convert_type_proc(v, ctn=nil)
|
253
|
-
if v.is_a?(JAVA_SQL_CLOB)
|
254
|
-
DERBY_CLOB_METHOD
|
255
|
-
else
|
256
|
-
super
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
246
|
# Derby needs a hex string casted to BLOB for blobs.
|
261
247
|
def literal_blob_append(sql, v)
|
262
248
|
sql << BLOB_OPEN << v.unpack(HSTAR).first << BLOB_CLOSE
|
@@ -293,18 +279,9 @@ module Sequel
|
|
293
279
|
end
|
294
280
|
end
|
295
281
|
|
296
|
-
# Derby
|
297
|
-
def
|
298
|
-
|
299
|
-
end
|
300
|
-
|
301
|
-
# Use a default FROM table if the dataset does not contain a FROM table.
|
302
|
-
def select_from_sql(sql)
|
303
|
-
if @opts[:from]
|
304
|
-
super
|
305
|
-
else
|
306
|
-
sql << DEFAULT_FROM
|
307
|
-
end
|
282
|
+
# Derby supports multiple rows in INSERT.
|
283
|
+
def multi_insert_sql_strategy
|
284
|
+
:values
|
308
285
|
end
|
309
286
|
|
310
287
|
# Offset comes before limit in Derby
|
@@ -49,7 +49,7 @@ module Sequel
|
|
49
49
|
# If the :prepare option is given and we aren't in a savepoint,
|
50
50
|
# prepare the transaction for a two-phase commit.
|
51
51
|
def commit_transaction(conn, opts=OPTS)
|
52
|
-
if (s = opts[:prepare]) &&
|
52
|
+
if (s = opts[:prepare]) && savepoint_level(conn) <= 1
|
53
53
|
log_connection_execute(conn, "PREPARE COMMIT #{s}")
|
54
54
|
else
|
55
55
|
super
|
@@ -142,12 +142,12 @@ module Sequel
|
|
142
142
|
|
143
143
|
# Dataset class for H2 datasets accessed via JDBC.
|
144
144
|
class Dataset < JDBC::Dataset
|
145
|
-
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
146
145
|
APOS = Dataset::APOS
|
147
146
|
HSTAR = "H*".freeze
|
148
147
|
ILIKE_PLACEHOLDER = ["CAST(".freeze, " AS VARCHAR_IGNORECASE)".freeze].freeze
|
149
148
|
TIME_FORMAT = "'%H:%M:%S'".freeze
|
150
|
-
|
149
|
+
ONLY_OFFSET = " LIMIT -1 OFFSET ".freeze
|
150
|
+
|
151
151
|
# Emulate the case insensitive LIKE operator and the bitwise operators.
|
152
152
|
def complex_expression_sql_append(sql, op, args)
|
153
153
|
case op
|
@@ -182,23 +182,6 @@ module Sequel
|
|
182
182
|
|
183
183
|
private
|
184
184
|
|
185
|
-
#JAVA_H2_CLOB = Java::OrgH2Jdbc::JdbcClob
|
186
|
-
|
187
|
-
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
|
188
|
-
def h2_clob(v) v.getSubString(1, v.length) end
|
189
|
-
end
|
190
|
-
|
191
|
-
H2_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:h2_clob)
|
192
|
-
|
193
|
-
# Handle H2 specific clobs as strings.
|
194
|
-
def convert_type_proc(v, ctn=nil)
|
195
|
-
if v.is_a?(Java::OrgH2Jdbc::JdbcClob)
|
196
|
-
H2_CLOB_METHOD
|
197
|
-
else
|
198
|
-
super
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
185
|
# H2 expects hexadecimal strings for blob values
|
203
186
|
def literal_blob_append(sql, v)
|
204
187
|
sql << APOS << v.unpack(HSTAR).first << APOS
|
@@ -209,8 +192,14 @@ module Sequel
|
|
209
192
|
v.strftime(TIME_FORMAT)
|
210
193
|
end
|
211
194
|
|
212
|
-
|
213
|
-
|
195
|
+
# H2 supports multiple rows in INSERT.
|
196
|
+
def multi_insert_sql_strategy
|
197
|
+
:values
|
198
|
+
end
|
199
|
+
|
200
|
+
def select_only_offset_sql(sql)
|
201
|
+
sql << ONLY_OFFSET
|
202
|
+
literal_append(sql, @opts[:offset])
|
214
203
|
end
|
215
204
|
|
216
205
|
# H2 supports quoted function names.
|
@@ -32,6 +32,11 @@ module Sequel
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
# HSQLDB supports DROP TABLE IF EXISTS
|
36
|
+
def supports_drop_table_if_exists?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
35
40
|
private
|
36
41
|
|
37
42
|
# HSQLDB specific SQL for renaming columns, and changing column types and/or nullity.
|
@@ -71,6 +76,16 @@ module Sequel
|
|
71
76
|
DATABASE_ERROR_REGEXPS
|
72
77
|
end
|
73
78
|
|
79
|
+
# IF EXISTS comes after table name on HSQLDB
|
80
|
+
def drop_table_sql(name, options)
|
81
|
+
"DROP TABLE #{quote_schema_table(name)}#{' IF EXISTS' if options[:if_exists]}#{' CASCADE' if options[:cascade]}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# IF EXISTS comes after view name on HSQLDB
|
85
|
+
def drop_view_sql(name, options)
|
86
|
+
"DROP VIEW #{quote_schema_table(name)}#{' IF EXISTS' if options[:if_exists]}#{' CASCADE' if options[:cascade]}"
|
87
|
+
end
|
88
|
+
|
74
89
|
# Use IDENTITY() to get the last inserted id.
|
75
90
|
def last_insert_id(conn, opts=OPTS)
|
76
91
|
statement(conn) do |stmt|
|
@@ -113,12 +128,6 @@ module Sequel
|
|
113
128
|
class Dataset < JDBC::Dataset
|
114
129
|
BOOL_TRUE = 'TRUE'.freeze
|
115
130
|
BOOL_FALSE = 'FALSE'.freeze
|
116
|
-
# HSQLDB does support common table expressions, but the support is broken.
|
117
|
-
# CTEs operate more like temprorary tables or views, lasting longer than the duration of the expression.
|
118
|
-
# CTEs in earlier queries might take precedence over CTEs with the same name in later queries.
|
119
|
-
# Also, if any CTE is recursive, all CTEs must be recursive.
|
120
|
-
# If you want to use CTEs with HSQLDB, you'll have to manually modify the dataset to allow it.
|
121
|
-
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
|
122
131
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
123
132
|
APOS = Dataset::APOS
|
124
133
|
HSTAR = "H*".freeze
|
@@ -148,6 +157,15 @@ module Sequel
|
|
148
157
|
true
|
149
158
|
end
|
150
159
|
|
160
|
+
# HSQLDB does support common table expressions, but the support is broken.
|
161
|
+
# CTEs operate more like temprorary tables or views, lasting longer than the duration of the expression.
|
162
|
+
# CTEs in earlier queries might take precedence over CTEs with the same name in later queries.
|
163
|
+
# Also, if any CTE is recursive, all CTEs must be recursive.
|
164
|
+
# If you want to use CTEs with HSQLDB, you'll have to manually modify the dataset to allow it.
|
165
|
+
def supports_cte?(type=:select)
|
166
|
+
false
|
167
|
+
end
|
168
|
+
|
151
169
|
# HSQLDB does not support IS TRUE.
|
152
170
|
def supports_is_true?
|
153
171
|
false
|
@@ -160,6 +178,10 @@ module Sequel
|
|
160
178
|
|
161
179
|
private
|
162
180
|
|
181
|
+
def empty_from_sql
|
182
|
+
DEFAULT_FROM
|
183
|
+
end
|
184
|
+
|
163
185
|
# Use string in hex format for blob data.
|
164
186
|
def literal_blob_append(sql, v)
|
165
187
|
sql << BLOB_OPEN << v.unpack(HSTAR).first << APOS
|
@@ -180,20 +202,11 @@ module Sequel
|
|
180
202
|
BOOL_TRUE
|
181
203
|
end
|
182
204
|
|
183
|
-
# HSQLDB
|
184
|
-
def
|
185
|
-
|
205
|
+
# HSQLDB supports multiple rows in INSERT.
|
206
|
+
def multi_insert_sql_strategy
|
207
|
+
:values
|
186
208
|
end
|
187
209
|
|
188
|
-
# Use a default FROM table if the dataset does not contain a FROM table.
|
189
|
-
def select_from_sql(sql)
|
190
|
-
if @opts[:from]
|
191
|
-
super
|
192
|
-
else
|
193
|
-
sql << DEFAULT_FROM
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
210
|
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
198
211
|
def select_with_sql_base
|
199
212
|
opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
|
@@ -25,21 +25,6 @@ module Sequel
|
|
25
25
|
# Dataset class for JTDS datasets accessed via JDBC.
|
26
26
|
class Dataset < JDBC::Dataset
|
27
27
|
include Sequel::MSSQL::DatasetMethods
|
28
|
-
|
29
|
-
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
|
30
|
-
def jtds_clob(v) v.getSubString(1, v.length) end
|
31
|
-
end
|
32
|
-
|
33
|
-
JTDS_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:jtds_clob)
|
34
|
-
|
35
|
-
# Handle CLOB types retrieved via JTDS.
|
36
|
-
def convert_type_proc(v, ctn=nil)
|
37
|
-
if v.is_a?(Java::NetSourceforgeJtdsJdbc::ClobImpl)
|
38
|
-
JTDS_CLOB_METHOD
|
39
|
-
else
|
40
|
-
super
|
41
|
-
end
|
42
|
-
end
|
43
28
|
end
|
44
29
|
end
|
45
30
|
end
|
@@ -3,6 +3,21 @@ Sequel.require 'adapters/jdbc/transactions'
|
|
3
3
|
|
4
4
|
module Sequel
|
5
5
|
module JDBC
|
6
|
+
class TypeConvertor
|
7
|
+
JAVA_BIG_DECIMAL_CONSTRUCTOR = java.math.BigDecimal.java_class.constructor(Java::long).method(:new_instance)
|
8
|
+
|
9
|
+
def OracleDecimal(r, i)
|
10
|
+
if v = r.getBigDecimal(i)
|
11
|
+
i = v.long_value
|
12
|
+
if v == JAVA_BIG_DECIMAL_CONSTRUCTOR.call(i)
|
13
|
+
i
|
14
|
+
else
|
15
|
+
BigDecimal.new(v.to_string)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
6
21
|
# Database and Dataset support for Oracle databases accessed via JDBC.
|
7
22
|
module Oracle
|
8
23
|
# Instance methods for Oracle Database objects accessed via JDBC.
|
@@ -31,6 +46,11 @@ module Sequel
|
|
31
46
|
super || exception.message =~ /\AClosed Connection/
|
32
47
|
end
|
33
48
|
|
49
|
+
# Default the fetch size for statements to 100, similar to the oci8-based oracle adapter.
|
50
|
+
def default_fetch_size
|
51
|
+
100
|
52
|
+
end
|
53
|
+
|
34
54
|
def last_insert_id(conn, opts)
|
35
55
|
unless sequence = opts[:sequence]
|
36
56
|
if t = opts[:table]
|
@@ -76,50 +96,31 @@ module Sequel
|
|
76
96
|
def supports_releasing_savepoints?
|
77
97
|
false
|
78
98
|
end
|
99
|
+
|
100
|
+
def setup_type_convertor_map
|
101
|
+
super
|
102
|
+
@type_convertor_map[:OracleDecimal] = TypeConvertor::INSTANCE.method(:OracleDecimal)
|
103
|
+
end
|
79
104
|
end
|
80
105
|
|
81
106
|
# Dataset class for Oracle datasets accessed via JDBC.
|
82
107
|
class Dataset < JDBC::Dataset
|
83
108
|
include Sequel::Oracle::DatasetMethods
|
84
109
|
|
85
|
-
|
110
|
+
NUMERIC_TYPE = Java::JavaSQL::Types::NUMERIC
|
111
|
+
TIMESTAMP_TYPE = Java::JavaSQL::Types::TIMESTAMP
|
112
|
+
TIMESTAMPTZ_TYPES = [Java::oracle.jdbc.OracleTypes::TIMESTAMPTZ, Java::oracle.jdbc.OracleTypes::TIMESTAMPLTZ]
|
86
113
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if v.scale == 0
|
93
|
-
i = v.long_value
|
94
|
-
if v.equals(JAVA_BIG_DECIMAL_CONSTRUCTOR.call(i))
|
95
|
-
i
|
96
|
-
else
|
97
|
-
decimal(v)
|
98
|
-
end
|
114
|
+
def type_convertor(map, meta, type, i)
|
115
|
+
case type
|
116
|
+
when NUMERIC_TYPE
|
117
|
+
if meta.getScale(i) == 0
|
118
|
+
map[:OracleDecimal]
|
99
119
|
else
|
100
|
-
|
120
|
+
super
|
101
121
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
ORACLE_DECIMAL_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:oracle_decimal)
|
106
|
-
|
107
|
-
def convert_type_oracle_timestamp(v)
|
108
|
-
db.to_application_timestamp(v.to_string)
|
109
|
-
end
|
110
|
-
|
111
|
-
def convert_type_oracle_timestamptz(v)
|
112
|
-
convert_type_oracle_timestamp(db.synchronize(@opts[:server]){|c| v.timestampValue(c)})
|
113
|
-
end
|
114
|
-
|
115
|
-
def convert_type_proc(v, ctn=nil)
|
116
|
-
case v
|
117
|
-
when JAVA_BIG_DECIMAL
|
118
|
-
ORACLE_DECIMAL_METHOD
|
119
|
-
when Java::OracleSql::TIMESTAMPTZ
|
120
|
-
method(:convert_type_oracle_timestamptz)
|
121
|
-
when Java::OracleSql::TIMESTAMP
|
122
|
-
method(:convert_type_oracle_timestamp)
|
122
|
+
when *TIMESTAMPTZ_TYPES
|
123
|
+
map[TIMESTAMP_TYPE]
|
123
124
|
else
|
124
125
|
super
|
125
126
|
end
|
@@ -4,6 +4,26 @@ module Sequel
|
|
4
4
|
Postgres::CONVERTED_EXCEPTIONS << NativeException
|
5
5
|
|
6
6
|
module JDBC
|
7
|
+
class TypeConvertor
|
8
|
+
# Return PostgreSQL array types as ruby Arrays instead of
|
9
|
+
# JDBC PostgreSQL driver-specific array type. Only used if the
|
10
|
+
# database does not have a conversion proc for the type.
|
11
|
+
def RubyPGArray(r, i)
|
12
|
+
if v = r.getArray(i)
|
13
|
+
v.array.to_ary
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return PostgreSQL hstore types as ruby Hashes instead of
|
18
|
+
# Java HashMaps. Only used if the database does not have a
|
19
|
+
# conversion proc for the type.
|
20
|
+
def RubyPGHstore(r, i)
|
21
|
+
if v = r.getObject(i)
|
22
|
+
v.to_hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
7
27
|
# Adapter, Database, and Dataset support for accessing a PostgreSQL
|
8
28
|
# database via JDBC.
|
9
29
|
module Postgres
|
@@ -12,7 +32,7 @@ module Sequel
|
|
12
32
|
module DatabaseMethods
|
13
33
|
extend Sequel::Database::ResetIdentifierMangling
|
14
34
|
include Sequel::Postgres::DatabaseMethods
|
15
|
-
|
35
|
+
|
16
36
|
# Add the primary_keys and primary_key_sequences instance variables,
|
17
37
|
# so we can get the correct return values for inserted rows.
|
18
38
|
def self.extended(db)
|
@@ -81,8 +101,30 @@ module Sequel
|
|
81
101
|
end
|
82
102
|
end
|
83
103
|
|
104
|
+
def oid_convertor_proc(oid)
|
105
|
+
if (conv = Sequel.synchronize{@oid_convertor_map[oid]}).nil?
|
106
|
+
conv = if pr = conversion_procs[oid]
|
107
|
+
lambda do |r, i|
|
108
|
+
if v = r.getString(i)
|
109
|
+
pr.call(v)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
false
|
114
|
+
end
|
115
|
+
Sequel.synchronize{@oid_convertor_map[oid] = conv}
|
116
|
+
end
|
117
|
+
conv
|
118
|
+
end
|
119
|
+
|
84
120
|
private
|
85
121
|
|
122
|
+
# Clear oid convertor map cache when conversion procs are updated.
|
123
|
+
def conversion_procs_updated
|
124
|
+
super
|
125
|
+
Sequel.synchronize{@oid_convertor_map = {}}
|
126
|
+
end
|
127
|
+
|
86
128
|
def disconnect_error?(exception, opts)
|
87
129
|
super || exception.message =~ /\AThis connection has been closed\.\z|\AFATAL: terminating connection due to administrator command\z/
|
88
130
|
end
|
@@ -101,6 +143,13 @@ module Sequel
|
|
101
143
|
end
|
102
144
|
conn
|
103
145
|
end
|
146
|
+
|
147
|
+
def setup_type_convertor_map
|
148
|
+
super
|
149
|
+
@oid_convertor_map = {}
|
150
|
+
@type_convertor_map[:RubyPGArray] = TypeConvertor::INSTANCE.method(:RubyPGArray)
|
151
|
+
@type_convertor_map[:RubyPGHstore] = TypeConvertor::INSTANCE.method(:RubyPGHstore)
|
152
|
+
end
|
104
153
|
end
|
105
154
|
|
106
155
|
# Dataset subclass used for datasets that connect to PostgreSQL via JDBC.
|
@@ -108,53 +157,6 @@ module Sequel
|
|
108
157
|
include Sequel::Postgres::DatasetMethods
|
109
158
|
APOS = Dataset::APOS
|
110
159
|
|
111
|
-
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
|
112
|
-
# Convert Java::OrgPostgresqlUtil::PGobject to ruby strings
|
113
|
-
def pg_object(v)
|
114
|
-
v.to_string
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# Handle conversions of PostgreSQL array instances
|
119
|
-
class PGArrayConverter
|
120
|
-
# Set the method that will return the correct conversion
|
121
|
-
# proc for elements of this array.
|
122
|
-
def initialize(meth)
|
123
|
-
@conversion_proc_method = meth
|
124
|
-
@conversion_proc = nil
|
125
|
-
end
|
126
|
-
|
127
|
-
# Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays
|
128
|
-
def call(v)
|
129
|
-
_pg_array(v.array)
|
130
|
-
end
|
131
|
-
|
132
|
-
private
|
133
|
-
|
134
|
-
# Handle multi-dimensional Java arrays by recursively mapping them
|
135
|
-
# to ruby arrays of ruby values.
|
136
|
-
def _pg_array(v)
|
137
|
-
v.to_ary.map do |i|
|
138
|
-
if i.respond_to?(:to_ary)
|
139
|
-
_pg_array(i)
|
140
|
-
elsif i
|
141
|
-
if @conversion_proc.nil?
|
142
|
-
@conversion_proc = @conversion_proc_method.call(i)
|
143
|
-
end
|
144
|
-
if @conversion_proc
|
145
|
-
@conversion_proc.call(i)
|
146
|
-
else
|
147
|
-
i
|
148
|
-
end
|
149
|
-
else
|
150
|
-
i
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
PG_OBJECT_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_object)
|
157
|
-
|
158
160
|
# Add the shared PostgreSQL prepared statement methods
|
159
161
|
def prepare(type, name=nil, *values)
|
160
162
|
ps = to_prepared_statement(type, values)
|
@@ -169,55 +171,35 @@ module Sequel
|
|
169
171
|
|
170
172
|
private
|
171
173
|
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
PG_OBJECT_METHOD
|
188
|
-
end
|
189
|
-
when String
|
190
|
-
if pr = db.conversion_procs[ctn]
|
174
|
+
# Literalize strings similar to the native postgres adapter
|
175
|
+
def literal_string_append(sql, v)
|
176
|
+
sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_string(v)} << APOS
|
177
|
+
end
|
178
|
+
|
179
|
+
STRING_TYPE = Java::JavaSQL::Types::VARCHAR
|
180
|
+
ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
|
181
|
+
PG_SPECIFIC_TYPES = [ARRAY_TYPE, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT]
|
182
|
+
HSTORE_TYPE = 'hstore'.freeze
|
183
|
+
|
184
|
+
def type_convertor(map, meta, type, i)
|
185
|
+
case type
|
186
|
+
when *PG_SPECIFIC_TYPES
|
187
|
+
oid = meta.field(i).oid
|
188
|
+
if pr = db.oid_convertor_proc(oid)
|
191
189
|
pr
|
190
|
+
elsif type == ARRAY_TYPE
|
191
|
+
map[:RubyPGArray]
|
192
|
+
elsif oid == 2950 # UUID
|
193
|
+
map[STRING_TYPE]
|
194
|
+
elsif meta.getPGType(i) == HSTORE_TYPE
|
195
|
+
map[:RubyPGHstore]
|
192
196
|
else
|
193
|
-
|
194
|
-
end
|
195
|
-
when JAVA_HASH_MAP
|
196
|
-
if Sequel.respond_to?(:hstore)
|
197
|
-
lambda{|x| Sequel.hstore(HASH_MAP_METHOD.call(x))}
|
198
|
-
else
|
199
|
-
HASH_MAP_METHOD
|
197
|
+
super
|
200
198
|
end
|
201
199
|
else
|
202
200
|
super
|
203
201
|
end
|
204
202
|
end
|
205
|
-
|
206
|
-
# The jdbc/postgresql adapter uses column type oids when determining
|
207
|
-
# conversion procs.
|
208
|
-
def convert_type_proc_uses_column_info?
|
209
|
-
true
|
210
|
-
end
|
211
|
-
|
212
|
-
# Use the column type oid as the database specific column info value.
|
213
|
-
def convert_type_proc_column_info(meta, i)
|
214
|
-
meta.field(i).oid
|
215
|
-
end
|
216
|
-
|
217
|
-
# Literalize strings similar to the native postgres adapter
|
218
|
-
def literal_string_append(sql, v)
|
219
|
-
sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_string(v)} << APOS
|
220
|
-
end
|
221
203
|
end
|
222
204
|
end
|
223
205
|
end
|