sequel 3.36.1 → 3.37.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +84 -0
- data/Rakefile +13 -0
- data/bin/sequel +12 -16
- data/doc/advanced_associations.rdoc +36 -67
- data/doc/association_basics.rdoc +11 -16
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/schema_modification.rdoc +4 -0
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +26 -8
- data/lib/sequel/adapters/mysql2.rb +4 -3
- data/lib/sequel/adapters/odbc/mssql.rb +2 -2
- data/lib/sequel/adapters/postgres.rb +4 -60
- data/lib/sequel/adapters/shared/mssql.rb +2 -1
- data/lib/sequel/adapters/shared/mysql.rb +0 -5
- data/lib/sequel/adapters/shared/postgres.rb +68 -2
- data/lib/sequel/adapters/shared/sqlite.rb +17 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +12 -1
- data/lib/sequel/adapters/utils/pg_types.rb +76 -0
- data/lib/sequel/core.rb +13 -0
- data/lib/sequel/database/misc.rb +41 -1
- data/lib/sequel/database/schema_generator.rb +23 -10
- data/lib/sequel/database/schema_methods.rb +26 -4
- data/lib/sequel/dataset/graph.rb +2 -1
- data/lib/sequel/dataset/query.rb +62 -2
- data/lib/sequel/extensions/_pretty_table.rb +7 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +5 -4
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +13 -2
- data/lib/sequel/extensions/core_extensions.rb +6 -0
- data/lib/sequel/extensions/eval_inspect.rb +158 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/named_timezones.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +4 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +219 -168
- data/lib/sequel/extensions/pg_array_ops.rb +7 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -4
- data/lib/sequel/extensions/pg_hstore.rb +3 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +7 -2
- data/lib/sequel/extensions/pg_inet.rb +28 -3
- data/lib/sequel/extensions/pg_interval.rb +192 -0
- data/lib/sequel/extensions/pg_json.rb +21 -9
- data/lib/sequel/extensions/pg_range.rb +487 -0
- data/lib/sequel/extensions/pg_range_ops.rb +122 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +3 -2
- data/lib/sequel/extensions/pretty_table.rb +12 -1
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/query_literals.rb +6 -6
- data/lib/sequel/extensions/schema_dumper.rb +39 -38
- data/lib/sequel/extensions/select_remove.rb +4 -0
- data/lib/sequel/extensions/server_block.rb +3 -2
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +9 -3
- data/lib/sequel/extensions/to_dot.rb +4 -0
- data/lib/sequel/model/associations.rb +150 -91
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/list.rb +1 -0
- data/lib/sequel/plugins/many_through_many.rb +33 -32
- data/lib/sequel/plugins/nested_attributes.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/sql.rb +14 -14
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mysql_spec.rb +25 -0
- data/spec/adapters/postgres_spec.rb +572 -28
- data/spec/adapters/sqlite_spec.rb +16 -1
- data/spec/core/database_spec.rb +61 -2
- data/spec/core/dataset_spec.rb +92 -0
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/extensions/arbitrary_servers_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/eval_inspect_spec.rb +58 -0
- data/spec/extensions/json_serializer_spec.rb +0 -6
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +81 -0
- data/spec/extensions/nested_attributes_spec.rb +21 -4
- data/spec/extensions/pg_array_ops_spec.rb +1 -11
- data/spec/extensions/pg_array_spec.rb +181 -90
- data/spec/extensions/pg_auto_parameterize_spec.rb +3 -3
- data/spec/extensions/pg_hstore_spec.rb +1 -3
- data/spec/extensions/pg_inet_spec.rb +6 -1
- data/spec/extensions/pg_interval_spec.rb +73 -0
- data/spec/extensions/pg_json_spec.rb +5 -9
- data/spec/extensions/pg_range_ops_spec.rb +49 -0
- data/spec/extensions/pg_range_spec.rb +372 -0
- data/spec/extensions/pg_statement_cache_spec.rb +1 -2
- data/spec/extensions/query_literals_spec.rb +1 -2
- data/spec/extensions/schema_dumper_spec.rb +48 -89
- data/spec/extensions/serialization_spec.rb +1 -5
- data/spec/extensions/server_block_spec.rb +2 -2
- data/spec/extensions/spec_helper.rb +12 -2
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/integration/associations_test.rb +4 -4
- data/spec/integration/database_test.rb +2 -2
- data/spec/integration/dataset_test.rb +4 -4
- data/spec/integration/eager_loader_test.rb +6 -6
- data/spec/integration/plugin_test.rb +2 -2
- data/spec/integration/spec_helper.rb +2 -2
- data/spec/model/association_reflection_spec.rb +5 -0
- data/spec/model/associations_spec.rb +156 -49
- data/spec/model/eager_loading_spec.rb +137 -2
- data/spec/model/model_spec.rb +10 -10
- metadata +15 -2
@@ -0,0 +1,122 @@
|
|
1
|
+
# The pg_range_ops extension adds support to Sequel's DSL to make
|
2
|
+
# it easier to call PostgreSQL range functions and operators.
|
3
|
+
#
|
4
|
+
# To load the extension:
|
5
|
+
#
|
6
|
+
# Sequel.extension :pg_range_ops
|
7
|
+
#
|
8
|
+
# The most common usage is taking an object that represents an SQL
|
9
|
+
# identifier (such as a :symbol), and calling #pg_range on it:
|
10
|
+
#
|
11
|
+
# r = :range.pg_range
|
12
|
+
#
|
13
|
+
# This creates a Sequel::Postgres::RangeOp object that can be used
|
14
|
+
# for easier querying:
|
15
|
+
#
|
16
|
+
# r.contains(:other) # range @> other
|
17
|
+
# r.contained_by(:other) # range <@ other
|
18
|
+
# r.overlaps(:other) # range && other
|
19
|
+
# r.left_of(:other) # range << other
|
20
|
+
# r.right_of(:other) # range >> other
|
21
|
+
# r.starts_before(:other) # range &< other
|
22
|
+
# r.ends_after(:other) # range &> other
|
23
|
+
# r.adjacent_to(:other) # range -|- other
|
24
|
+
#
|
25
|
+
# r.lower # lower(range)
|
26
|
+
# r.upper # upper(range)
|
27
|
+
# r.isempty # isempty(range)
|
28
|
+
# r.lower_inc # lower_inc(range)
|
29
|
+
# r.upper_inc # upper_inc(range)
|
30
|
+
# r.lower_inf # lower_inf(range)
|
31
|
+
# r.upper_inf # upper_inf(range)
|
32
|
+
#
|
33
|
+
# See the PostgreSQL range function and operator documentation for more
|
34
|
+
# details on what these functions and operators do.
|
35
|
+
#
|
36
|
+
# If you are also using the pg_range extension, you should load it before
|
37
|
+
# loading this extension. Doing so will allow you to use PGArray#op to get
|
38
|
+
# an RangeOp, allowing you to perform range operations on range literals.
|
39
|
+
|
40
|
+
module Sequel
|
41
|
+
module Postgres
|
42
|
+
# The RangeOp class is a simple container for a single object that
|
43
|
+
# defines methods that yield Sequel expression objects representing
|
44
|
+
# PostgreSQL range operators and functions.
|
45
|
+
#
|
46
|
+
# Most methods in this class are defined via metaprogramming, see
|
47
|
+
# the pg_range_ops extension documentation for details on the API.
|
48
|
+
class RangeOp < Sequel::SQL::Wrapper
|
49
|
+
OPERATORS = {
|
50
|
+
:contains => ["(".freeze, " @> ".freeze, ")".freeze].freeze,
|
51
|
+
:contained_by => ["(".freeze, " <@ ".freeze, ")".freeze].freeze,
|
52
|
+
:left_of => ["(".freeze, " << ".freeze, ")".freeze].freeze,
|
53
|
+
:right_of => ["(".freeze, " >> ".freeze, ")".freeze].freeze,
|
54
|
+
:starts_before => ["(".freeze, " &< ".freeze, ")".freeze].freeze,
|
55
|
+
:ends_after => ["(".freeze, " &> ".freeze, ")".freeze].freeze,
|
56
|
+
:adjacent_to => ["(".freeze, " -|- ".freeze, ")".freeze].freeze,
|
57
|
+
:overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
|
58
|
+
}
|
59
|
+
FUNCTIONS = %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'
|
60
|
+
|
61
|
+
FUNCTIONS.each do |f|
|
62
|
+
class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
|
63
|
+
end
|
64
|
+
OPERATORS.keys.each do |f|
|
65
|
+
class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
|
66
|
+
end
|
67
|
+
|
68
|
+
# These operators are already supported by the wrapper, but for ranges they
|
69
|
+
# return ranges, so wrap the results in another RangeOp.
|
70
|
+
%w'+ * -'.each do |f|
|
71
|
+
class_eval("def #{f}(v); RangeOp.new(super) end", __FILE__, __LINE__)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return the receiver.
|
75
|
+
def pg_range
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Create a boolen expression for the given type and argument.
|
82
|
+
def operator(type, other)
|
83
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(OPERATORS[type], [value, other]))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return a function called with the receiver.
|
87
|
+
def function(name)
|
88
|
+
Sequel::SQL::Function.new(name, self)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module RangeOpMethods
|
93
|
+
# Wrap the receiver in an RangeOp so you can easily use the PostgreSQL
|
94
|
+
# range functions and operators with it.
|
95
|
+
def pg_range
|
96
|
+
RangeOp.new(self)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if defined?(PGRange)
|
101
|
+
class PGRange
|
102
|
+
# Wrap the PGRange instance in an RangeOp, allowing you to easily use
|
103
|
+
# the PostgreSQL range functions and operators with literal ranges.
|
104
|
+
def op
|
105
|
+
RangeOp.new(self)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class SQL::GenericExpression
|
112
|
+
include Sequel::Postgres::RangeOpMethods
|
113
|
+
end
|
114
|
+
|
115
|
+
class LiteralString
|
116
|
+
include Sequel::Postgres::RangeOpMethods
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Symbol
|
121
|
+
include Sequel::Postgres::RangeOpMethods
|
122
|
+
end
|
@@ -3,8 +3,7 @@
|
|
3
3
|
# executed repeatedly. When combined with the pg_auto_parameterize
|
4
4
|
# extension, it can take Sequel code such as:
|
5
5
|
#
|
6
|
-
# DB.
|
7
|
-
# DB.extend Sequel::Postgres::StatementCache::DatabaseMethods
|
6
|
+
# DB.extension :pg_auto_parameterize, :pg_statement_cache
|
8
7
|
# DB[:table].filter(:a=>1)
|
9
8
|
# DB[:table].filter(:a=>2)
|
10
9
|
# DB[:table].filter(:a=>3)
|
@@ -313,4 +312,6 @@ module Sequel
|
|
313
312
|
end
|
314
313
|
end
|
315
314
|
end
|
315
|
+
|
316
|
+
Database.register_extension(:pg_statement_cache, Postgres::StatementCache::DatabaseMethods)
|
316
317
|
end
|
@@ -1,6 +1,17 @@
|
|
1
1
|
# The pretty_table extension adds Sequel::Dataset#print and the
|
2
2
|
# Sequel::PrettyTable class for creating nice-looking plain-text
|
3
|
-
# tables.
|
3
|
+
# tables. Example:
|
4
|
+
#
|
5
|
+
# +--+-------+
|
6
|
+
# |id|name |
|
7
|
+
# |--+-------|
|
8
|
+
# |1 |fasdfas|
|
9
|
+
# |2 |test |
|
10
|
+
# +--+-------+
|
11
|
+
#
|
12
|
+
# To load the extension:
|
13
|
+
#
|
14
|
+
# Sequel.extension :pretty_table
|
4
15
|
|
5
16
|
module Sequel
|
6
17
|
extension :_pretty_table
|
@@ -21,17 +21,15 @@
|
|
21
21
|
# desirable in this area, but for backwards compatibility, the
|
22
22
|
# defaults won't be changed until the next major release.
|
23
23
|
#
|
24
|
-
#
|
25
|
-
# Sequel::QueryLiterals module available. You can extend specific
|
26
|
-
# datasets with this module:
|
24
|
+
# You can load this extension into specific datasets:
|
27
25
|
#
|
28
26
|
# ds = DB[:table]
|
29
|
-
# ds.
|
27
|
+
# ds.extension(:query_literals)
|
30
28
|
#
|
31
|
-
#
|
29
|
+
# Or you can load it into all of a database's datasets, which
|
32
30
|
# is probably the desired behavior if you are using this extension:
|
33
31
|
#
|
34
|
-
# DB.
|
32
|
+
# DB.extension(:query_literals)
|
35
33
|
|
36
34
|
module Sequel
|
37
35
|
# The QueryLiterals module can be used to make select, group, and
|
@@ -76,4 +74,6 @@ module Sequel
|
|
76
74
|
end
|
77
75
|
end
|
78
76
|
end
|
77
|
+
|
78
|
+
Dataset.register_extension(:query_literals, QueryLiterals)
|
79
79
|
end
|
@@ -3,6 +3,10 @@
|
|
3
3
|
# database (which can be the same type or a different type than
|
4
4
|
# the current database). The main interface is through
|
5
5
|
# Sequel::Database#dump_schema_migration.
|
6
|
+
#
|
7
|
+
# To load the extension:
|
8
|
+
#
|
9
|
+
# Sequel.extension :schema_dumper
|
6
10
|
|
7
11
|
module Sequel
|
8
12
|
class Database
|
@@ -17,7 +21,7 @@ module Sequel
|
|
17
21
|
ts = tables(options)
|
18
22
|
<<END_MIG
|
19
23
|
Sequel.migration do
|
20
|
-
|
24
|
+
change do
|
21
25
|
#{ts.sort_by{|t| t.to_s}.map{|t| dump_table_foreign_keys(t)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
22
26
|
end
|
23
27
|
end
|
@@ -26,38 +30,35 @@ END_MIG
|
|
26
30
|
|
27
31
|
# Dump indexes for all tables as a migration. This complements
|
28
32
|
# the :indexes=>false option to dump_schema_migration. Options:
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
33
|
+
# :same_db :: Create a dump for the same database type, so
|
34
|
+
# don't ignore errors if the index statements fail.
|
35
|
+
# :index_names :: If set to false, don't record names of indexes. If
|
36
|
+
# set to :namespace, prepend the table name to the index name if the
|
37
|
+
# database does not use a global index namespace.
|
34
38
|
def dump_indexes_migration(options={})
|
35
39
|
ts = tables(options)
|
36
40
|
<<END_MIG
|
37
41
|
Sequel.migration do
|
38
|
-
|
42
|
+
change do
|
39
43
|
#{ts.sort_by{|t| t.to_s}.map{|t| dump_table_indexes(t, :add_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
40
44
|
end
|
41
|
-
|
42
|
-
down do
|
43
|
-
#{ts.sort_by{|t| t.to_s}.reverse.map{|t| dump_table_indexes(t, :drop_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
44
|
-
end
|
45
45
|
end
|
46
46
|
END_MIG
|
47
47
|
end
|
48
48
|
|
49
49
|
# Return a string that contains a Sequel::Migration subclass that when
|
50
50
|
# run would recreate the database structure. Options:
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
51
|
+
# :same_db :: Don't attempt to translate database types to ruby types.
|
52
|
+
# If this isn't set to true, all database types will be translated to
|
53
|
+
# ruby types, but there is no guarantee that the migration generated
|
54
|
+
# will yield the same type. Without this set, types that aren't
|
55
|
+
# recognized will be translated to a string-like type.
|
56
|
+
# :foreign_keys :: If set to false, don't dump foreign_keys (they can be
|
57
|
+
# added later via #dump_foreign_key_migration)
|
58
|
+
# :indexes :: If set to false, don't dump indexes (they can be added
|
59
|
+
# later via #dump_index_migration).
|
60
|
+
# :index_names :: If set to false, don't record names of indexes. If
|
61
|
+
# set to :namespace, prepend the table name to the index name.
|
61
62
|
def dump_schema_migration(options={})
|
62
63
|
options = options.dup
|
63
64
|
if options[:indexes] == false && !options.has_key?(:foreign_keys)
|
@@ -78,13 +79,9 @@ END_MIG
|
|
78
79
|
|
79
80
|
<<END_MIG
|
80
81
|
Sequel.migration do
|
81
|
-
|
82
|
+
change do
|
82
83
|
#{ts.map{|t| dump_table_schema(t, options)}.join("\n\n").gsub(/^/o, ' ')}#{"\n \n" if skipped_fks}#{skipped_fks}
|
83
84
|
end
|
84
|
-
|
85
|
-
down do
|
86
|
-
drop_table(#{ts.reverse.inspect[1...-1]})
|
87
|
-
end
|
88
85
|
end
|
89
86
|
END_MIG
|
90
87
|
end
|
@@ -113,16 +110,15 @@ END_MIG
|
|
113
110
|
end
|
114
111
|
end
|
115
112
|
|
116
|
-
#
|
117
|
-
|
118
|
-
def column_schema_to_generator_opts(name, schema, options)
|
113
|
+
# Recreate the column in the passed Schema::Generator from the given name and parsed database schema.
|
114
|
+
def recreate_column(name, schema, gen, options)
|
119
115
|
if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
|
120
116
|
type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
121
117
|
[:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
|
122
118
|
if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
|
123
|
-
|
119
|
+
gen.primary_key(name)
|
124
120
|
else
|
125
|
-
|
121
|
+
gen.primary_key(name, type_hash)
|
126
122
|
end
|
127
123
|
else
|
128
124
|
col_opts = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
@@ -137,9 +133,14 @@ END_MIG
|
|
137
133
|
col_opts[:null] = false if schema[:allow_null] == false
|
138
134
|
if table = schema[:table]
|
139
135
|
[:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
|
140
|
-
[:
|
136
|
+
col_opts[:type] = type unless type == Integer || type == 'integer'
|
137
|
+
gen.foreign_key(name, table, col_opts)
|
141
138
|
else
|
142
|
-
|
139
|
+
gen.column(name, type, col_opts)
|
140
|
+
if (type == Integer || type == Bignum) && schema[:db_type] =~ / unsigned\z/io
|
141
|
+
Sequel.extension :eval_inspect
|
142
|
+
gen.check(Sequel::SQL::Identifier.new(name) >= 0)
|
143
|
+
end
|
143
144
|
end
|
144
145
|
end
|
145
146
|
end
|
@@ -197,7 +198,7 @@ END_MIG
|
|
197
198
|
# string that would add the foreign keys if run in a migration.
|
198
199
|
def dump_add_fk_constraints(table, fks)
|
199
200
|
sfks = "alter_table(#{table.inspect}) do\n"
|
200
|
-
sfks <<
|
201
|
+
sfks << create_table_generator do
|
201
202
|
fks.sort_by{|fk| fk[:columns].map{|c| c.to_s}}.each do |fk|
|
202
203
|
foreign_key fk[:columns], fk
|
203
204
|
end
|
@@ -229,7 +230,7 @@ END_MIG
|
|
229
230
|
s = schema(table).dup
|
230
231
|
pks = s.find_all{|x| x.last[:primary_key] == true}.map{|x| x.first}
|
231
232
|
options = options.merge(:single_pk=>true) if pks.length == 1
|
232
|
-
m = method(:
|
233
|
+
m = method(:recreate_column)
|
233
234
|
im = method(:index_to_generator_opts)
|
234
235
|
|
235
236
|
if options[:indexes] != false
|
@@ -269,8 +270,8 @@ END_MIG
|
|
269
270
|
end
|
270
271
|
end
|
271
272
|
|
272
|
-
|
273
|
-
s.each{|name, info|
|
273
|
+
create_table_generator do
|
274
|
+
s.each{|name, info| m.call(name, info, self, options)}
|
274
275
|
primary_key(pks) if !@primary_key && pks.length > 0
|
275
276
|
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts, options))} if indexes
|
276
277
|
composite_fks.each{|fk| send(:foreign_key, fk[:columns], fk)} if composite_fks
|
@@ -286,7 +287,7 @@ END_MIG
|
|
286
287
|
return ''
|
287
288
|
end
|
288
289
|
im = method(:index_to_generator_opts)
|
289
|
-
gen =
|
290
|
+
gen = create_table_generator do
|
290
291
|
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts, options))}
|
291
292
|
end
|
292
293
|
gen.dump_indexes(meth=>table, :ignore_errors=>!options[:same_db])
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# The select_remove extension adds Sequel::Dataset#select_remove for removing existing selected
|
2
2
|
# columns from a dataset. It's not part of Sequel core as it is rarely needed and has
|
3
3
|
# some corner cases where it can't work correctly.
|
4
|
+
#
|
5
|
+
# To load the extension:
|
6
|
+
#
|
7
|
+
# Sequel.extension :select_remove
|
4
8
|
|
5
9
|
module Sequel
|
6
10
|
class Dataset
|
@@ -4,8 +4,7 @@
|
|
4
4
|
#
|
5
5
|
# First, you need to enable it on the database object:
|
6
6
|
#
|
7
|
-
#
|
8
|
-
# DB.extend Sequel::ServerBlock
|
7
|
+
# DB.extension :server_block
|
9
8
|
#
|
10
9
|
# Then you can call with_server:
|
11
10
|
#
|
@@ -136,4 +135,6 @@ module Sequel
|
|
136
135
|
end
|
137
136
|
end
|
138
137
|
end
|
138
|
+
|
139
|
+
Database.register_extension(:server_block, ServerBlock)
|
139
140
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# The split_array_nil extension overrides Sequel's default handling of
|
2
|
+
# IN/NOT IN with arrays of values to do specific nil checking. For example,
|
3
|
+
#
|
4
|
+
# ds = DB[:table].where(:column=>[1, nil])
|
5
|
+
#
|
6
|
+
# By default, that produces the following SQL:
|
7
|
+
#
|
8
|
+
# SELECT * FROM table WHERE (column IN (1, NULL))
|
9
|
+
#
|
10
|
+
# However, because NULL = NULL is not true in SQL (it is NULL), this
|
11
|
+
# will not return rows in the table where the column is NULL. This
|
12
|
+
# extension allows for an alternative behavior more similar to ruby,
|
13
|
+
# which will return rows in the table where the column is NULL, using
|
14
|
+
# a query like:
|
15
|
+
#
|
16
|
+
# SELECT * FROM table WHERE ((column IN (1)) OR (column IS NULL)))
|
17
|
+
#
|
18
|
+
# Similarly, for NOT IN queries:
|
19
|
+
#
|
20
|
+
# ds = DB[:table].exclude(:column=>[1, nil])
|
21
|
+
# # Default:
|
22
|
+
# # SELECT * FROM table WHERE (column NOT IN (1, NULL))
|
23
|
+
# # with split_array_nils extension:
|
24
|
+
# # SELECT * FROM table WHERE ((column NOT IN (1)) AND (column IS NOT NULL)))
|
25
|
+
#
|
26
|
+
# To use this extension with a single dataset:
|
27
|
+
#
|
28
|
+
# ds = ds.extension(:split_array_nil)
|
29
|
+
#
|
30
|
+
# To use this extension for all of a database's datasets:
|
31
|
+
#
|
32
|
+
# DB.extension(:split_array_nil)
|
33
|
+
|
34
|
+
module Sequel
|
35
|
+
class Dataset
|
36
|
+
module SplitArrayNil
|
37
|
+
# Over the IN/NOT IN handling with an array of values where one of the
|
38
|
+
# values in the array is nil, by removing nils from the array of values,
|
39
|
+
# and using a separate OR IS NULL clause for IN or AND IS NOT NULL clause
|
40
|
+
# for NOT IN.
|
41
|
+
def complex_expression_sql_append(sql, op, args)
|
42
|
+
case op
|
43
|
+
when :IN, :"NOT IN"
|
44
|
+
vals = args.at(1)
|
45
|
+
if vals.is_a?(Array) && vals.any?{|v| v.nil?}
|
46
|
+
cols = args.at(0)
|
47
|
+
vals = vals.compact
|
48
|
+
c = Sequel::SQL::BooleanExpression
|
49
|
+
if op == :IN
|
50
|
+
literal_append(sql, c.new(:OR, c.new(:IN, cols, vals), c.new(:IS, cols, nil)))
|
51
|
+
else
|
52
|
+
literal_append(sql, c.new(:AND, c.new(:"NOT IN", cols, vals), c.new(:"IS NOT", cols, nil)))
|
53
|
+
end
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Dataset.register_extension(:split_array_nil, Dataset::SplitArrayNil)
|
65
|
+
end
|