sequel 3.28.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.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -71,11 +71,11 @@ module Sequel
71
71
  # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
72
72
  def alter_table(name, generator=nil, &block)
73
73
  generator ||= Schema::AlterTableGenerator.new(self, &block)
74
- alter_table_sql_list(name, generator.operations).flatten.each {|sql| execute_ddl(sql)}
75
74
  remove_cached_schema(name)
75
+ apply_alter_table(name, generator.operations)
76
76
  nil
77
77
  end
78
-
78
+
79
79
  # Creates a table with the columns given in the provided block:
80
80
  #
81
81
  # DB.create_table :posts do
@@ -231,6 +231,11 @@ module Sequel
231
231
 
232
232
  private
233
233
 
234
+ # Apply the changes in the given alter table ops to the table given by name.
235
+ def apply_alter_table(name, ops)
236
+ alter_table_sql_list(name, ops).flatten.each{|sql| execute_ddl(sql)}
237
+ end
238
+
234
239
  # The SQL to execute to modify the DDL for the given table name. op
235
240
  # should be one of the operations returned by the AlterTableGenerator.
236
241
  def alter_table_sql(table, op)
@@ -8,15 +8,20 @@ module Sequel
8
8
  # ---------------------
9
9
 
10
10
  # Action methods defined by Sequel that execute code on the database.
11
- ACTION_METHODS = %w'<< [] []= all avg count columns columns! delete each
12
- empty? fetch_rows first get import insert insert_multiple interval last
13
- map max min multi_insert range select_hash select_map select_order_map
14
- set single_record single_value sum to_csv to_hash truncate update'.map{|x| x.to_sym}
11
+ ACTION_METHODS = (<<-METHS).split.map{|x| x.to_sym}
12
+ << [] []= all avg count columns columns! delete each
13
+ empty? fetch_rows first get import insert insert_multiple interval last
14
+ map max min multi_insert range select_hash select_map select_order_map
15
+ set single_record single_value sum to_csv to_hash truncate update
16
+ METHS
15
17
 
16
- # Alias for insert, but not aliased directly so subclasses
17
- # don't have to override both methods.
18
- def <<(*args)
19
- insert(*args)
18
+ # Inserts the given argument into the database. Returns self so it
19
+ # can be used safely when chaining:
20
+ #
21
+ # DB[:items] << {:id=>0, :name=>'Zero'} << DB[:old_items].select(:id, name)
22
+ def <<(arg)
23
+ insert(arg)
24
+ self
20
25
  end
21
26
 
22
27
  # Returns the first record matching the conditions. Examples:
@@ -72,7 +77,7 @@ module Sequel
72
77
  # # => [:id, :name]
73
78
  def columns
74
79
  return @columns if @columns
75
- ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1)
80
+ ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1, :offset=>nil)
76
81
  ds.each{break}
77
82
  @columns = ds.instance_variable_get(:@columns)
78
83
  @columns || []
@@ -354,8 +359,9 @@ module Sequel
354
359
  def map(column=nil, &block)
355
360
  if column
356
361
  raise(Error, ARG_BLOCK_ERROR_MSG) if block
362
+ return naked.map(column) if row_proc
357
363
  if column.is_a?(Array)
358
- super(){|r| column.map{|c| r[c]}}
364
+ super(){|r| r.values_at(*column)}
359
365
  else
360
366
  super(){|r| r[column]}
361
367
  end
@@ -546,21 +552,22 @@ module Sequel
546
552
  def to_hash(key_column, value_column = nil)
547
553
  h = {}
548
554
  if value_column
555
+ return naked.to_hash(key_column, value_column) if row_proc
549
556
  if value_column.is_a?(Array)
550
557
  if key_column.is_a?(Array)
551
- each{|r| h[key_column.map{|c| r[c]}] = value_column.map{|c| r[c]}}
558
+ each{|r| h[r.values_at(*key_column)] = r.values_at(*value_column)}
552
559
  else
553
- each{|r| h[r[key_column]] = value_column.map{|c| r[c]}}
560
+ each{|r| h[r[key_column]] = r.values_at(*value_column)}
554
561
  end
555
562
  else
556
563
  if key_column.is_a?(Array)
