sequel 3.21.0 → 3.29.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 +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- 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 +72 -27
- data/doc/opening_databases.rdoc +86 -50
- data/doc/prepared_statements.rdoc +40 -13
- 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/release_notes/3.29.0.txt +459 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -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 +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -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 +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- 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/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +997 -331
- data/lib/sequel/model/base.rb +386 -129
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -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/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -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 +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +200 -120
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
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
|
|
|
@@ -62,6 +62,10 @@ module Sequel
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
|
|
65
|
+
if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
|
|
66
|
+
columns = [literal(columns().last)]
|
|
67
|
+
values = ['DEFAULT'.lit]
|
|
68
|
+
end
|
|
65
69
|
clone(:columns=>columns, :values=>values)._insert_sql
|
|
66
70
|
end
|
|
67
71
|
|
|
@@ -101,7 +105,7 @@ module Sequel
|
|
|
101
105
|
when Array
|
|
102
106
|
literal_array(v)
|
|
103
107
|
when Time
|
|
104
|
-
literal_time(v)
|
|
108
|
+
v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v)
|
|
105
109
|
when DateTime
|
|
106
110
|
literal_datetime(v)
|
|
107
111
|
when Date
|
|
@@ -163,7 +167,7 @@ module Sequel
|
|
|
163
167
|
end
|
|
164
168
|
|
|
165
169
|
# ---------------------
|
|
166
|
-
# :section: Internal Methods relating to SQL Creation
|
|
170
|
+
# :section: 9 - Internal Methods relating to SQL Creation
|
|
167
171
|
# These methods, while public, are not designed to be used directly by the end user.
|
|
168
172
|
# ---------------------
|
|
169
173
|
|
|
@@ -212,7 +216,11 @@ module Sequel
|
|
|
212
216
|
|
|
213
217
|
# SQL fragment for BooleanConstants
|
|
214
218
|
def boolean_constant_sql(constant)
|
|
215
|
-
|
|
219
|
+
if (constant == true || constant == false) && !supports_where_true?
|
|
220
|
+
constant == true ? '(1 = 1)' : '(1 = 0)'
|
|
221
|
+
else
|
|
222
|
+
literal(constant)
|
|
223
|
+
end
|
|
216
224
|
end
|
|
217
225
|
|
|
218
226
|
# SQL fragment for CaseExpression
|
|
@@ -269,6 +277,7 @@ module Sequel
|
|
|
269
277
|
literal(op == :IN ? expr : ~expr)
|
|
270
278
|
else
|
|
271
279
|
old_vals = vals
|
|
280
|
+
vals = vals.naked if vals.is_a?(Sequel::Dataset)
|
|
272
281
|
vals = vals.to_a
|
|
273
282
|
val_cols = old_vals.columns
|
|
274
283
|
complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
|
|
@@ -300,6 +309,8 @@ module Sequel
|
|
|
300
309
|
literal(args.at(0))
|
|
301
310
|
when :'B~'
|
|
302
311
|
"~#{literal(args.at(0))}"
|
|
312
|
+
when :extract
|
|
313
|
+
"extract(#{args.at(0)} FROM #{literal(args.at(1))})"
|
|
303
314
|
else
|
|
304
315
|
raise(InvalidOperation, "invalid operator #{op}")
|
|
305
316
|
end
|
|
@@ -434,7 +445,7 @@ module Sequel
|
|
|
434
445
|
when :all
|
|
435
446
|
"ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
|
|
436
447
|
when :rows
|
|
437
|
-
"ROWS UNBOUNDED PRECEDING"
|
|
448
|
+
"ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
|
|
438
449
|
when String
|
|
439
450
|
opts[:frame]
|
|
440
451
|
else
|
|
@@ -547,6 +558,28 @@ module Sequel
|
|
|
547
558
|
(columns.nil? || columns.empty?) ? WILDCARD : expression_list(columns)
|
|
548
559
|
end
|
|
549
560
|
|
|
561
|
+
# Yield each two pair of arguments to the block, which should
|
|
562
|
+
# return a string representing the SQL code for those
|
|
563
|
+
# two arguments. If more than 2 arguments are provided, all
|
|
564
|
+
# calls to the block # after the first will have a LiteralString
|
|
565
|
+
# as the first argument, representing the application of the block to
|
|
566
|
+
# the previous arguments.
|
|
567
|
+
def complex_expression_arg_pairs(args)
|
|
568
|
+
case args.length
|
|
569
|
+
when 1
|
|
570
|
+
literal(args.at(0))
|
|
571
|
+
when 2
|
|
572
|
+
yield args.at(0), args.at(1)
|
|
573
|
+
else
|
|
574
|
+
args.inject{|m, a| LiteralString.new(yield(m, a))}
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
# The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause.
|
|
579
|
+
def compound_dataset_sql(ds)
|
|
580
|
+
subselect_sql(ds)
|
|
581
|
+
end
|
|
582
|
+
|
|
550
583
|
# The alias to use for datasets, takes a number to make sure the name is unique.
|
|
551
584
|
def dataset_alias(number)
|
|
552
585
|
:"#{DATASET_ALIAS_BASE_NAME}#{number}"
|
|
@@ -573,7 +606,7 @@ module Sequel
|
|
|
573
606
|
# database supports them), and override %z to always use a numeric offset
|
|
574
607
|
# of hours and minutes.
|
|
575
608
|
def format_timestamp(v)
|
|
576
|
-
v2 =
|
|
609
|
+
v2 = db.from_application_timestamp(v)
|
|
577
610
|
fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
|
|
578
611
|
if m == '%N'
|
|
579
612
|
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
|
|
@@ -636,6 +669,15 @@ module Sequel
|
|
|
636
669
|
end
|
|
637
670
|
end
|
|
638
671
|
|
|
672
|
+
# SQL fragment specifying the values to return.
|
|
673
|
+
def insert_returning_sql(sql)
|
|
674
|
+
if opts.has_key?(:returning)
|
|
675
|
+
sql << " RETURNING #{column_list(Array(opts[:returning]))}"
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
alias delete_returning_sql insert_returning_sql
|
|
679
|
+
alias update_returning_sql insert_returning_sql
|
|
680
|
+
|
|
639
681
|
# SQL fragment specifying a JOIN type, converts underscores to
|
|
640
682
|
# spaces and upcases.
|
|
641
683
|
def join_type_sql(join_type)
|
|
@@ -722,6 +764,11 @@ module Sequel
|
|
|
722
764
|
end
|
|
723
765
|
end
|
|
724
766
|
|
|
767
|
+
# SQL fragment for Sequel::SQLTime, containing just the time part
|
|
768
|
+
def literal_sqltime(v)
|
|
769
|
+
v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
|
|
770
|
+
end
|
|
771
|
+
|
|
725
772
|
# SQL fragment for String. Doubles \ and ' by default.
|
|
726
773
|
def literal_string(v)
|
|
727
774
|
"'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
|
|
@@ -774,59 +821,7 @@ module Sequel
|
|
|
774
821
|
|
|
775
822
|
# Qualify the given expression e to the given table.
|
|
776
823
|
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
|
|
824
|
+
Qualifier.new(self, table).transform(e)
|
|
830
825
|
end
|
|
831
826
|
|
|
832
827
|
# The order of methods to call to build the SELECT SQL statement
|
|
@@ -852,8 +847,7 @@ module Sequel
|
|
|
852
847
|
def select_compounds_sql(sql)
|
|
853
848
|
return unless @opts[:compounds]
|
|
854
849
|
@opts[:compounds].each do |type, dataset, all|
|
|
855
|
-
|
|
856
|
-
sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}"
|
|
850
|
+
sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_dataset_sql(dataset)}"
|
|
857
851
|
end
|
|
858
852
|
end
|
|
859
853
|
|
|
@@ -914,6 +908,9 @@ module Sequel
|
|
|
914
908
|
return if !ws || ws.empty?
|
|
915
909
|
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
910
|
end
|
|
911
|
+
alias delete_with_sql select_with_sql
|
|
912
|
+
alias insert_with_sql select_with_sql
|
|
913
|
+
alias update_with_sql select_with_sql
|
|
917
914
|
|
|
918
915
|
# The base keyword to use for the SQL WITH clause
|
|
919
916
|
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
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
# Then, before data is stored in the database, it is converted to New
|
|
22
22
|
# York time. When data is retrieved from the database, it is
|
|
23
23
|
# converted to Los Angeles time.
|
|
24
|
+
#
|
|
25
|
+
# Note that typecasting from the database timezone to the application
|
|
26
|
+
# timezone when fetching rows is dependent on the database adapter,
|
|
27
|
+
# and only works on adapters where Sequel itself does the conversion.
|
|
28
|
+
# It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
|
|
24
29
|
|
|
25
30
|
require 'tzinfo'
|
|
26
31
|
|
|
@@ -32,7 +32,7 @@ module Sequel
|
|
|
32
32
|
module ThreadLocalTimezones
|
|
33
33
|
%w'application database typecast'.each do |t|
|
|
34
34
|
class_eval("def thread_#{t}_timezone=(tz); Thread.current[:#{t}_timezone] = convert_timezone_setter_arg(tz); end", __FILE__, __LINE__)
|
|
35
|
-
class_eval(<<END, __FILE__, __LINE__)
|
|
35
|
+
class_eval(<<END, __FILE__, __LINE__ + 1)
|
|
36
36
|
def #{t}_timezone
|
|
37
37
|
if tz = Thread.current[:#{t}_timezone]
|
|
38
38
|
tz unless tz == :nil
|
|
@@ -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
|