sequel 3.46.0 → 3.47.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|