557
- each{|r| h[key_column.map{|c| r[c]}] = r[value_column]}
564
+ each{|r| h[r.values_at(*key_column)] = r[value_column]}
558
565
  else
559
566
  each{|r| h[r[key_column]] = r[value_column]}
560
567
  end
561
568
  end
562
569
  elsif key_column.is_a?(Array)
563
- each{|r| h[key_column.map{|c| r[c]}] = r}
570
+ each{|r| h[r.values_at(*key_column)] = r}
564
571
  else
565
572
  each{|r| h[r[key_column]] = r}
566
573
  end
@@ -594,6 +601,18 @@ module Sequel
594
601
  end
595
602
  end
596
603
 
604
+ protected
605
+
606
+ # Return an array of arrays of values given by the symbols in ret_cols.
607
+ def _select_map_multiple(ret_cols)
608
+ map{|r| r.values_at(*ret_cols)}
609
+ end
610
+
611
+ # Returns an array of the first value in each row.
612
+ def _select_map_single
613
+ map{|r| r.values.first}
614
+ end
615
+
597
616
  private
598
617
 
599
618
  # Internals of +select_map+ and +select_order_map+
@@ -609,14 +628,13 @@ module Sequel
609
628
  ds = ds.select(&block)
610
629
  ds = ds.order(&block) if order
611
630
  end
612
- if ds.opts[:select].length > 1
613
- ret_cols = select_cols.map{|c| hash_key_symbol(c)}
614
- ds.map{|r| ret_cols.map{|c| r[c]}}
631
+ if column.is_a?(Array)
632
+ ds._select_map_multiple(select_cols.map{|c| hash_key_symbol(c)})
615
633
  else
616
- ds.map{|r| r.values.first}
634
+ ds._select_map_single
617
635
  end
618
636
  end
619
-
637
+
620
638
  # Set the server to use to :default unless it is already set in the passed opts
621
639
  def default_server_opts(opts)
622
640
  {:server=>@opts[:server] || :default}.merge(opts)
@@ -24,12 +24,24 @@ module Sequel
24
24
  true
25
25
  end
26
26
 
27
+ # Whether you must use a column alias list for recursive CTEs (false by
28
+ # default).
29
+ def recursive_cte_requires_column_aliases?
30
+ false
31
+ end
32
+
27
33
  # Whether the dataset requires SQL standard datetimes (false by default,
28
34
  # as most allow strings with ISO 8601 format).
29
35
  def requires_sql_standard_datetimes?
30
36
  false
31
37
  end
32
38
 
39
+ # Whether type specifiers are required for prepared statement/bound
40
+ # variable argument placeholders (i.e. :bv__integer)
41
+ def requires_placeholder_type_specifiers?
42
+ false
43
+ end
44
+
33
45
  # Whether the dataset supports common table expressions (the WITH clause).
34
46
  # If given, +type+ can be :select, :insert, :update, or :delete, in which case it
35
47
  # determines whether WITH is supported for the respective statement type.
@@ -126,6 +138,18 @@ module Sequel
126
138
 
127
139
  private
128
140
 
141
+ # Whether insert(nil) or insert({}) must be emulated by
142
+ # using at least one value, false by default.
143
+ def insert_supports_empty_values?
144
+ true
145
+ end
146
+
147
+ # Whether using an offset returns an extra row number column that should be
148
+ # eliminated, false by default.
149
+ def offset_returns_row_number_column?
150
+ false
151
+ end
152
+
129
153
  # Whether the RETURNING clause is used for the given dataset.
130
154
  # +type+ can be :insert, :update, or :delete.
131
155
  def uses_returning?(type)
@@ -113,6 +113,8 @@ module Sequel
113
113
  add_table = options[:select] == false ? false : true
114
114
  # Whether to add the columns to the list of column aliases
115
115
  add_columns = !ds.opts.include?(:graph_aliases)
116
+ # columns to select
117
+ select = (opts[:select] || []).dup
116
118
 
117
119
  # Setup the initial graph data structure if it doesn't exist
118
120
  unless graph = opts[:graph]
@@ -130,7 +132,6 @@ module Sequel
130
132
  # aliased, but are not included if set_graph_aliases
131
133
  # has been used.
132
134
  if add_columns
