sequel 3.36.1 → 3.37.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.
- 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
|