sequel 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
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