133
- select = opts[:select] = []
134
135
  columns.each do |column|
135
136
  column_aliases[column] = [master, column]
136
137
  select.push(SQL::QualifiedIdentifier.new(master, column))
@@ -147,7 +148,6 @@ module Sequel
147
148
 
148
149
  # Add the columns to the selection unless we are ignoring them
149
150
  if add_table && add_columns
150
- select = opts[:select]
151
151
  column_aliases = graph[:column_aliases]
152
152
  ca_num = graph[:column_alias_num]
153
153
  # Which columns to add to the result set
@@ -165,15 +165,16 @@ module Sequel
165
165
  column_alias = :"#{column_alias}_#{column_alias_num}"
166
166
  ca_num[column_alias] += 1
167
167
  end
168
- [column_alias, SQL::QualifiedIdentifier.new(table_alias, column).as(column_alias)]
168
+ [column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table_alias, column), column_alias)]
169
169
  else
170
- [column, SQL::QualifiedIdentifier.new(table_alias, column)]
170
+ ident = SQL::QualifiedIdentifier.new(table_alias, column)
171
+ [column, ident]
171
172
  end
172
173
  column_aliases[col_alias] = [table_alias, column]
173
174
  select.push(identifier)
174
175
  end
175
176
  end
176
- ds
177
+ ds.select(*select)
177
178
  end
178
179
 
179
180
  # This allows you to manually specify the graph aliases to use
@@ -225,7 +226,7 @@ module Sequel
225
226
  column ||= col_alias
226
227
  gas[col_alias] = [table, column]
227
228
  identifier = value || SQL::QualifiedIdentifier.new(table, column)
228
- identifier = SQL::AliasedExpression.new(identifier, col_alias) if value or column != col_alias
229
+ identifier = SQL::AliasedExpression.new(identifier, col_alias) if value || column != col_alias
229
230
  identifier
230
231
  end
231
232
  [identifiers, gas]
@@ -137,7 +137,9 @@ module Sequel
137
137
  # Returns a string representation of the dataset including the class name
138
138
  # and the corresponding SQL select statement.
139
139
  def inspect
140
- "#<#{self.class}: #{sql.inspect}>"
140
+ c = self.class
141
+ c = c.superclass while c.name.nil? || c.name == ''
142
+ "#<#{c.name}: #{sql.inspect}>"
141
143
  end
142
144
 
143
145
  # The alias to use for the row_number column, used when emulating OFFSET
@@ -170,6 +172,10 @@ module Sequel
170
172
  # used, where N is an integer starting at 0 and increasing until an
171
173
  # unused one is found.
172
174
  #
175
+ # You can provide a second addition array argument containing symbols
176
+ # that should not be considered valid table aliases. The current aliases
177
+ # for the FROM and JOIN tables are automatically included in this array.
178
+ #
173
179
  # DB[:table].unused_table_alias(:t)
174
180
  # # => :t
175
181
  #
@@ -178,9 +184,11 @@ module Sequel
178
184
  #
179
185
  # DB[:table, :table_0].unused_table_alias(:table)
180
186
  # # => :table_1
181
- def unused_table_alias(table_alias)
187
+ #
188
+ # DB[:table, :table_0].unused_table_alias(:table, [:table_1, :table_2])
189
+ # # => :table_3
190
+ def unused_table_alias(table_alias, used_aliases = [])
182
191
  table_alias = alias_symbol(table_alias)
183
- used_aliases = []
184
192
  used_aliases += opts[:from].map{|t| alias_symbol(t)} if opts[:from]
185
193
  used_aliases += opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if opts[:join]
186
194
  if used_aliases.include?(table_alias)
@@ -29,9 +29,8 @@ module Sequel
29
29
  # Whether to quote identifiers for this dataset
30
30
  attr_writer :quote_identifiers
31
31
 
32
- # The row_proc for this database, should be a Proc that takes
33
- # a single hash argument and returns the object you want
34
- # each to return.
32
+ # The row_proc for this database, should be any object that responds to +call+ with
33
+ # a single hash argument and returns the object you want #each to return.
35
34
  attr_accessor :row_proc
36
35
 
37
36
  # Add a mutation method to this dataset instance.
