sequel 3.46.0 → 3.47.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 +96 -0
- data/Rakefile +7 -1
- data/bin/sequel +6 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +14 -35
- data/doc/association_basics.rdoc +66 -4
- data/doc/migration.rdoc +4 -0
- data/doc/opening_databases.rdoc +6 -0
- data/doc/postgresql.rdoc +302 -0
- data/doc/release_notes/3.47.0.txt +270 -0
- data/doc/security.rdoc +6 -0
- data/lib/sequel/adapters/ibmdb.rb +9 -9
- data/lib/sequel/adapters/jdbc.rb +22 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
- data/lib/sequel/adapters/mock.rb +2 -0
- data/lib/sequel/adapters/postgres.rb +44 -13
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +94 -55
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/utils/pg_types.rb +1 -14
- data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/features.rb +5 -0
- data/lib/sequel/database/misc.rb +47 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/dataset/actions.rb +4 -2
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +8 -6
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/constraint_validations.rb +5 -2
- data/lib/sequel/extensions/migration.rb +10 -8
- data/lib/sequel/extensions/pagination.rb +3 -0
- data/lib/sequel/extensions/pg_array.rb +85 -25
- data/lib/sequel/extensions/pg_hstore.rb +8 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
- data/lib/sequel/extensions/pg_inet.rb +16 -13
- data/lib/sequel/extensions/pg_interval.rb +6 -2
- data/lib/sequel/extensions/pg_json.rb +18 -11
- data/lib/sequel/extensions/pg_range.rb +17 -2
- data/lib/sequel/extensions/pg_range_ops.rb +7 -5
- data/lib/sequel/extensions/pg_row.rb +29 -12
- data/lib/sequel/extensions/pretty_table.rb +3 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -1
- data/lib/sequel/extensions/select_remove.rb +3 -0
- data/lib/sequel/model.rb +8 -2
- data/lib/sequel/model/associations.rb +39 -27
- data/lib/sequel/model/base.rb +99 -38
- data/lib/sequel/model/plugins.rb +25 -0
- data/lib/sequel/plugins/association_autoreloading.rb +27 -22
- data/lib/sequel/plugins/association_dependencies.rb +1 -7
- data/lib/sequel/plugins/auto_validations.rb +110 -0
- data/lib/sequel/plugins/boolean_readers.rb +1 -6
- data/lib/sequel/plugins/caching.rb +6 -13
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +14 -7
- data/lib/sequel/plugins/constraint_validations.rb +2 -13
- data/lib/sequel/plugins/defaults_setter.rb +1 -6
- data/lib/sequel/plugins/dirty.rb +8 -0
- data/lib/sequel/plugins/error_splitter.rb +54 -0
- data/lib/sequel/plugins/force_encoding.rb +1 -5
- data/lib/sequel/plugins/hook_class_methods.rb +1 -6
- data/lib/sequel/plugins/input_transformer.rb +79 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -1
- data/lib/sequel/plugins/instance_hooks.rb +7 -1
- data/lib/sequel/plugins/json_serializer.rb +5 -10
- data/lib/sequel/plugins/lazy_attributes.rb +20 -7
- data/lib/sequel/plugins/list.rb +1 -6
- data/lib/sequel/plugins/many_through_many.rb +1 -2
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
- data/lib/sequel/plugins/optimistic_locking.rb +1 -5
- data/lib/sequel/plugins/pg_row.rb +4 -2
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -5
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/serialization.rb +11 -13
- data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/static_cache.rb +67 -19
- data/lib/sequel/plugins/string_stripper.rb +7 -27
- data/lib/sequel/plugins/subclasses.rb +3 -5
- data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -7
- data/lib/sequel/plugins/touch.rb +5 -8
- data/lib/sequel/plugins/tree.rb +1 -6
- data/lib/sequel/plugins/typecast_on_load.rb +1 -5
- data/lib/sequel/plugins/update_primary_key.rb +26 -14
- data/lib/sequel/plugins/validation_class_methods.rb +31 -16
- data/lib/sequel/plugins/validation_helpers.rb +50 -26
- data/lib/sequel/plugins/xml_serializer.rb +3 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +131 -15
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +16 -17
- data/spec/core/database_spec.rb +111 -40
- data/spec/core/dataset_spec.rb +65 -74
- data/spec/core/expression_filters_spec.rb +6 -5
- data/spec/core/object_graph_spec.rb +0 -1
- data/spec/core/schema_spec.rb +23 -23
- data/spec/core/spec_helper.rb +5 -1
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_validations_spec.rb +90 -0
- data/spec/extensions/caching_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -1
- data/spec/extensions/composition_spec.rb +12 -5
- data/spec/extensions/constraint_validations_spec.rb +4 -4
- data/spec/extensions/core_refinements_spec.rb +29 -79
- data/spec/extensions/dirty_spec.rb +14 -0
- data/spec/extensions/error_splitter_spec.rb +18 -0
- data/spec/extensions/identity_map_spec.rb +0 -1
- data/spec/extensions/input_transformer_spec.rb +54 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/instance_hooks_spec.rb +12 -1
- data/spec/extensions/json_serializer_spec.rb +0 -1
- data/spec/extensions/lazy_attributes_spec.rb +64 -55
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
- data/spec/extensions/migration_spec.rb +16 -0
- data/spec/extensions/null_dataset_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +48 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
- data/spec/extensions/pg_hstore_spec.rb +5 -0
- data/spec/extensions/pg_inet_spec.rb +5 -0
- data/spec/extensions/pg_interval_spec.rb +7 -3
- data/spec/extensions/pg_json_spec.rb +6 -1
- data/spec/extensions/pg_range_ops_spec.rb +4 -1
- data/spec/extensions/pg_range_spec.rb +5 -0
- data/spec/extensions/pg_row_plugin_spec.rb +13 -0
- data/spec/extensions/pg_row_spec.rb +28 -19
- data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/query_literals_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
- data/spec/extensions/serialization_spec.rb +15 -1
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +59 -9
- data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
- data/spec/extensions/update_primary_key_spec.rb +17 -1
- data/spec/extensions/validation_class_methods_spec.rb +25 -0
- data/spec/extensions/validation_helpers_spec.rb +59 -3
- data/spec/integration/associations_test.rb +5 -5
- data/spec/integration/eager_loader_test.rb +32 -63
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +88 -56
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/timezone_test.rb +0 -1
- data/spec/integration/transaction_test.rb +0 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +106 -84
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +8 -8
- data/spec/model/model_spec.rb +27 -9
- data/spec/model/plugins_spec.rb +71 -0
- data/spec/model/record_spec.rb +99 -13
- metadata +12 -2
|
@@ -27,9 +27,9 @@ module Sequel::Database::SplitAlterTable
|
|
|
27
27
|
op_groups.last << op
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
op_groups.each do |
|
|
31
|
-
next if
|
|
32
|
-
alter_table_sql_list(name,
|
|
30
|
+
op_groups.each do |opgs|
|
|
31
|
+
next if opgs.empty?
|
|
32
|
+
alter_table_sql_list(name, opgs).each{|sql| execute_ddl(sql)}
|
|
33
33
|
remove_cached_schema(name)
|
|
34
34
|
end
|
|
35
35
|
end
|
data/lib/sequel/core.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Sequel
|
|
|
37
37
|
|
|
38
38
|
# Returns the scheme symbol for the Database class.
|
|
39
39
|
def self.adapter_scheme
|
|
40
|
-
@scheme
|
|
40
|
+
@scheme if defined?(@scheme)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# Connects to a database. See Sequel.connect.
|
|
@@ -55,7 +55,7 @@ module Sequel
|
|
|
55
55
|
c = adapter_class(scheme)
|
|
56
56
|
uri_options = c.send(:uri_to_options, uri)
|
|
57
57
|
uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
|
|
58
|
-
uri_options.to_a.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
|
|
58
|
+
uri_options.to_a.each{|k,v| uri_options[k] = (defined?(URI::DEFAULT_PARSER) ? URI::DEFAULT_PARSER : URI).unescape(v) if v.is_a?(String)}
|
|
59
59
|
opts = uri_options.merge(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
|
|
60
60
|
end
|
|
61
61
|
when Hash
|
|
@@ -53,6 +53,11 @@ module Sequel
|
|
|
53
53
|
supports_prepared_transactions? && supports_savepoints?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# Whether the database supports schema parsing via Database#schema.
|
|
57
|
+
def supports_schema_parsing?
|
|
58
|
+
respond_to?(:schema_parse_table, true)
|
|
59
|
+
end
|
|
60
|
+
|
|
56
61
|
# Whether the database and adapter support transaction isolation levels, false by default.
|
|
57
62
|
def supports_transaction_isolation_levels?
|
|
58
63
|
false
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -18,6 +18,33 @@ module Sequel
|
|
|
18
18
|
# have specific support for the database in use.
|
|
19
19
|
DEFAULT_DATABASE_ERROR_REGEXPS = {}.freeze
|
|
20
20
|
|
|
21
|
+
# Mapping of schema type symbols to class or arrays of classes for that
|
|
22
|
+
# symbol.
|
|
23
|
+
SCHEMA_TYPE_CLASSES = {:string=>String, :integer=>Integer, :date=>Date, :datetime=>[Time, DateTime].freeze,
|
|
24
|
+
:time=>Sequel::SQLTime, :boolean=>[TrueClass, FalseClass].freeze, :float=>Float, :decimal=>BigDecimal,
|
|
25
|
+
:blob=>Sequel::SQL::Blob}.freeze
|
|
26
|
+
|
|
27
|
+
# Nested hook Proc; each new hook Proc just wraps the previous one.
|
|
28
|
+
@initialize_hook = Proc.new {|db| }
|
|
29
|
+
|
|
30
|
+
# Register a hook that will be run when a new Database is instantiated. It is
|
|
31
|
+
# called with the new database handle.
|
|
32
|
+
def self.after_initialize(&block)
|
|
33
|
+
raise Error, "must provide block to after_initialize" unless block
|
|
34
|
+
Sequel.synchronize do
|
|
35
|
+
previous = @initialize_hook
|
|
36
|
+
@initialize_hook = Proc.new do |db|
|
|
37
|
+
previous.call(db)
|
|
38
|
+
block.call(db)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Apply an extension to all Database objects created in the future.
|
|
44
|
+
def self.extension(*extensions)
|
|
45
|
+
after_initialize{|db| db.extension(*extensions)}
|
|
46
|
+
end
|
|
47
|
+
|
|
21
48
|
# Register an extension callback for Database objects. ext should be the
|
|
22
49
|
# extension name symbol, and mod should either be a Module that the
|
|
23
50
|
# database is extended with, or a callable object called with the database
|
|
@@ -35,6 +62,11 @@ module Sequel
|
|
|
35
62
|
Sequel.synchronize{EXTENSIONS[ext] = block}
|
|
36
63
|
end
|
|
37
64
|
|
|
65
|
+
# Run the after_initialize hook for the given +instance+.
|
|
66
|
+
def self.run_after_initialize(instance)
|
|
67
|
+
@initialize_hook.call(instance)
|
|
68
|
+
end
|
|
69
|
+
|
|
38
70
|
# Converts a uri to an options hash. These options are then passed
|
|
39
71
|
# to a newly created database object.
|
|
40
72
|
def self.uri_to_options(uri)
|
|
@@ -45,7 +77,7 @@ module Sequel
|
|
|
45
77
|
:database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
|
|
46
78
|
end
|
|
47
79
|
private_class_method :uri_to_options
|
|
48
|
-
|
|
80
|
+
|
|
49
81
|
# The options hash for this database
|
|
50
82
|
attr_reader :opts
|
|
51
83
|
|
|
@@ -93,10 +125,14 @@ module Sequel
|
|
|
93
125
|
@dataset_class = dataset_class_default
|
|
94
126
|
@cache_schema = typecast_value_boolean(@opts.fetch(:cache_schema, true))
|
|
95
127
|
@dataset_modules = []
|
|
128
|
+
@schema_type_classes = SCHEMA_TYPE_CLASSES.dup
|
|
96
129
|
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
|
|
97
130
|
@pool = ConnectionPool.get_pool(self, @opts)
|
|
98
131
|
|
|
99
|
-
|
|
132
|
+
unless typecast_value_boolean(@opts[:keep_reference]) == false
|
|
133
|
+
Sequel.synchronize{::Sequel::DATABASES.push(self)}
|
|
134
|
+
end
|
|
135
|
+
Sequel::Database.run_after_initialize(self)
|
|
100
136
|
end
|
|
101
137
|
|
|
102
138
|
# If a transaction is not currently in process, yield to the block immediately.
|
|
@@ -201,6 +237,11 @@ module Sequel
|
|
|
201
237
|
def quote_identifier(v)
|
|
202
238
|
schema_utility_dataset.quote_identifier(v)
|
|
203
239
|
end
|
|
240
|
+
|
|
241
|
+
# Return ruby class or array of classes for the given type symbol.
|
|
242
|
+
def schema_type_class(type)
|
|
243
|
+
@schema_type_classes[type]
|
|
244
|
+
end
|
|
204
245
|
|
|
205
246
|
# Default serial primary key options, used by the table creation
|
|
206
247
|
# code.
|
|
@@ -210,6 +251,7 @@ module Sequel
|
|
|
210
251
|
|
|
211
252
|
# Cache the prepared statement object at the given name.
|
|
212
253
|
def set_prepared_statement(name, ps)
|
|
254
|
+
ps.prepared_sql
|
|
213
255
|
Sequel.synchronize{prepared_statements[name] = ps}
|
|
214
256
|
end
|
|
215
257
|
|
|
@@ -306,8 +348,8 @@ module Sequel
|
|
|
306
348
|
return klass
|
|
307
349
|
end
|
|
308
350
|
else
|
|
309
|
-
database_error_regexps.each do |regexp,
|
|
310
|
-
return
|
|
351
|
+
database_error_regexps.each do |regexp, klss|
|
|
352
|
+
return klss if exception.message =~ regexp
|
|
311
353
|
end
|
|
312
354
|
end
|
|
313
355
|
|
|
@@ -349,7 +391,7 @@ module Sequel
|
|
|
349
391
|
raise exception
|
|
350
392
|
end
|
|
351
393
|
end
|
|
352
|
-
|
|
394
|
+
|
|
353
395
|
# Typecast the value to an SQL::Blob
|
|
354
396
|
def typecast_value_blob(value)
|
|
355
397
|
value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
|
|
@@ -154,7 +154,7 @@ module Sequel
|
|
|
154
154
|
# # :db_type=>"text",
|
|
155
155
|
# # :allow_null=>false}]]
|
|
156
156
|
def schema(table, opts={})
|
|
157
|
-
raise(Error, 'schema parsing is not implemented on this database') unless
|
|
157
|
+
raise(Error, 'schema parsing is not implemented on this database') unless supports_schema_parsing?
|
|
158
158
|
|
|
159
159
|
opts = opts.dup
|
|
160
160
|
tab = if table.is_a?(Dataset)
|
|
@@ -312,7 +312,7 @@ module Sequel
|
|
|
312
312
|
# for this database. Used when parsing metadata so that column symbols are
|
|
313
313
|
# returned as expected.
|
|
314
314
|
def metadata_dataset
|
|
315
|
-
return @metadata_dataset if @metadata_dataset
|
|
315
|
+
return @metadata_dataset if defined?(@metadata_dataset) && @metadata_dataset
|
|
316
316
|
ds = dataset
|
|
317
317
|
ds.identifier_input_method = identifier_input_method_default
|
|
318
318
|
ds.identifier_output_method = identifier_output_method_default
|
|
@@ -148,7 +148,7 @@ module Sequel
|
|
|
148
148
|
def each
|
|
149
149
|
if @opts[:graph]
|
|
150
150
|
graph_each{|r| yield r}
|
|
151
|
-
elsif row_proc = @row_proc
|
|
151
|
+
elsif defined?(@row_proc) && (row_proc = @row_proc)
|
|
152
152
|
fetch_rows(select_sql){|r| yield row_proc.call(r)}
|
|
153
153
|
else
|
|
154
154
|
fetch_rows(select_sql){|r| yield r}
|
|
@@ -262,7 +262,9 @@ module Sequel
|
|
|
262
262
|
end
|
|
263
263
|
|
|
264
264
|
if column.is_a?(Array)
|
|
265
|
-
|
|
265
|
+
if r = ds.single_record
|
|
266
|
+
r.values_at(*column.map{|c| hash_key_symbol(c)})
|
|
267
|
+
end
|
|
266
268
|
else
|
|
267
269
|
ds.single_value
|
|
268
270
|
end
|
data/lib/sequel/dataset/misc.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Sequel
|
|
|
29
29
|
# Override the given *_sql method based on the type, and
|
|
30
30
|
# cache the result of the sql.
|
|
31
31
|
def prepared_sql
|
|
32
|
-
return @prepared_sql if @prepared_sql
|
|
32
|
+
return @prepared_sql if defined?(@prepared_sql) && @prepared_sql
|
|
33
33
|
@prepared_args ||= []
|
|
34
34
|
@prepared_sql = super
|
|
35
35
|
@opts[:sql] = @prepared_sql
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -495,7 +495,7 @@ module Sequel
|
|
|
495
495
|
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
|
496
496
|
if using_join && !supports_join_using?
|
|
497
497
|
h = {}
|
|
498
|
-
expr.each{|
|
|
498
|
+
expr.each{|e| h[e] = e}
|
|
499
499
|
return join_table(type, table, h, options)
|
|
500
500
|
end
|
|
501
501
|
|
|
@@ -600,11 +600,14 @@ module Sequel
|
|
|
600
600
|
end
|
|
601
601
|
|
|
602
602
|
# Returns a cloned dataset with the given lock style. If style is a
|
|
603
|
-
# string, it will be used directly.
|
|
604
|
-
#
|
|
605
|
-
#
|
|
603
|
+
# string, it will be used directly. You should never pass a string
|
|
604
|
+
# to this method that is derived from user input, as that can lead to
|
|
605
|
+
# SQL injection.
|
|
606
606
|
#
|
|
607
|
-
#
|
|
607
|
+
# A symbol may be used for database independent locking behavior, but
|
|
608
|
+
# all supported symbols have separate methods (e.g. for_update).
|
|
609
|
+
#
|
|
610
|
+
# DB[:items].lock_style('FOR SHARE NOWAIT') # SELECT * FROM items FOR SHARE NOWAIT
|
|
608
611
|
def lock_style(style)
|
|
609
612
|
clone(:lock => style)
|
|
610
613
|
end
|
|
@@ -1107,7 +1110,6 @@ module Sequel
|
|
|
1107
1110
|
# DB[:items].invert_order([:category, Sequel.desc(:price)]) #=> [Sequel.desc(:category), Sequel.asc(:price)]
|
|
1108
1111
|
def invert_order(order)
|
|
1109
1112
|
return nil unless order
|
|
1110
|
-
new_order = []
|
|
1111
1113
|
order.map do |f|
|
|
1112
1114
|
case f
|
|
1113
1115
|
when SQL::OrderedExpression
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -150,7 +150,9 @@ module Sequel
|
|
|
150
150
|
else
|
|
151
151
|
check_truncation_allowed!
|
|
152
152
|
raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where] || opts[:having]
|
|
153
|
-
|
|
153
|
+
t = ''
|
|
154
|
+
source_list_append(t, opts[:from])
|
|
155
|
+
_truncate_sql(t)
|
|
154
156
|
end
|
|
155
157
|
end
|
|
156
158
|
|
|
@@ -345,7 +347,7 @@ module Sequel
|
|
|
345
347
|
end
|
|
346
348
|
end
|
|
347
349
|
def_append_methods(PUBLIC_APPEND_METHODS + PRIVATE_APPEND_METHODS)
|
|
348
|
-
private
|
|
350
|
+
private(*PRIVATE_APPEND_METHODS)
|
|
349
351
|
|
|
350
352
|
# SQL fragment for AliasedExpression
|
|
351
353
|
def aliased_expression_sql_append(sql, ae)
|
|
@@ -412,11 +414,11 @@ module Sequel
|
|
|
412
414
|
when *IS_OPERATORS
|
|
413
415
|
r = args.at(1)
|
|
414
416
|
if r.nil? || supports_is_true?
|
|
415
|
-
raise(InvalidOperation, 'Invalid argument used for IS operator') unless
|
|
417
|
+
raise(InvalidOperation, 'Invalid argument used for IS operator') unless val = IS_LITERALS[r]
|
|
416
418
|
sql << PAREN_OPEN
|
|
417
419
|
literal_append(sql, args.at(0))
|
|
418
420
|
sql << SPACE << op.to_s << SPACE
|
|
419
|
-
sql <<
|
|
421
|
+
sql << val << PAREN_CLOSE
|
|
420
422
|
elsif op == :IS
|
|
421
423
|
complex_expression_sql_append(sql, :"=", args)
|
|
422
424
|
else
|
|
@@ -667,7 +669,7 @@ module Sequel
|
|
|
667
669
|
sch = sch.to_s if sch
|
|
668
670
|
case table_name
|
|
669
671
|
when Symbol
|
|
670
|
-
s, t,
|
|
672
|
+
s, t, _ = split_symbol(table_name)
|
|
671
673
|
[s||sch, t]
|
|
672
674
|
when SQL::QualifiedIdentifier
|
|
673
675
|
[table_name.table.to_s, table_name.column.to_s]
|
|
@@ -1195,7 +1197,7 @@ module Sequel
|
|
|
1195
1197
|
# name isn't already qualified.
|
|
1196
1198
|
def qualified_column_name(column, table)
|
|
1197
1199
|
if Symbol === column
|
|
1198
|
-
c_table, column,
|
|
1200
|
+
c_table, column, _ = split_symbol(column)
|
|
1199
1201
|
unless c_table
|
|
1200
1202
|
case table
|
|
1201
1203
|
when Symbol
|
|
@@ -357,7 +357,7 @@ module Sequel
|
|
|
357
357
|
when :includes
|
|
358
358
|
generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| {c => arg}}))
|
|
359
359
|
if arg.is_a?(Range)
|
|
360
|
-
if
|
|
360
|
+
if arg.begin.is_a?(Integer) && arg.end.is_a?(Integer)
|
|
361
361
|
validation_type = :includes_int_range
|
|
362
362
|
arg = "#{arg.begin}..#{'.' if arg.exclude_end?}#{arg.end}"
|
|
363
363
|
else
|
|
@@ -410,7 +410,10 @@ module Sequel
|
|
|
410
410
|
# Add the constraint to the generator, including a NOT NULL constraint
|
|
411
411
|
# for all columns unless the :allow_nil option is given.
|
|
412
412
|
def generator_add_constraint_from_validation(generator, val, cons)
|
|
413
|
-
|
|
413
|
+
if val[:allow_nil]
|
|
414
|
+
nil_cons = Sequel.expr(val[:columns].map{|c| [c, nil]})
|
|
415
|
+
cons = Sequel.|(nil_cons, cons) if cons
|
|
416
|
+
else
|
|
414
417
|
nil_cons = Sequel.negate(val[:columns].map{|c| [c, nil]})
|
|
415
418
|
cons = cons ? Sequel.&(nil_cons, cons) : nil_cons
|
|
416
419
|
end
|
|
@@ -85,7 +85,7 @@ module Sequel
|
|
|
85
85
|
# Whether to use transactions for this migration, default depends on the
|
|
86
86
|
# database.
|
|
87
87
|
attr_accessor :use_transactions
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
# Don't set transaction use by default.
|
|
90
90
|
def initialize
|
|
91
91
|
@use_transactions = nil
|
|
@@ -374,11 +374,12 @@ module Sequel
|
|
|
374
374
|
end
|
|
375
375
|
|
|
376
376
|
# Migrates the supplied database using the migration files in the the specified directory. Options:
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
#
|
|
377
|
+
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
|
378
|
+
# :column :: The column in the :table argument storing the migration version (default: :version).
|
|
379
|
+
# :current :: The current version of the database. If not given, it is retrieved from the database
|
|
380
|
+
# using the :table and :column options.
|
|
381
|
+
# :table :: The table containing the schema version (default: :schema_info).
|
|
382
|
+
# :target :: The target version to which to migrate. If not given, migrates to the maximum version.
|
|
382
383
|
#
|
|
383
384
|
# Examples:
|
|
384
385
|
# Sequel::Migrator.run(DB, "migrations")
|
|
@@ -435,6 +436,7 @@ module Sequel
|
|
|
435
436
|
raise(Error, "Must supply a valid migration path") unless File.directory?(directory)
|
|
436
437
|
@db = db
|
|
437
438
|
@directory = directory
|
|
439
|
+
@allow_missing_migration_files = opts[:allow_missing_migration_files]
|
|
438
440
|
@files = get_migration_files
|
|
439
441
|
schema, table = @db.send(:schema_and_table, opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE))
|
|
440
442
|
@table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
|
|
@@ -553,7 +555,7 @@ module Sequel
|
|
|
553
555
|
raise(Error, "Duplicate migration version: #{version}") if files[version]
|
|
554
556
|
files[version] = File.join(directory, file)
|
|
555
557
|
end
|
|
556
|
-
1.upto(files.length - 1){|i| raise(Error, "Missing migration version: #{i}") unless files[i]}
|
|
558
|
+
1.upto(files.length - 1){|i| raise(Error, "Missing migration version: #{i}") unless files[i]} unless @allow_missing_migration_files
|
|
557
559
|
files
|
|
558
560
|
end
|
|
559
561
|
|
|
@@ -674,7 +676,7 @@ module Sequel
|
|
|
674
676
|
def get_applied_migrations
|
|
675
677
|
am = ds.select_order_map(column)
|
|
676
678
|
missing_migration_files = am - files.map{|f| File.basename(f).downcase}
|
|
677
|
-
raise(Error, "Applied migration files not in file system: #{missing_migration_files.join(', ')}") if missing_migration_files.length > 0
|
|
679
|
+
raise(Error, "Applied migration files not in file system: #{missing_migration_files.join(', ')}") if missing_migration_files.length > 0 && !@allow_missing_migration_files
|
|
678
680
|
am
|
|
679
681
|
end
|
|
680
682
|
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
#
|
|
18
18
|
# Sequel.pg_array(array)
|
|
19
19
|
#
|
|
20
|
-
# If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]
|
|
21
|
-
# or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html]
|
|
20
|
+
# If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html],
|
|
21
|
+
# or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html]
|
|
22
22
|
# and have activated refinements for the file, you can also use Array#pg_array:
|
|
23
23
|
#
|
|
24
24
|
# array.pg_array
|
|
@@ -32,15 +32,14 @@
|
|
|
32
32
|
#
|
|
33
33
|
# DB[:table].insert(:column=>Sequel.pg_array([1, 2, 3]))
|
|
34
34
|
#
|
|
35
|
-
#
|
|
36
|
-
# probably want to modify the schema parsing/typecasting so that it
|
|
37
|
-
# recognizes and correctly handles the arrays, which you can do by:
|
|
35
|
+
# To use this extension, first load it into your Sequel::Database instance:
|
|
38
36
|
#
|
|
39
37
|
# DB.extension :pg_array
|
|
40
38
|
#
|
|
41
|
-
# If you are not using the native postgres adapter
|
|
42
|
-
#
|
|
43
|
-
# set it to typecast the
|
|
39
|
+
# If you are not using the native postgres adapter and are using array
|
|
40
|
+
# types as model column values you probably should use the
|
|
41
|
+
# pg_typecast_on_load plugin in the model, and set it to typecast the
|
|
42
|
+
# array column(s) on load.
|
|
44
43
|
#
|
|
45
44
|
# This extension by default includes handlers for array types for
|
|
46
45
|
# all scalar types that the native postgres adapter handles. It
|
|
@@ -48,18 +47,24 @@
|
|
|
48
47
|
# general, you just need to make sure that the scalar type is
|
|
49
48
|
# handled and has the appropriate converter installed in
|
|
50
49
|
# Sequel::Postgres::PG_TYPES under the appropriate type OID.
|
|
51
|
-
# Then you can call
|
|
52
|
-
#
|
|
53
|
-
# for the array type.
|
|
50
|
+
# Then you can call
|
|
51
|
+
# Sequel::Postgres::PGArray::DatabaseMethods#register_array_type
|
|
52
|
+
# to automatically set up a handler for the array type. So if you
|
|
53
|
+
# want to support the foo[] type (assuming the foo type is already
|
|
54
|
+
# supported):
|
|
54
55
|
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
56
|
+
# DB.register_array_type('foo')
|
|
57
|
+
#
|
|
58
|
+
# You can also register array types on a global basis using
|
|
59
|
+
# Sequel::Postgres::PGArray.register. In this case, you'll have
|
|
60
|
+
# to specify the type oids:
|
|
58
61
|
#
|
|
59
62
|
# Sequel::Postgres::PGArray.register('foo', :oid=>4321, :scalar_oid=>1234)
|
|
60
63
|
#
|
|
61
|
-
# Sequel::Postgres::PGArray
|
|
62
|
-
# and
|
|
64
|
+
# Both Sequel::Postgres::PGArray::DatabaseMethods#register_array_type
|
|
65
|
+
# and Sequel::Postgres::PGArray.register support many options to
|
|
66
|
+
# customize the array type handling. See the Sequel::Postgres::PGArray.register
|
|
67
|
+
# method documentation.
|
|
63
68
|
#
|
|
64
69
|
# If you want an easy way to call PostgreSQL array functions and
|
|
65
70
|
# operators, look into the pg_array_ops extension.
|
|
@@ -115,8 +120,8 @@ module Sequel
|
|
|
115
120
|
NULL = 'NULL'.freeze
|
|
116
121
|
QUOTE = '"'.freeze
|
|
117
122
|
|
|
118
|
-
#
|
|
119
|
-
# used by the schema parsing.
|
|
123
|
+
# Global hash of database array type name strings to symbols (e.g. 'double precision' => :float),
|
|
124
|
+
# used by the schema parsing for array types registered globally.
|
|
120
125
|
ARRAY_TYPES = {}
|
|
121
126
|
|
|
122
127
|
# Registers an array type that the extension should handle. Makes a Database instance that
|
|
@@ -153,6 +158,8 @@ module Sequel
|
|
|
153
158
|
# typecasting method to be created in the database. This should only be used
|
|
154
159
|
# to alias existing array types. For example, if there is an array type that can be
|
|
155
160
|
# treated just like an integer array, you can do :typecast_method=>:integer.
|
|
161
|
+
# :typecast_method_map :: The map in which to place the database type string to type symbol mapping.
|
|
162
|
+
# Defaults to ARRAY_TYPES.
|
|
156
163
|
# :typecast_methods_module :: If given, a module object to add the typecasting method to. Defaults
|
|
157
164
|
# to DatabaseMethods.
|
|
158
165
|
#
|
|
@@ -163,6 +170,7 @@ module Sequel
|
|
|
163
170
|
type = (typecast_method || opts[:type_symbol] || db_type).to_sym
|
|
164
171
|
type_procs = opts[:type_procs] || PG_TYPES
|
|
165
172
|
mod = opts[:typecast_methods_module] || DatabaseMethods
|
|
173
|
+
typecast_method_map = opts[:typecast_method_map] || ARRAY_TYPES
|
|
166
174
|
|
|
167
175
|
if converter = opts[:converter]
|
|
168
176
|
raise Error, "can't provide both a block and :converter option to register" if block
|
|
@@ -178,7 +186,7 @@ module Sequel
|
|
|
178
186
|
array_type = (opts[:array_type] || db_type).to_s.dup.freeze
|
|
179
187
|
creator = (opts[:parser] == :json ? JSONCreator : Creator).new(array_type, converter)
|
|
180
188
|
|
|
181
|
-
|
|
189
|
+
typecast_method_map[db_type] = :"#{type}_array"
|
|
182
190
|
|
|
183
191
|
define_array_typecast_method(mod, type, creator, opts.fetch(:scalar_typecast, type)) unless typecast_method
|
|
184
192
|
|
|
@@ -208,6 +216,22 @@ module Sequel
|
|
|
208
216
|
ESCAPE_REPLACEMENT = '\\\\\1'.freeze
|
|
209
217
|
BLOB_RANGE = 1...-1
|
|
210
218
|
|
|
219
|
+
# Create the local hash of database type strings to schema type symbols,
|
|
220
|
+
# used for array types local to this database.
|
|
221
|
+
def self.extended(db)
|
|
222
|
+
db.instance_eval do
|
|
223
|
+
@pg_array_schema_types ||= {}
|
|
224
|
+
copy_conversion_procs([1009, 1007, 1016, 1231, 1022, 1000, 1001, 1182, 1183, 1270, 1005, 1028, 1021, 1014, 1015])
|
|
225
|
+
[:string_array, :integer_array, :decimal_array, :float_array, :boolean_array, :blob_array, :date_array, :time_array, :datetime_array].each do |v|
|
|
226
|
+
@schema_type_classes[v] = PGArray
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
procs = db.conversion_procs
|
|
231
|
+
procs[1115] = Creator.new("timestamp without time zone", procs[1114])
|
|
232
|
+
procs[1185] = Creator.new("timestamp with time zone", procs[1184])
|
|
233
|
+
end
|
|
234
|
+
|
|
211
235
|
# Handle arrays in bound variables
|
|
212
236
|
def bound_variable_arg(arg, conn)
|
|
213
237
|
case arg
|
|
@@ -220,13 +244,24 @@ module Sequel
|
|
|
220
244
|
end
|
|
221
245
|
end
|
|
222
246
|
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
247
|
+
# Register a database specific array type. This can be used to support
|
|
248
|
+
# different array types per Database. Use of this method does not
|
|
249
|
+
# affect global state, unlike PGArray.register. See PGArray.register for
|
|
250
|
+
# possible options.
|
|
251
|
+
def register_array_type(db_type, opts={}, &block)
|
|
252
|
+
opts = {:type_procs=>conversion_procs, :typecast_method_map=>@pg_array_schema_types, :typecast_methods_module=>(class << self; self; end)}.merge(opts)
|
|
253
|
+
unless (opts.has_key?(:scalar_oid) || block) && opts.has_key?(:oid)
|
|
254
|
+
array_oid, scalar_oid = from(:pg_type).where(:typname=>db_type.to_s).get([:typarray, :oid])
|
|
255
|
+
opts[:scalar_oid] = scalar_oid unless opts.has_key?(:scalar_oid) || block
|
|
256
|
+
opts[:oid] = array_oid unless opts.has_key?(:oid)
|
|
229
257
|
end
|
|
258
|
+
PGArray.register(db_type, opts, &block)
|
|
259
|
+
@schema_type_classes[:"#{opts[:typecast_method] || opts[:type_symbol] || db_type}_array"] = PGArray
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Return PGArray if this type matches any supported array type.
|
|
263
|
+
def schema_type_class(type)
|
|
264
|
+
super || (ARRAY_TYPES.each_value{|v| return PGArray if type == v}; nil)
|
|
230
265
|
end
|
|
231
266
|
|
|
232
267
|
private
|
|
@@ -247,6 +282,15 @@ module Sequel
|
|
|
247
282
|
end
|
|
248
283
|
end
|
|
249
284
|
|
|
285
|
+
# Automatically handle array types for the given named types.
|
|
286
|
+
def convert_named_procs_to_procs(named_procs)
|
|
287
|
+
h = super
|
|
288
|
+
from(:pg_type).where(:oid=>h.keys).select_map([:typname, :oid, :typarray]).each do |name, scalar_oid, array_oid|
|
|
289
|
+
register_array_type(name, :type_procs=>h, :oid=>array_oid.to_i, :scalar_oid=>scalar_oid.to_i)
|
|
290
|
+
end
|
|
291
|
+
h
|
|
292
|
+
end
|
|
293
|
+
|
|
250
294
|
# Manually override the typecasting for timestamp array types so that
|
|
251
295
|
# they use the database's timezone instead of the global Sequel
|
|
252
296
|
# timezone.
|
|
@@ -259,6 +303,22 @@ module Sequel
|
|
|
259
303
|
procs
|
|
260
304
|
end
|
|
261
305
|
|
|
306
|
+
# Look into both the current database's array schema types and the global
|
|
307
|
+
# array schema types to get the type symbol for the given database type
|
|
308
|
+
# string.
|
|
309
|
+
def pg_array_schema_type(type)
|
|
310
|
+
@pg_array_schema_types[type] || ARRAY_TYPES[type]
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Make the column type detection handle registered array types.
|
|
314
|
+
def schema_column_type(db_type)
|
|
315
|
+
if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1))
|
|
316
|
+
type
|
|
317
|
+
else
|
|
318
|
+
super
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
262
322
|
# Given a value to typecast and the type of PGArray subclass:
|
|
263
323
|
# * If given a PGArray with a matching array_type, use it directly.
|
|
264
324
|
# * If given a PGArray with a different array_type, return a PGArray
|