sequel 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
@@ -223,6 +223,7 @@ module Sequel
|
|
223
223
|
@operations << {:op => :add_constraint, :name => name, :type => :check, :check => block || args}
|
224
224
|
end
|
225
225
|
|
226
|
+
# Add a unique constraint to the given column(s)
|
226
227
|
def add_unique_constraint(columns, opts = {})
|
227
228
|
@operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge(opts)
|
228
229
|
end
|
@@ -16,9 +16,18 @@ module Sequel
|
|
16
16
|
# DB.add_index :posts, :title
|
17
17
|
# DB.add_index :posts, [:author, :title], :unique => true
|
18
18
|
#
|
19
|
+
#
|
20
|
+
# Options:
|
21
|
+
# * :ignore_errors - Ignore any DatabaseErrors that are raised
|
22
|
+
#
|
19
23
|
# See alter_table.
|
20
|
-
def add_index(table,
|
21
|
-
|
24
|
+
def add_index(table, columns, options={})
|
25
|
+
e = options[:ignore_errors]
|
26
|
+
begin
|
27
|
+
alter_table(table){add_index(columns, options)}
|
28
|
+
rescue DatabaseError
|
29
|
+
raise unless e
|
30
|
+
end
|
22
31
|
end
|
23
32
|
|
24
33
|
# Alters the given table with the specified block. Example:
|
@@ -55,13 +64,14 @@ module Sequel
|
|
55
64
|
#
|
56
65
|
# Options:
|
57
66
|
# * :temp - Create the table as a temporary table.
|
67
|
+
# * :ignore_index_errors - Ignore any errors when creating indexes.
|
58
68
|
#
|
59
69
|
# See Schema::Generator.
|
60
70
|
def create_table(name, options={}, &block)
|
61
71
|
options = {:generator=>options} if options.is_a?(Schema::Generator)
|
62
72
|
generator = options[:generator] || Schema::Generator.new(self, &block)
|
63
|
-
|
64
|
-
|
73
|
+
create_table_from_generator(name, generator, options)
|
74
|
+
create_table_indexes_from_generator(name, generator, options)
|
65
75
|
end
|
66
76
|
|
67
77
|
# Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
|
@@ -70,6 +80,11 @@ module Sequel
|
|
70
80
|
create_table(name, options, &block)
|
71
81
|
end
|
72
82
|
|
83
|
+
# Creates the table unless the table already exists
|
84
|
+
def create_table?(name, options={}, &block)
|
85
|
+
create_table(name, options, &block) unless table_exists?(name)
|
86
|
+
end
|
87
|
+
|
73
88
|
# Creates a view, replacing it if it already exists:
|
74
89
|
#
|
75
90
|
# DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
|
@@ -165,5 +180,24 @@ module Sequel
|
|
165
180
|
def set_column_type(table, *args)
|
166
181
|
alter_table(table) {set_column_type(*args)}
|
167
182
|
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
# Execute the create table statements using the generator.
|
187
|
+
def create_table_from_generator(name, generator, options)
|
188
|
+
execute_ddl(create_table_sql(name, generator, options))
|
189
|
+
end
|
190
|
+
|
191
|
+
# Execute the create index statements using the generator.
|
192
|
+
def create_table_indexes_from_generator(name, generator, options)
|
193
|
+
e = options[:ignore_index_errors]
|
194
|
+
index_sql_list(name, generator.indexes).each do |sql|
|
195
|
+
begin
|
196
|
+
execute_ddl(sql)
|
197
|
+
rescue DatabaseError
|
198
|
+
raise unless e
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
168
202
|
end
|
169
203
|
end
|
data/lib/sequel/dataset.rb
CHANGED
@@ -259,6 +259,12 @@ module Sequel
|
|
259
259
|
!(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
|
260
260
|
end
|
261
261
|
|
262
|
+
# Whether this dataset is a simple SELECT * FROM table.
|
263
|
+
def simple_select_all?
|
264
|
+
o = @opts.reject{|k,v| v.nil?}
|
265
|
+
o.length == 1 && o[:from] && o[:from].length == 1
|
266
|
+
end
|
267
|
+
|
262
268
|
private
|
263
269
|
|
264
270
|
# Set the server to use to :default unless it is already set in the passed opts
|
@@ -11,7 +11,7 @@ module Sequel
|
|
11
11
|
#
|
12
12
|
# ds[:id=>1] => {:id=1}
|
13
13
|
def [](*conditions)
|
14
|
-
raise(Error, ARRAY_ACCESS_ERROR_MSG) if (conditions.length == 1 and conditions.is_a?(Integer)) or conditions.length == 0
|
14
|
+
raise(Error, ARRAY_ACCESS_ERROR_MSG) if (conditions.length == 1 and conditions.first.is_a?(Integer)) or conditions.length == 0
|
15
15
|
first(*conditions)
|
16
16
|
end
|
17
17
|
|
@@ -109,7 +109,7 @@ module Sequel
|
|
109
109
|
# # this will commit every 50 records
|
110
110
|
# dataset.import([:x, :y], [[1, 2], [3, 4], ...], :slice => 50)
|
111
111
|
def import(columns, values, opts={})
|
112
|
-
return @db.transaction{execute_dui("
|
112
|
+
return @db.transaction{execute_dui("#{insert_sql_base}#{quote_schema_table(@opts[:from].first)} (#{identifier_list(columns)}) VALUES #{literal(values)}")} if values.is_a?(Dataset)
|
113
113
|
|
114
114
|
return if values.empty?
|
115
115
|
raise(Error, IMPORT_ERROR_MSG) if columns.empty?
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -69,7 +69,7 @@ module Sequel
|
|
69
69
|
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
70
70
|
|
71
71
|
# Join the table early in order to avoid cloning the dataset twice
|
72
|
-
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
|
72
|
+
ds = join_table(options[:join_type] || :left_outer, dataset.simple_select_all? ? table : dataset, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
|
73
73
|
opts = ds.opts
|
74
74
|
|
75
75
|
# Whether to include the table in the result set
|
@@ -167,7 +167,7 @@ module Sequel
|
|
167
167
|
# #set_graph_aliases.
|
168
168
|
def add_graph_aliases(graph_aliases)
|
169
169
|
ds = select_more(*graph_alias_columns(graph_aliases))
|
170
|
-
ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || {}).merge(graph_aliases)
|
170
|
+
ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || ds.opts[:graph][:column_aliases] || {}).merge(graph_aliases)
|
171
171
|
ds
|
172
172
|
end
|
173
173
|
|
@@ -90,14 +90,9 @@ module Sequel
|
|
90
90
|
# Changes the values of symbols if they start with $ and
|
91
91
|
# prepared_args is present. If so, they are considered placeholders,
|
92
92
|
# and they are substituted using prepared_arg.
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
if match = PLACEHOLDER_RE.match(v.to_s) and @prepared_args
|
97
|
-
super(prepared_arg(match[1].to_sym))
|
98
|
-
else
|
99
|
-
super
|
100
|
-
end
|
93
|
+
def literal_symbol(v)
|
94
|
+
if match = PLACEHOLDER_RE.match(v.to_s) and @prepared_args
|
95
|
+
literal(prepared_arg(match[1].to_sym))
|
101
96
|
else
|
102
97
|
super
|
103
98
|
end
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -7,10 +7,12 @@ module Sequel
|
|
7
7
|
COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
|
8
8
|
COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
|
9
9
|
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
|
10
|
+
INSERT_SQL_BASE="INSERT INTO ".freeze
|
10
11
|
IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
|
11
12
|
IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
|
12
13
|
N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
|
13
14
|
NULL = "NULL".freeze
|
15
|
+
QUALIFY_KEYS = [:select, :where, :having, :order, :group]
|
14
16
|
QUESTION_MARK = '?'.freeze
|
15
17
|
STOCK_COUNT_OPTS = {:select => [SQL::AliasedExpression.new(LiteralString.new("COUNT(*)").freeze, :count)], :order => nil}.freeze
|
16
18
|
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
|
@@ -317,7 +319,7 @@ module Sequel
|
|
317
319
|
if values.empty?
|
318
320
|
insert_default_values_sql
|
319
321
|
else
|
320
|
-
"
|
322
|
+
"#{insert_sql_base}#{from} VALUES #{literal(values)}"
|
321
323
|
end
|
322
324
|
when Hash
|
323
325
|
values = @opts[:defaults].merge(values) if @opts[:defaults]
|
@@ -330,10 +332,10 @@ module Sequel
|
|
330
332
|
fl << literal(String === k ? k.to_sym : k)
|
331
333
|
vl << literal(v)
|
332
334
|
end
|
333
|
-
"
|
335
|
+
"#{insert_sql_base}#{from} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
|
334
336
|
end
|
335
337
|
when Dataset
|
336
|
-
"
|
338
|
+
"#{insert_sql_base}#{from} #{literal(values)}"
|
337
339
|
end
|
338
340
|
end
|
339
341
|
|
@@ -532,7 +534,7 @@ module Sequel
|
|
532
534
|
# This method should be overridden by descendants if the support
|
533
535
|
# inserting multiple records in a single SQL statement.
|
534
536
|
def multi_insert_sql(columns, values)
|
535
|
-
s = "
|
537
|
+
s = "#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES "
|
536
538
|
values.map{|r| s + literal(r)}
|
537
539
|
end
|
538
540
|
|
@@ -595,6 +597,30 @@ module Sequel
|
|
595
597
|
[qcr.table, qcr.column].map{|x| [SQL::QualifiedIdentifier, SQL::Identifier, Symbol].any?{|c| x.is_a?(c)} ? literal(x) : quote_identifier(x)}.join('.')
|
596
598
|
end
|
597
599
|
|
600
|
+
# Return a copy of the dataset with unqualified identifiers in the
|
601
|
+
# SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
|
602
|
+
# given table. If no columns are currently selected, select all
|
603
|
+
# columns of the given table.
|
604
|
+
def qualify_to(table)
|
605
|
+
o = @opts
|
606
|
+
return clone if o[:sql]
|
607
|
+
h = {}
|
608
|
+
(o.keys & QUALIFY_KEYS).each do |k|
|
609
|
+
h[k] = qualified_expression(o[k], table)
|
610
|
+
end
|
611
|
+
h[:select] = [SQL::ColumnAll.new(table)] if !o[:select] || o[:select].empty?
|
612
|
+
clone(h)
|
613
|
+
end
|
614
|
+
|
615
|
+
# Qualify the dataset to its current first source. This is useful
|
616
|
+
# if you have unqualified identifiers in the query that all refer to
|
617
|
+
# the first source, and you want to join to another table which
|
618
|
+
# has columns with the same name as columns in the current dataset.
|
619
|
+
# See qualify_to.
|
620
|
+
def qualify_to_first_source
|
621
|
+
qualify_to(first_source)
|
622
|
+
end
|
623
|
+
|
598
624
|
# Adds quoting to identifiers (columns and tables). If identifiers are not
|
599
625
|
# being quoted, returns name as a string. If identifiers are being quoted
|
600
626
|
# quote the name with quoted_identifier.
|
@@ -654,7 +680,11 @@ module Sequel
|
|
654
680
|
# dataset.select{|o| o.a, o.sum(:b)} # SELECT a, sum(b) FROM items
|
655
681
|
def select(*columns, &block)
|
656
682
|
columns += Array(virtual_row_block_call(block)) if block
|
657
|
-
|
683
|
+
m = []
|
684
|
+
columns.map do |i|
|
685
|
+
i.is_a?(Hash) ? m.concat(i.map{|k, v| SQL::AliasedExpression.new(k,v)}) : m << i
|
686
|
+
end
|
687
|
+
clone(:select => m)
|
658
688
|
end
|
659
689
|
|
660
690
|
# Returns a copy of the dataset selecting the wildcard.
|
@@ -690,7 +720,7 @@ module Sequel
|
|
690
720
|
|
691
721
|
# SQL fragment for specifying subscripts (SQL arrays)
|
692
722
|
def subscript_sql(s)
|
693
|
-
"#{literal(s.f)}[#{s.sub
|
723
|
+
"#{literal(s.f)}[#{expression_list(s.sub)}]"
|
694
724
|
end
|
695
725
|
|
696
726
|
# Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
|
@@ -778,6 +808,12 @@ module Sequel
|
|
778
808
|
|
779
809
|
protected
|
780
810
|
|
811
|
+
# Return a from_self dataset if an order or limit is specified, so it works as expected
|
812
|
+
# with UNION, EXCEPT, and INTERSECT clauses.
|
813
|
+
def compound_from_self
|
814
|
+
(@opts[:limit] || @opts[:order]) ? from_self : self
|
815
|
+
end
|
816
|
+
|
781
817
|
# Returns a table reference for use in the FROM clause. Returns an SQL subquery
|
782
818
|
# frgament with an optional table alias.
|
783
819
|
def to_table_reference(table_alias=nil)
|
@@ -803,21 +839,14 @@ module Sequel
|
|
803
839
|
# Converts an array of column names into a comma seperated string of
|
804
840
|
# column names. If the array is empty, a wildcard (*) is returned.
|
805
841
|
def column_list(columns)
|
806
|
-
|
807
|
-
WILDCARD
|
808
|
-
else
|
809
|
-
m = columns.map do |i|
|
810
|
-
i.is_a?(Hash) ? i.map{|k, v| as_sql(literal(k), v)} : literal(i)
|
811
|
-
end
|
812
|
-
m.join(COMMA_SEPARATOR)
|
813
|
-
end
|
842
|
+
(columns.nil? || columns.empty?) ? WILDCARD : expression_list(columns)
|
814
843
|
end
|
815
844
|
|
816
845
|
# Add the dataset to the list of compounds
|
817
846
|
def compound_clone(type, dataset, all)
|
818
|
-
clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset, all]])
|
847
|
+
compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, all]]).from_self
|
819
848
|
end
|
820
|
-
|
849
|
+
|
821
850
|
# Converts an array of expressions into a comma separated string of
|
822
851
|
# expressions.
|
823
852
|
def expression_list(columns)
|
@@ -863,9 +892,14 @@ module Sequel
|
|
863
892
|
columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
|
864
893
|
end
|
865
894
|
|
895
|
+
# SQL statement for the beginning of an INSERT statement
|
896
|
+
def insert_sql_base
|
897
|
+
INSERT_SQL_BASE
|
898
|
+
end
|
899
|
+
|
866
900
|
# SQL statement for formatting an insert statement with default values
|
867
901
|
def insert_default_values_sql
|
868
|
-
"
|
902
|
+
"#{insert_sql_base}#{source_list(@opts[:from])} DEFAULT VALUES"
|
869
903
|
end
|
870
904
|
|
871
905
|
# Inverts the given order by breaking it into a list of column references
|
@@ -1003,6 +1037,47 @@ module Sequel
|
|
1003
1037
|
column
|
1004
1038
|
end
|
1005
1039
|
end
|
1040
|
+
|
1041
|
+
# Qualify the given expression e to the given table.
|
1042
|
+
def qualified_expression(e, table)
|
1043
|
+
case e
|
1044
|
+
when Symbol
|
1045
|
+
t, column, aliaz = split_symbol(e)
|
1046
|
+
if t
|
1047
|
+
e
|
1048
|
+
elsif aliaz
|
1049
|
+
SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table, SQL::Identifier.new(column)), aliaz)
|
1050
|
+
else
|
1051
|
+
SQL::QualifiedIdentifier.new(table, e)
|
1052
|
+
end
|
1053
|
+
when Array
|
1054
|
+
e.map{|a| qualified_expression(a, table)}
|
1055
|
+
when Hash
|
1056
|
+
h = {}
|
1057
|
+
e.each{|k,v| h[qualified_expression(k, table)] = qualified_expression(v, table)}
|
1058
|
+
h
|
1059
|
+
when SQL::Identifier
|
1060
|
+
SQL::QualifiedIdentifier.new(table, e)
|
1061
|
+
when SQL::OrderedExpression
|
1062
|
+
SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending)
|
1063
|
+
when SQL::AliasedExpression
|
1064
|
+
SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
|
1065
|
+
when SQL::CaseExpression
|
1066
|
+
SQL::CaseExpression.new(qualified_expression(e.conditions, table), qualified_expression(e.default, table), qualified_expression(e.expression, table))
|
1067
|
+
when SQL::Cast
|
1068
|
+
SQL::Cast.new(qualified_expression(e.expr, table), e.type)
|
1069
|
+
when SQL::Function
|
1070
|
+
SQL::Function.new(e.f, *qualified_expression(e.args, table))
|
1071
|
+
when SQL::ComplexExpression
|
1072
|
+
SQL::ComplexExpression.new(e.op, *qualified_expression(e.args, table))
|
1073
|
+
when SQL::SQLArray
|
1074
|
+
SQL::SQLArray.new(qualified_expression(e.array, table))
|
1075
|
+
when SQL::Subscript
|
1076
|
+
SQL::Subscript.new(qualified_expression(e.f, table), qualified_expression(e.sub, table))
|
1077
|
+
else
|
1078
|
+
e
|
1079
|
+
end
|
1080
|
+
end
|
1006
1081
|
|
1007
1082
|
# The order of methods to call to build the SELECT SQL statement
|
1008
1083
|
def select_clause_order
|
@@ -1028,8 +1103,7 @@ module Sequel
|
|
1028
1103
|
return unless @opts[:compounds]
|
1029
1104
|
@opts[:compounds].each do |type, dataset, all|
|
1030
1105
|
compound_sql = subselect_sql(dataset)
|
1031
|
-
|
1032
|
-
sql.replace("#{sql} #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}")
|
1106
|
+
sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}"
|
1033
1107
|
end
|
1034
1108
|
end
|
1035
1109
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# The blank extension adds the blank? method to all objects (e.g. Object#blank?).
|
2
|
+
|
1
3
|
class FalseClass
|
2
4
|
# false is always blank
|
3
5
|
def blank?
|
@@ -5,7 +7,6 @@ class FalseClass
|
|
5
7
|
end
|
6
8
|
end
|
7
9
|
|
8
|
-
# Helpers from Metaid and a bit more
|
9
10
|
class Object
|
10
11
|
# Objects are blank if they respond true to empty?
|
11
12
|
def blank?
|
@@ -1,6 +1,7 @@
|
|
1
|
-
#
|
2
|
-
# words from singular to plural,class names to table names, modularized class
|
3
|
-
# names to ones without, and class names to foreign keys.
|
1
|
+
# The inflector extension adds inflection instance methods to String, which allows the easy transformation of
|
2
|
+
# words from singular to plural, class names to table names, modularized class
|
3
|
+
# names to ones without, and class names to foreign keys. It exists for
|
4
|
+
# backwards compatibility to legacy Sequel code.
|
4
5
|
|
5
6
|
class String
|
6
7
|
# This module acts as a singleton returned/yielded by String.inflections,
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# Adds the Sequel::Migration and Sequel::Migrator classes, which allow
|
2
|
+
# the user to easily group schema changes and migrate the database
|
3
|
+
# to a newer version or revert to a previous version.
|
4
|
+
|
1
5
|
module Sequel
|
2
6
|
# The Migration class describes a database migration that can be reversed.
|
3
7
|
# The migration looks very similar to ActiveRecord (Rails) migrations, e.g.:
|
@@ -128,6 +132,7 @@ module Sequel
|
|
128
132
|
# Sequel::Migrator.apply(DB, '.', 5, 1)
|
129
133
|
module Migrator
|
130
134
|
MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/.freeze
|
135
|
+
MIGRATION_SPLITTER = '_'.freeze
|
131
136
|
|
132
137
|
# Migrates the supplied database in the specified directory from the
|
133
138
|
# current version to the target version. If no current version is
|
@@ -162,7 +167,7 @@ module Sequel
|
|
162
167
|
# Returns the latest version available in the specified directory.
|
163
168
|
def self.latest_migration_version(directory)
|
164
169
|
l = migration_files(directory).last
|
165
|
-
l ? File.basename(l)
|
170
|
+
l ? migration_version_from_file(File.basename(l)) : nil
|
166
171
|
end
|
167
172
|
|
168
173
|
# Returns a list of migration classes filtered for the migration range and
|
@@ -190,7 +195,7 @@ module Sequel
|
|
190
195
|
def self.migration_files(directory, range = nil)
|
191
196
|
files = []
|
192
197
|
Dir.new(directory).each do |file|
|
193
|
-
files[file
|
198
|
+
files[migration_version_from_file(file)] = File.join(directory, file) if MIGRATION_FILE_PATTERN.match(file)
|
194
199
|
end
|
195
200
|
filtered = range ? files[range] : files
|
196
201
|
filtered ? filtered.compact : []
|
@@ -208,5 +213,11 @@ module Sequel
|
|
208
213
|
dataset = schema_info_dataset(db)
|
209
214
|
dataset.send(dataset.first ? :update : :<<, :version => version)
|
210
215
|
end
|
216
|
+
|
217
|
+
# Return the integer migration version based on the filename.
|
218
|
+
def self.migration_version_from_file(filename)
|
219
|
+
filename.split(MIGRATION_SPLITTER, 2).first.to_i
|
220
|
+
end
|
221
|
+
private_class_method :migration_version_from_file
|
211
222
|
end
|
212
223
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# The pagination extension adds the Sequel::Dataset#paginate and #each_page methods,
|
2
|
+
# which return paginated (limited and offset) datasets with some helpful methods
|
3
|
+
# that make creating a paginated display easier.
|
4
|
+
|
1
5
|
module Sequel
|
2
6
|
class Dataset
|
3
7
|
# Returns a paginated dataset. The returned dataset is limited to
|