@@ -35,7 +35,7 @@ module Sequel
35
35
  return @prepared_sql if @prepared_sql
36
36
  @prepared_args ||= []
37
37
  @prepared_sql = super
38
- meta_def("#{sql_query_type}_sql"){|*args| prepared_sql}
38
+ @opts[:sql] = @prepared_sql
39
39
  @prepared_sql
40
40
  end
41
41
 
@@ -121,8 +121,7 @@ module Sequel
121
121
  when :select, :all
122
122
  all(&block)
123
123
  when :insert_select
124
- meta_def(:select_sql){prepared_sql}
125
- first
124
+ with_sql(prepared_sql).first
126
125
  when :first
127
126
  first
128
127
  when :insert
@@ -211,7 +210,10 @@ module Sequel
211
210
  end
212
211
 
213
212
  # Prepare an SQL statement for later execution. Takes a type similar to #call,
214
- # and the name symbol of the prepared statement.
213
+ # and the +name+ symbol of the prepared statement. While +name+ defaults to +nil+,
214
+ # it should always be provided as a symbol for the name of the prepared statement,
215
+ # as some databases require that prepared statements have names.
216
+ #
215
217
  # This returns a clone of the dataset extended with PreparedStatementMethods,
216
218
  # which you can +call+ with the hash of bind variables to use.
217
219
  # The prepared statement is also stored in
@@ -28,12 +28,14 @@ module Sequel
28
28
  JOIN_METHODS = (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]
29
29
 
30
30
  # Methods that return modified datasets
31
- QUERY_METHODS = %w'add_graph_aliases and distinct except exclude exclude_having exclude_where
32
- filter for_update from from_self graph grep group group_and_count group_by having intersect invert
33
- limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
34
- reverse reverse_order select select_all select_append select_group select_more server
35
- set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
36
- unlimited unordered where with with_recursive with_sql'.collect{|x| x.to_sym} + JOIN_METHODS
31
+ QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
32
+ add_graph_aliases and distinct except exclude exclude_having exclude_where
33
+ filter for_update from from_self graph grep group group_and_count group_by having intersect invert
34
+ limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
35
+ reverse reverse_order select select_all select_append select_group select_more server
36
+ set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
37
+ unlimited unordered where with with_recursive with_sql
38
+ METHS
37
39
 
38
40
  # Adds an further filter to an existing filter using AND. If no filter
39
41
  # exists an error is raised. This method is identical to #filter except
@@ -201,11 +203,17 @@ module Sequel
201
203
  def from(*source)
202
204
  table_alias_num = 0
203
205
  sources = []
206
+ ctes = nil
204
207
  source.each do |s|
205
208
  case s
206
209
  when Hash
207
210
  s.each{|k,v| sources << SQL::AliasedExpression.new(k,v)}
208
211
  when Dataset
212
+ if hoist_cte?(s)
213
+ ctes ||= []
214
+ ctes += s.opts[:with]
215
+ s = s.clone(:with=>nil)
216
+ end
209
217
  sources << SQL::AliasedExpression.new(s, dataset_alias(table_alias_num+=1))
210
218
  when Symbol
211
219
  sch, table, aliaz = split_symbol(s)
@@ -220,6 +228,7 @@ module Sequel
220
228
  end
221
229
  end
222
230
  o = {:from=>sources.empty? ? nil : sources}
231
+ o[:with] = (opts[:with] || []) + ctes if ctes
223
232
  o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
224
233
  clone(o)
225
234
  end
@@ -433,7 +442,7 @@ module Sequel
433
442
  # # SELECT * FROM a NATURAL JOIN b INNER JOIN c
434
443
  # # ON ((c.d > b.e) AND (c.f IN (SELECT g FROM b)))
435
444
  def join_table(type, table, expr=nil, options={}, &block)
436
- if table.is_a?(Dataset) && table.opts[:with] && !supports_cte_in_subqueries?
445
+ if hoist_cte?(table)
437
446
  s, ds = hoist_cte(table)
438
447
  return s.join_table(type, ds, expr, options, &block)
439
448
  end
@@ -881,7 +890,12 @@ module Sequel
881
890
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
882
891
  def with(name, dataset, opts={})
