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
@@ -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
|