sequel 3.21.0 → 3.28.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 +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: User Methods relating to SQL Creation
|
|
4
|
+
# :section: 3 - User Methods relating to SQL Creation
|
|
5
5
|
# These are methods you can call to see what SQL will be generated by the dataset.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -101,7 +101,7 @@ module Sequel
|
|
|
101
101
|
when Array
|
|
102
102
|
literal_array(v)
|
|
103
103
|
when Time
|
|
104
|
-
literal_time(v)
|
|
104
|
+
v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v)
|
|
105
105
|
when DateTime
|
|
106
106
|
literal_datetime(v)
|
|
107
107
|
when Date
|
|
@@ -163,7 +163,7 @@ module Sequel
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
# ---------------------
|
|
166
|
-
# :section: Internal Methods relating to SQL Creation
|
|
166
|
+
# :section: 9 - Internal Methods relating to SQL Creation
|
|
167
167
|
# These methods, while public, are not designed to be used directly by the end user.
|
|
168
168
|
# ---------------------
|
|
169
169
|
|
|
@@ -212,7 +212,11 @@ module Sequel
|
|
|
212
212
|
|
|
213
213
|
# SQL fragment for BooleanConstants
|
|
214
214
|
def boolean_constant_sql(constant)
|
|
215
|
-
|
|
215
|
+
if (constant == true || constant == false) && !supports_where_true?
|
|
216
|
+
constant == true ? '(1 = 1)' : '(1 = 0)'
|
|
217
|
+
else
|
|
218
|
+
literal(constant)
|
|
219
|
+
end
|
|
216
220
|
end
|
|
217
221
|
|
|
218
222
|
# SQL fragment for CaseExpression
|
|
@@ -269,6 +273,7 @@ module Sequel
|
|
|
269
273
|
literal(op == :IN ? expr : ~expr)
|
|
270
274
|
else
|
|
271
275
|
old_vals = vals
|
|
276
|
+
vals = vals.naked if vals.is_a?(Sequel::Dataset)
|
|
272
277
|
vals = vals.to_a
|
|
273
278
|
val_cols = old_vals.columns
|
|
274
279
|
complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
|
|
@@ -300,6 +305,8 @@ module Sequel
|
|
|
300
305
|
literal(args.at(0))
|
|
301
306
|
when :'B~'
|
|
302
307
|
"~#{literal(args.at(0))}"
|
|
308
|
+
when :extract
|
|
309
|
+
"extract(#{args.at(0)} FROM #{literal(args.at(1))})"
|
|
303
310
|
else
|
|
304
311
|
raise(InvalidOperation, "invalid operator #{op}")
|
|
305
312
|
end
|
|
@@ -434,7 +441,7 @@ module Sequel
|
|
|
434
441
|
when :all
|
|
435
442
|
"ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
|
|
436
443
|
when :rows
|
|
437
|
-
"ROWS UNBOUNDED PRECEDING"
|
|
444
|
+
"ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
|
|
438
445
|
when String
|
|
439
446
|
opts[:frame]
|
|
440
447
|
else
|
|
@@ -636,6 +643,15 @@ module Sequel
|
|
|
636
643
|
end
|
|
637
644
|
end
|
|
638
645
|
|
|
646
|
+
# SQL fragment specifying the values to return.
|
|
647
|
+
def insert_returning_sql(sql)
|
|
648
|
+
if opts.has_key?(:returning)
|
|
649
|
+
sql << " RETURNING #{column_list(Array(opts[:returning]))}"
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
alias delete_returning_sql insert_returning_sql
|
|
653
|
+
alias update_returning_sql insert_returning_sql
|
|
654
|
+
|
|
639
655
|
# SQL fragment specifying a JOIN type, converts underscores to
|
|
640
656
|
# spaces and upcases.
|
|
641
657
|
def join_type_sql(join_type)
|
|
@@ -722,6 +738,11 @@ module Sequel
|
|
|
722
738
|
end
|
|
723
739
|
end
|
|
724
740
|
|
|
741
|
+
# SQL fragment for Sequel::SQLTime, containing just the time part
|
|
742
|
+
def literal_sqltime(v)
|
|
743
|
+
v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
|
|
744
|
+
end
|
|
745
|
+
|
|
725
746
|
# SQL fragment for String. Doubles \ and ' by default.
|
|
726
747
|
def literal_string(v)
|
|
727
748
|
"'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
|
|
@@ -774,59 +795,7 @@ module Sequel
|
|
|
774
795
|
|
|
775
796
|
# Qualify the given expression e to the given table.
|
|
776
797
|
def qualified_expression(e, table)
|
|
777
|
-
|
|
778
|
-
when Symbol
|
|
779
|
-
t, column, aliaz = split_symbol(e)
|
|
780
|
-
if t
|
|
781
|
-
e
|
|
782
|
-
elsif aliaz
|
|
783
|
-
SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table, SQL::Identifier.new(column)), aliaz)
|
|
784
|
-
else
|
|
785
|
-
SQL::QualifiedIdentifier.new(table, e)
|
|
786
|
-
end
|
|
787
|
-
when Array
|
|
788
|
-
e.map{|a| qualified_expression(a, table)}
|
|
789
|
-
when Hash
|
|
790
|
-
h = {}
|
|
791
|
-
e.each{|k,v| h[qualified_expression(k, table)] = qualified_expression(v, table)}
|
|
792
|
-
h
|
|
793
|
-
when SQL::Identifier
|
|
794
|
-
SQL::QualifiedIdentifier.new(table, e)
|
|
795
|
-
when SQL::OrderedExpression
|
|
796
|
-
SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending, :nulls=>e.nulls)
|
|
797
|
-
when SQL::AliasedExpression
|
|
798
|
-
SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
|
|
799
|
-
when SQL::CaseExpression
|
|
800
|
-
args = [qualified_expression(e.conditions, table), qualified_expression(e.default, table)]
|
|
801
|
-
args << qualified_expression(e.expression, table) if e.expression?
|
|
802
|
-
SQL::CaseExpression.new(*args)
|
|
803
|
-
when SQL::Cast
|
|
804
|
-
SQL::Cast.new(qualified_expression(e.expr, table), e.type)
|
|
805
|
-
when SQL::Function
|
|
806
|
-
SQL::Function.new(e.f, *qualified_expression(e.args, table))
|
|
807
|
-
when SQL::ComplexExpression
|
|
808
|
-
SQL::ComplexExpression.new(e.op, *qualified_expression(e.args, table))
|
|
809
|
-
when SQL::Subscript
|
|
810
|
-
SQL::Subscript.new(qualified_expression(e.f, table), qualified_expression(e.sub, table))
|
|
811
|
-
when SQL::WindowFunction
|
|
812
|
-
SQL::WindowFunction.new(qualified_expression(e.function, table), qualified_expression(e.window, table))
|
|
813
|
-
when SQL::Window
|
|
814
|
-
o = e.opts.dup
|
|
815
|
-
o[:partition] = qualified_expression(o[:partition], table) if o[:partition]
|
|
816
|
-
o[:order] = qualified_expression(o[:order], table) if o[:order]
|
|
817
|
-
SQL::Window.new(o)
|
|
818
|
-
when SQL::PlaceholderLiteralString
|
|
819
|
-
args = if e.args.is_a?(Hash)
|
|
820
|
-
h = {}
|
|
821
|
-
e.args.each{|k,v| h[k] = qualified_expression(v, table)}
|
|
822
|
-
h
|
|
823
|
-
else
|
|
824
|
-
qualified_expression(e.args, table)
|
|
825
|
-
end
|
|
826
|
-
SQL::PlaceholderLiteralString.new(e.str, args, e.parens)
|
|
827
|
-
else
|
|
828
|
-
e
|
|
829
|
-
end
|
|
798
|
+
Qualifier.new(self, table).transform(e)
|
|
830
799
|
end
|
|
831
800
|
|
|
832
801
|
# The order of methods to call to build the SELECT SQL statement
|
|
@@ -914,6 +883,10 @@ module Sequel
|
|
|
914
883
|
return if !ws || ws.empty?
|
|
915
884
|
sql.replace("#{select_with_sql_base}#{ws.map{|w| "#{quote_identifier(w[:name])}#{"(#{argument_list(w[:args])})" if w[:args]} AS #{literal_dataset(w[:dataset])}"}.join(COMMA_SEPARATOR)} #{sql}")
|
|
916
885
|
end
|
|
886
|
+
alias delete_with_sql select_with_sql
|
|
887
|
+
alias insert_with_sql select_with_sql
|
|
888
|
+
alias update_with_sql select_with_sql
|
|
889
|
+
|
|
917
890
|
|
|
918
891
|
# The base keyword to use for the SQL WITH clause
|
|
919
892
|
def select_with_sql_base
|
data/lib/sequel/dataset.rb
CHANGED
|
@@ -26,6 +26,14 @@ module Sequel
|
|
|
26
26
|
extend Metaprogramming
|
|
27
27
|
include Metaprogramming
|
|
28
28
|
include Enumerable
|
|
29
|
+
include SQL::AliasMethods
|
|
30
|
+
include SQL::BooleanMethods
|
|
31
|
+
include SQL::CastMethods
|
|
32
|
+
include SQL::ComplexExpressionMethods
|
|
33
|
+
include SQL::InequalityMethods
|
|
34
|
+
include SQL::NumericMethods
|
|
35
|
+
include SQL::OrderMethods
|
|
36
|
+
include SQL::StringMethods
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
require(%w"query actions features graph prepared_statements misc mutation sql", 'dataset')
|
data/lib/sequel/exceptions.rb
CHANGED
|
@@ -44,6 +44,10 @@ module Sequel
|
|
|
44
44
|
# and won't reraise it.
|
|
45
45
|
class Rollback < Error ; end
|
|
46
46
|
|
|
47
|
+
# Exception that occurs when unbinding a dataset that has multiple different values
|
|
48
|
+
# for a given variable.
|
|
49
|
+
class UnbindDuplicate < Error; end
|
|
50
|
+
|
|
47
51
|
class Error
|
|
48
52
|
AdapterNotFound = Sequel::AdapterNotFound
|
|
49
53
|
InvalidOperation = Sequel::InvalidOperation
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# The columns_introspection extension attempts to introspect the
|
|
2
|
+
# selected columns for a dataset before issuing a query. If it
|
|
3
|
+
# thinks it can guess correctly at the columns the query will use,
|
|
4
|
+
# it will return the columns without issuing a database query.
|
|
5
|
+
# This method is not fool-proof, it's possible that some databases
|
|
6
|
+
# will use column names that Sequel does not expect.
|
|
7
|
+
#
|
|
8
|
+
# To enable this for a single dataset, extend the dataset with
|
|
9
|
+
# Sequel::ColumnIntrospection. To enable this for all datasets, run:
|
|
10
|
+
#
|
|
11
|
+
# Sequel::Dataset.introspect_all_columns
|
|
12
|
+
|
|
13
|
+
module Sequel
|
|
14
|
+
module ColumnsIntrospection
|
|
15
|
+
# Attempt to guess the columns that will be returned
|
|
16
|
+
# if there are columns selected, in order to skip a database
|
|
17
|
+
# query to retrieve the columns. This should work with
|
|
18
|
+
# Symbols, SQL::Identifiers, SQL::QualifiedIdentifiers, and
|
|
19
|
+
# SQL::AliasedExpressions.
|
|
20
|
+
def columns
|
|
21
|
+
return @columns if @columns
|
|
22
|
+
return columns_without_introspection unless cols = opts[:select] and !cols.empty?
|
|
23
|
+
probable_columns = cols.map{|c| probable_column_name(c)}
|
|
24
|
+
if probable_columns.all?
|
|
25
|
+
@columns = probable_columns
|
|
26
|
+
else
|
|
27
|
+
columns_without_introspection
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Return the probable name of the column, or nil if one
|
|
34
|
+
# cannot be determined.
|
|
35
|
+
def probable_column_name(c)
|
|
36
|
+
case c
|
|
37
|
+
when Symbol
|
|
38
|
+
_, c, a = split_symbol(c)
|
|
39
|
+
(a || c).to_sym
|
|
40
|
+
when SQL::Identifier
|
|
41
|
+
c.value.to_sym
|
|
42
|
+
when SQL::QualifiedIdentifier
|
|
43
|
+
col = c.column
|
|
44
|
+
col.is_a?(SQL::Identifier) ? col.value.to_sym : col.to_sym
|
|
45
|
+
when SQL::AliasedExpression
|
|
46
|
+
a = c.aliaz
|
|
47
|
+
a.is_a?(SQL::Identifier) ? a.value.to_sym : a.to_sym
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Dataset
|
|
53
|
+
alias columns_without_introspection columns
|
|
54
|
+
|
|
55
|
+
# Enable column introspection for every dataset.
|
|
56
|
+
def self.introspect_all_columns
|
|
57
|
+
include ColumnsIntrospection
|
|
58
|
+
remove_method(:columns) if instance_methods(false).map{|x| x.to_s}.include?('columns')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# the user to easily group schema changes and migrate the database
|
|
3
3
|
# to a newer version or revert to a previous version.
|
|
4
4
|
|
|
5
|
+
#
|
|
5
6
|
module Sequel
|
|
6
7
|
# Sequel's older migration class, available for backward compatibility.
|
|
7
8
|
# Uses subclasses with up and down instance methods for each migration:
|
|
@@ -302,7 +303,7 @@ module Sequel
|
|
|
302
303
|
#
|
|
303
304
|
# Part of the +migration+ extension.
|
|
304
305
|
class Migrator
|
|
305
|
-
MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/i.freeze
|
|
306
|
+
MIGRATION_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
|
|
306
307
|
MIGRATION_SPLITTER = '_'.freeze
|
|
307
308
|
MINIMUM_TIMESTAMP = 20000101
|
|
308
309
|
|
|
@@ -375,7 +376,8 @@ module Sequel
|
|
|
375
376
|
@db = db
|
|
376
377
|
@directory = directory
|
|
377
378
|
@files = get_migration_files
|
|
378
|
-
|
|
379
|
+
schema, table = @db.send(:schema_and_table, opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE))
|
|
380
|
+
@table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
|
|
379
381
|
@column = opts[:column] || self.class.const_get(:DEFAULT_SCHEMA_COLUMN)
|
|
380
382
|
@ds = schema_dataset
|
|
381
383
|
end
|
|
@@ -423,7 +425,7 @@ module Sequel
|
|
|
423
425
|
@current = opts[:current] || current_migration_version
|
|
424
426
|
|
|
425
427
|
raise(Error, "No current version available") unless current
|
|
426
|
-
raise(Error, "No target version available") unless target
|
|
428
|
+
raise(Error, "No target version available, probably because no migration files found or filenames don't follow the migration filename convention") unless target
|
|
427
429
|
|
|
428
430
|
@direction = current < target ? :up : :down
|
|
429
431
|
@migrations = get_migrations
|
|
@@ -588,7 +590,7 @@ module Sequel
|
|
|
588
590
|
next unless MIGRATION_FILE_PATTERN.match(file)
|
|
589
591
|
files << File.join(directory, file)
|
|
590
592
|
end
|
|
591
|
-
files.
|
|
593
|
+
files.sort_by{|f| MIGRATION_FILE_PATTERN.match(File.basename(f))[1].to_i}
|
|
592
594
|
end
|
|
593
595
|
|
|
594
596
|
# Returns tuples of migration, filename, and direction
|
|
@@ -4,109 +4,111 @@
|
|
|
4
4
|
# of the dataset's abstract syntax tree.
|
|
5
5
|
|
|
6
6
|
module Sequel
|
|
7
|
-
class
|
|
7
|
+
class ToDot
|
|
8
8
|
# The option keys that should be included in the dot output.
|
|
9
9
|
TO_DOT_OPTIONS = [:with, :distinct, :select, :from, :join, :where, :group, :having, :compounds, :order, :limit, :offset, :lock].freeze
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
# Given a +Dataset+, return a string in +dot+ format that will
|
|
12
|
+
# generate a visualization of the dataset.
|
|
13
|
+
def self.output(ds)
|
|
14
|
+
new(ds).output
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Given a +Dataset+, parse the internal structure to generate
|
|
18
|
+
# a dataset visualization.
|
|
19
|
+
def initialize(ds)
|
|
20
|
+
@i = 0
|
|
21
|
+
@stack = [@i]
|
|
22
|
+
@dot = ["digraph G {", "0 [label=\"self\"];"]
|
|
23
|
+
v(ds, "")
|
|
24
|
+
@dot << "}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Output the dataset visualization as a string in +dot+ format.
|
|
28
|
+
def output
|
|
29
|
+
@dot.join("\n")
|
|
20
30
|
end
|
|
21
31
|
|
|
22
32
|
private
|
|
23
33
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# its children are then added by recursively calling this method. The
|
|
39
|
-
# return value is the integer representing the last created node.
|
|
40
|
-
def _to_dot(dot, l, c, e, i)
|
|
41
|
-
i += 1
|
|
42
|
-
dot << "#{c} -> #{i} [label=\"#{l}\"];" if l
|
|
43
|
-
c = i
|
|
34
|
+
# Add an entry to the +dot+ output with the given label. If +j+
|
|
35
|
+
# is given, it is used directly as the node or transition. Otherwise
|
|
36
|
+
# a node is created for the current object.
|
|
37
|
+
def dot(label, j=nil)
|
|
38
|
+
@dot << "#{j||@i} [label=#{label.to_s.inspect}];"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Recursive method that parses all of Sequel's internal datastructures,
|
|
42
|
+
# adding the appropriate nodes and transitions to the internal +dot+
|
|
43
|
+
# structure.
|
|
44
|
+
def v(e, l)
|
|
45
|
+
@i += 1
|
|
46
|
+
dot(l, "#{@stack.last} -> #{@i}") if l
|
|
47
|
+
@stack.push(@i)
|
|
44
48
|
case e
|
|
45
49
|
when LiteralString
|
|
46
|
-
dot
|
|
47
|
-
i
|
|
50
|
+
dot "#{e.inspect}.lit"
|
|
48
51
|
when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
|
|
49
|
-
dot
|
|
50
|
-
i
|
|
52
|
+
dot e.inspect
|
|
51
53
|
when Array
|
|
52
|
-
dot
|
|
53
|
-
e.each_with_index do |
|
|
54
|
-
|
|
54
|
+
dot "Array"
|
|
55
|
+
e.each_with_index do |val, j|
|
|
56
|
+
v(val, j)
|
|
55
57
|
end
|
|
56
58
|
when Hash
|
|
57
|
-
dot
|
|
58
|
-
e.each do |k,
|
|
59
|
-
|
|
59
|
+
dot "Hash"
|
|
60
|
+
e.each do |k, val|
|
|
61
|
+
v(val, k)
|
|
60
62
|
end
|
|
61
63
|
when SQL::ComplexExpression
|
|
62
|
-
dot
|
|
63
|
-
e.args.each_with_index do |
|
|
64
|
-
|
|
64
|
+
dot "ComplexExpression: #{e.op}"
|
|
65
|
+
e.args.each_with_index do |val, j|
|
|
66
|
+
v(val, j)
|
|
65
67
|
end
|
|
66
68
|
when SQL::Identifier
|
|
67
|
-
dot
|
|
68
|
-
|
|
69
|
+
dot "Identifier"
|
|
70
|
+
v(e.value, :value)
|
|
69
71
|
when SQL::QualifiedIdentifier
|
|
70
|
-
dot
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
dot "QualifiedIdentifier"
|
|
73
|
+
v(e.table, :table)
|
|
74
|
+
v(e.column, :column)
|
|
73
75
|
when SQL::OrderedExpression
|
|
74
|
-
dot
|
|
75
|
-
|
|
76
|
+
dot "OrderedExpression: #{e.descending ? :DESC : :ASC}#{" NULLS #{e.nulls.to_s.upcase}" if e.nulls}"
|
|
77
|
+
v(e.expression, :expression)
|
|
76
78
|
when SQL::AliasedExpression
|
|
77
|
-
dot
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
dot "AliasedExpression"
|
|
80
|
+
v(e.expression, :expression)
|
|
81
|
+
v(e.aliaz, :alias)
|
|
80
82
|
when SQL::CaseExpression
|
|
81
|
-
dot
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
dot "CaseExpression"
|
|
84
|
+
v(e.expression, :expression) if e.expression
|
|
85
|
+
v(e.conditions, :conditions)
|
|
86
|
+
v(e.default, :default)
|
|
85
87
|
when SQL::Cast
|
|
86
|
-
dot
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
dot "Cast"
|
|
89
|
+
v(e.expr, :expr)
|
|
90
|
+
v(e.type, :type)
|
|
89
91
|
when SQL::Function
|
|
90
|
-
dot
|
|
91
|
-
e.args.each_with_index do |
|
|
92
|
-
|
|
92
|
+
dot "Function: #{e.f}"
|
|
93
|
+
e.args.each_with_index do |val, j|
|
|
94
|
+
v(val, j)
|
|
93
95
|
end
|
|
94
96
|
when SQL::Subscript
|
|
95
|
-
dot
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
dot "Subscript"
|
|
98
|
+
v(e.f, :f)
|
|
99
|
+
v(e.sub, :sub)
|
|
98
100
|
when SQL::WindowFunction
|
|
99
|
-
dot
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
dot "WindowFunction"
|
|
102
|
+
v(e.function, :function)
|
|
103
|
+
v(e.window, :window)
|
|
102
104
|
when SQL::Window
|
|
103
|
-
dot
|
|
104
|
-
|
|
105
|
+
dot "Window"
|
|
106
|
+
v(e.opts, :opts)
|
|
105
107
|
when SQL::PlaceholderLiteralString
|
|
106
108
|
str = e.str
|
|
107
109
|
str = "(#{str})" if e.parens
|
|
108
|
-
dot
|
|
109
|
-
|
|
110
|
+
dot "PlaceholderLiteralString: #{str.inspect}"
|
|
111
|
+
v(e.args, :args)
|
|
110
112
|
when SQL::JoinClause
|
|
111
113
|
str = "#{e.join_type.to_s.upcase} JOIN"
|
|
112
114
|
if e.is_a?(SQL::JoinOnClause)
|
|
@@ -114,24 +116,34 @@ module Sequel
|
|
|
114
116
|
elsif e.is_a?(SQL::JoinUsingClause)
|
|
115
117
|
str << " USING"
|
|
116
118
|
end
|
|
117
|
-
dot
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
dot str
|
|
120
|
+
v(e.table, :table)
|
|
121
|
+
v(e.table_alias, :alias) if e.table_alias
|
|
120
122
|
if e.is_a?(SQL::JoinOnClause)
|
|
121
|
-
|
|
123
|
+
v(e.on, :on)
|
|
122
124
|
elsif e.is_a?(SQL::JoinUsingClause)
|
|
123
|
-
|
|
125
|
+
v(e.using, :using)
|
|
124
126
|
end
|
|
125
127
|
when Dataset
|
|
126
|
-
dot
|
|
128
|
+
dot "Dataset"
|
|
127
129
|
TO_DOT_OPTIONS.each do |k|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
if val = e.opts[k]
|
|
131
|
+
v(val, k.to_s)
|
|
132
|
+
end
|
|
130
133
|
end
|
|
131
134
|
else
|
|
132
|
-
dot
|
|
135
|
+
dot "Unhandled: #{e.inspect}"
|
|
133
136
|
end
|
|
134
|
-
|
|
137
|
+
@stack.pop
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class Dataset
|
|
142
|
+
# Return a string that can be processed by the +dot+ program (included
|
|
143
|
+
# with graphviz) in order to see a visualization of the dataset's
|
|
144
|
+
# abstract syntax tree.
|
|
145
|
+
def to_dot
|
|
146
|
+
ToDot.output(self)
|
|
135
147
|
end
|
|
136
148
|
end
|
|
137
149
|
end
|