883
892
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
884
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
893
+ if hoist_cte?(dataset)
894
+ s, ds = hoist_cte(dataset)
895
+ s.with(name, ds, opts)
896
+ else
897
+ clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
898
+ end
885
899
  end
886
900
 
887
901
  # Add a recursive common table expression (CTE) with the given name, a dataset that
@@ -903,7 +917,15 @@ module Sequel
903
917
  # # SELECT i AS id, pi AS parent_id FROM t
904
918
  def with_recursive(name, nonrecursive, recursive, opts={})
905
919
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
906
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
920
+ if hoist_cte?(nonrecursive)
921
+ s, ds = hoist_cte(nonrecursive)
922
+ s.with_recursive(name, ds, recursive, opts)
923
+ elsif hoist_cte?(recursive)
924
+ s, ds = hoist_cte(recursive)
925
+ s.with_recursive(name, nonrecursive, ds, opts)
926
+ else
927
+ clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
928
+ end
907
929
  end
908
930
 
909
931
  # Returns a copy of the dataset with the static SQL used. This is useful if you want
@@ -929,6 +951,16 @@ module Sequel
929
951
 
930
952
  protected
931
953
 
954
+ # Add the dataset to the list of compounds
955
+ def compound_clone(type, dataset, opts)
956
+ if hoist_cte?(dataset)
957
+ s, ds = hoist_cte(dataset)
958
+ return s.compound_clone(type, ds, opts)
959
+ end
960
+ ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
961
+ opts[:from_self] == false ? ds : ds.from_self(opts)
962
+ end
963
+
932
964
  # Return true if the dataset has a non-nil value for any key in opts.
933
965
  def options_overlap(opts)
934
966
  !(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
@@ -960,12 +992,6 @@ module Sequel
960
992
  _filter_or_exclude(false, clause, *cond, &block)
961
993
  end
962
994
 
963
- # Add the dataset to the list of compounds
964
- def compound_clone(type, dataset, opts)
965
- ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
966
- opts[:from_self] == false ? ds : ds.from_self(opts)
967
- end
968
-
969
995
  # SQL expression object based on the expr type. See +filter+.
970
996
  def filter_expr(expr = nil, &block)
971
997
  expr = nil if expr == []
@@ -1013,6 +1039,11 @@ module Sequel
1013
1039
  [clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
1014
1040
  end
1015
1041
 
1042
+ # Whether CTEs need to be hoisted from the given ds into the current ds.
1043
+ def hoist_cte?(ds)
1044
+ ds.is_a?(Dataset) && ds.opts[:with] && !supports_cte_in_subqueries?
1045
+ end
1046
+
1016
1047
  # Inverts the given order by breaking it into a list of column references
1017
1048
  # and inverting them.
1018
1049
  #
@@ -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
 
@@ -554,6 +558,28 @@ module Sequel
554
558
  (columns.nil? || columns.empty?) ? WILDCARD : expression_list(columns)
555
559
  end
556
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
+
557
583
  # The alias to use for datasets, takes a number to make sure the name is unique.
558
584
  def dataset_alias(number)
559
585
  :"#{DATASET_ALIAS_BASE_NAME}#{number}"
@@ -580,7 +606,7 @@ module Sequel
580
606
  # database supports them), and override %z to always use a numeric offset
581
607
  # of hours and minutes.
582
608
  def format_timestamp(v)
583
- v2 = Sequel.application_to_database_timestamp(v)
609
+ v2 = db.from_application_timestamp(v)
584
610
  fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
585
611
  if m == '%N'
586
612
  format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
@@ -821,8 +847,7 @@ module Sequel
821
847
  def select_compounds_sql(sql)
822
848
  return unless @opts[:compounds]
823
849
  @opts[:compounds].each do |type, dataset, all|
824
- compound_sql = subselect_sql(dataset)
825
- sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}"
850
+ sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_dataset_sql(dataset)}"
826
851
  end
827
852
  end
828
853
 
@@ -886,7 +911,6 @@ module Sequel
886
911
  alias delete_with_sql select_with_sql
887
912
  alias insert_with_sql select_with_sql
888
913
  alias update_with_sql select_with_sql
889
-
890
914
 
891
915
  # The base keyword to use for the SQL WITH clause
892
916
  def select_with_sql_base