sequel 3.33.0 → 3.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -11,8 +11,8 @@ module Sequel
11
11
  ACTION_METHODS = (<<-METHS).split.map{|x| x.to_sym}
12
12
  << [] []= all avg count columns columns! delete each
13
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
14
+ map max min multi_insert range select_hash select_hash_groups select_map select_order_map
15
+ set single_record single_value sum to_csv to_hash to_hash_groups truncate update
16
16
  METHS
17
17
 
18
18
  # Inserts the given argument into the database. Returns self so it
@@ -124,13 +124,13 @@ module Sequel
124
124
  # running additional queries inside the provided block. If you are
125
125
  # running queries inside the block, you should use +all+ instead of +each+
126
126
  # for the outer queries, or use a separate thread or shard inside +each+:
127
- def each(&block)
127
+ def each
128
128
  if @opts[:graph]
129
- graph_each(&block)
129
+ graph_each{|r| yield r}
130
130
  elsif row_proc = @row_proc
131
131
  fetch_rows(select_sql){|r| yield row_proc.call(r)}
132
132
  else
133
- fetch_rows(select_sql, &block)
133
+ fetch_rows(select_sql){|r| yield r}
134
134
  end
135
135
  self
136
136
  end
@@ -421,7 +421,7 @@ module Sequel
421
421
  end
422
422
 
423
423
  # Returns a hash with key_column values as keys and value_column values as
424
- # values. Similar to to_hash, but only selects the two columns.
424
+ # values. Similar to to_hash, but only selects the columns given.
425
425
  #
426
426
  # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
427
427
  # # => {1=>'a', 2=>'b', ...}
@@ -436,19 +436,28 @@ module Sequel
436
436
  # that Sequel can determine. Usually you can do this by calling the #as method
437
437
  # on the expression and providing an alias.
438
438
  def select_hash(key_column, value_column)
439
- if key_column.is_a?(Array)
440
- if value_column.is_a?(Array)
441
- select(*(key_column + value_column)).to_hash(key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
442
- else
443
- select(*(key_column + [value_column])).to_hash(key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
444
- end
445
- elsif value_column.is_a?(Array)
446
- select(key_column, *value_column).to_hash(hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
447
- else
448
- select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
449
- end
439
+ _select_hash(:to_hash, key_column, value_column)
450
440
  end
451
441
 
442
+ # Returns a hash with key_column values as keys and an array of value_column values.
443
+ # Similar to to_hash_groups, but only selects the columns given.
444
+ #
445
+ # DB[:table].select_hash(:name, :id) # SELECT id, name FROM table
446
+ # # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
447
+ #
448
+ # You can also provide an array of column names for either the key_column,
449
+ # the value column, or both:
450
+ #
451
+ # DB[:table].select_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
452
+ # # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
453
+ #
454
+ # When using this method, you must be sure that each expression has an alias
455
+ # that Sequel can determine. Usually you can do this by calling the #as method
456
+ # on the expression and providing an alias.
457
+ def select_hash_groups(key_column, value_column)
458
+ _select_hash(:to_hash_groups, key_column, value_column)
459
+ end
460
+
452
461
  # Selects the column given (either as an argument or as a block), and
453
462
  # returns an array of all values of that column in the dataset. If you
454
463
  # give a block argument that returns an array with multiple entries,
@@ -472,7 +481,6 @@ module Sequel
472
481
  def select_map(column=nil, &block)
473
482
  _select_map(column, false, &block)
474
483
  end
475
-
476
484
 
477
485
  # The same as select_map, but in addition orders the array by the column.
478
486
  #
@@ -591,6 +599,49 @@ module Sequel
591
599
  h
592
600
  end
593
601
 
602
+ # Returns a hash with one column used as key and the values being an
603
+ # array of column values. If the value_column is not given or nil, uses
604
+ # the entire hash as the value.
605
+ #
606
+ # DB[:table].to_hash(:name, :id) # SELECT * FROM table
607
+ # # {'Jim'=>[1, 4, 16, ...], 'Bob'=>[2], ...}
608
+ #
609
+ # DB[:table].to_hash(:name) # SELECT * FROM table
610
+ # # {'Jim'=>[{:id=>1, :name=>'Jim'}, {:id=>4, :name=>'Jim'}, ...], 'Bob'=>[{:id=>2, :name=>'Bob'}], ...}
611
+ #
612
+ # You can also provide an array of column names for either the key_column,
613
+ # the value column, or both:
614
+ #
615
+ # DB[:table].to_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
616
+ # # {['Jim', 'Bob']=>[['Smith', 1], ['Jackson', 4], ...], ...}
617
+ #
618
+ # DB[:table].to_hash([:first, :middle]) # SELECT * FROM table
619
+ # # {['Jim', 'Bob']=>[{:id=>1, :first=>'Jim', :middle=>'Bob', :last=>'Smith'}, ...], ...}
620
+ def to_hash_groups(key_column, value_column = nil)
621
+ h = {}
622
+ if value_column
623
+ return naked.to_hash_groups(key_column, value_column) if row_proc
624
+ if value_column.is_a?(Array)
625
+ if key_column.is_a?(Array)
626
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r.values_at(*value_column)}
627
+ else
628
+ each{|r| (h[r[key_column]] ||= []) << r.values_at(*value_column)}
629
+ end
630
+ else
631
+ if key_column.is_a?(Array)
632
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r[value_column]}
633
+ else
634
+ each{|r| (h[r[key_column]] ||= []) << r[value_column]}
635
+ end
636
+ end
637
+ elsif key_column.is_a?(Array)
638
+ each{|r| (h[r.values_at(*key_column)] ||= []) << r}
639
+ else
640
+ each{|r| (h[r[key_column]] ||= []) << r}
641
+ end
642
+ h
643
+ end
644
+
594
645
  # Truncates the dataset. Returns nil.
595
646
  #
596
647
  # DB[:table].truncate # TRUNCATE table
@@ -618,6 +669,13 @@ module Sequel
618
669
  end
619
670
  end
620
671
 
672
+ # Execute the given SQL and return the number of rows deleted. This exists
673
+ # solely as an optimization, replacing with_sql(sql).delete. It's significantly
674
+ # faster as it does not require cloning the current dataset.
675
+ def with_sql_delete(sql)
676
+ execute_dui(sql)
677
+ end
678
+
621
679
  protected
622
680
 
623
681
  # Internals of #import. If primary key values are requested, use
@@ -645,6 +703,21 @@ module Sequel
645
703
 
646
704
  private
647
705
 
706
+ # Internals of +select_hash+ and +select_hash_groups+
707
+ def _select_hash(meth, key_column, value_column)
708
+ if key_column.is_a?(Array)
709
+ if value_column.is_a?(Array)
710
+ select(*(key_column + value_column)).send(meth, key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
711
+ else
712
+ select(*(key_column + [value_column])).send(meth, key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
713
+ end
714
+ elsif value_column.is_a?(Array)
715
+ select(key_column, *value_column).send(meth, hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
716
+ else
717
+ select(key_column, value_column).send(meth, hash_key_symbol(key_column), hash_key_symbol(value_column))
718
+ end
719
+ end
720
+
648
721
  # Internals of +select_map+ and +select_order_map+
649
722
  def _select_map(column, order, &block)
650
723
  ds = naked.ungraphed
@@ -165,5 +165,10 @@ module Sequel
165
165
  def uses_returning?(type)
166
166
  opts[:returning] && !@opts[:sql] && supports_returning?(type)
167
167
  end
168
+
169
+ # Whether the dataset uses WITH ROLLUP/CUBE instead of ROLLUP()/CUBE().
170
+ def uses_with_rollup?
171
+ false
172
+ end
168
173
  end
169
174
  end
@@ -17,7 +17,7 @@ module Sequel
17
17
 
18
18
  # The bind arguments to use for running this prepared statement
19
19
  attr_accessor :bind_arguments
20
-
20
+
21
21
  # Set the bind arguments based on the hash and call super.
22
22
  def call(bind_vars={}, &block)
23
23
  ds = bind(bind_vars)
@@ -46,6 +46,10 @@ module Sequel
46
46
  module PreparedStatementMethods
47
47
  PLACEHOLDER_RE = /\A\$(.*)\z/
48
48
 
49
+ # Whether to log the full SQL query. By default, just the prepared statement
50
+ # name is generally logged on adapters that support native prepared statements.
51
+ attr_accessor :log_sql
52
+
49
53
  # The type of prepared statement, should be one of :select, :first,
50
54
  # :insert, :update, or :delete
51
55
  attr_accessor :prepared_type
@@ -139,7 +143,7 @@ module Sequel
139
143
  delete
140
144
  when Array
141
145
  case @prepared_type.at(0)
142
- when :map, :to_hash
146
+ when :map, :to_hash, :to_hash_groups
143
147
  send(*@prepared_type, &block)
144
148
  end
145
149
  else
@@ -20,7 +20,7 @@ module Sequel
20
20
  # DB.select(1).where(DB[:items].exists)
21
21
  # # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
22
22
  def exists
23
- SQL::PlaceholderLiteralString.new("EXISTS ?", [self], true)
23
+ SQL::PlaceholderLiteralString.new(EXISTS, [self], true)
24
24
  end
25
25
 
26
26
  # Returns an INSERT SQL query string. See +insert+.
@@ -63,7 +63,7 @@ module Sequel
63
63
 
64
64
  if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
65
65
  columns = [columns().last]
66
- values = ['DEFAULT'.lit]
66
+ values = [DEFAULT]
67
67
  end
68
68
  clone(:columns=>columns, :values=>values)._insert_sql
69
69
  end
@@ -182,6 +182,7 @@ module Sequel
182
182
  clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
183
183
  end
184
184
 
185
+ WILDCARD = LiteralString.new('*').freeze
185
186
  ALL = ' ALL'.freeze
186
187
  AND_SEPARATOR = " AND ".freeze
187
188
  APOS = "'".freeze
@@ -200,7 +201,6 @@ module Sequel
200
201
  CASE_THEN = " THEN ".freeze
201
202
  CASE_WHEN = " WHEN ".freeze
202
203
  CAST_OPEN = 'CAST('.freeze
203
- COLUMN_ALL = '.*'.freeze
204
204
  COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
205
205
  COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
206
206
  COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
@@ -209,8 +209,9 @@ module Sequel
209
209
  CONDITION_FALSE = '(1 = 0)'.freeze
210
210
  CONDITION_TRUE = '(1 = 1)'.freeze
211
211
  COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
212
- COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
212
+ COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, WILDCARD).as(:count)
213
213
  DATASET_ALIAS_BASE_NAME = 't'.freeze
214
+ DEFAULT = LiteralString.new('DEFAULT').freeze
214
215
  DEFAULT_VALUES = " DEFAULT VALUES".freeze
215
216
  DELETE = 'DELETE'.freeze
216
217
  DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
@@ -221,6 +222,7 @@ module Sequel
221
222
  DOUBLE_QUOTE = '""'.freeze
222
223
  EQUAL = ' = '.freeze
223
224
  EXTRACT = 'extract('.freeze
225
+ EXISTS = ['EXISTS '.freeze].freeze
224
226
  FOR_UPDATE = ' FOR UPDATE'.freeze
225
227
  FORMAT_DATE = "'%Y-%m-%d'".freeze
226
228
  FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
@@ -267,6 +269,7 @@ module Sequel
267
269
  SET = ' SET '.freeze
268
270
  SPACE = ' '.freeze
269
271
  SQL_WITH = "WITH ".freeze
272
+ SPACE_WITH = " WITH ".freeze
270
273
  TILDE = '~'.freeze
271
274
  TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
272
275
  STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
@@ -276,8 +279,9 @@ module Sequel
276
279
  UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
277
280
  USING = ' USING ('.freeze
278
281
  VALUES = " VALUES ".freeze
282
+ V187 = '1.8.7'.freeze
283
+ V190 = '1.9.0'.freeze
279
284
  WHERE = " WHERE ".freeze
280
- WILDCARD = LiteralString.new('*').freeze
281
285
 
282
286
  PUBLIC_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
283
287
  literal
@@ -392,10 +396,10 @@ module Sequel
392
396
 
393
397
  # SQL fragment for specifying all columns in a given table
394
398
  def column_all_sql_append(sql, ca)
395
- quote_schema_table_append(sql, ca.table)
396
- sql << COLUMN_ALL
399
+ qualified_identifier_sql_append(sql, ca.table, WILDCARD)
397
400
  end
398
401
 
402
+ # SQL fragment for the complex expression.
399
403
  def complex_expression_sql_append(sql, op, args)
400
404
  case op
401
405
  when *IS_OPERATORS
@@ -509,7 +513,7 @@ module Sequel
509
513
  table_alias = jc.table_alias
510
514
  table_alias = nil if table == table_alias
511
515
  sql << SPACE << join_type_sql(jc.join_type) << SPACE
512
- table_ref_append(sql, table)
516
+ identifier_append(sql, table)
513
517
  as_sql_append(sql, table_alias) if table_alias
514
518
  end
515
519
 
@@ -550,11 +554,11 @@ module Sequel
550
554
  # SQL fragment for a literal string with placeholders
551
555
  def placeholder_literal_string_sql_append(sql, pls)
552
556
  args = pls.args
557
+ str = pls.str
553
558
  sql << PAREN_OPEN if pls.parens
554
559
  if args.is_a?(Hash)
555
560
  re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
556
- if RUBY_VERSION >= '1.8.7'
557
- str = pls.str
561
+ if RUBY_VERSION >= V187
558
562
  loop do
559
563
  previous, q, str = str.partition(re)
560
564
  sql << previous
@@ -562,12 +566,17 @@ module Sequel
562
566
  break if str.empty?
563
567
  end
564
568
  else
565
- sql << pls.str.gsub(re){literal(args[$1.to_sym])}
569
+ sql << str.gsub(re){literal(args[$1.to_sym])}
570
+ end
571
+ elsif str.is_a?(Array)
572
+ len = args.length
573
+ str.each_with_index do |s, i|
574
+ sql << s
575
+ literal_append(sql, args[i]) unless i == len
566
576
  end
567
577
  else
568
578
  i = -1
569
- if RUBY_VERSION >= '1.8.7'
570
- str = pls.str
579
+ if RUBY_VERSION >= V187
571
580
  loop do
572
581
  previous, q, str = str.partition(QUESTION_MARK)
573
582
  sql << previous
@@ -575,7 +584,7 @@ module Sequel
575
584
  break if str.empty?
576
585
  end
577
586
  else
578
- sql << pls.str.gsub(QUESTION_MARK_RE){literal(args.at(i+=1))}
587
+ sql << str.gsub(QUESTION_MARK_RE){literal(args.at(i+=1))}
579
588
  end
580
589
  end
581
590
  sql << PAREN_CLOSE if pls.parens
@@ -583,20 +592,12 @@ module Sequel
583
592
 
584
593
  # SQL fragment for the qualifed identifier, specifying
585
594
  # a table and a column (or schema and table).
586
- def qualified_identifier_sql_append(sql, qcr)
587
- case t = qcr.table
588
- when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
589
- literal_append(sql, t)
590
- else
591
- quote_identifier_append(sql, t)
592
- end
595
+ # If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
596
+ # column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
597
+ def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
598
+ identifier_append(sql, table)
593
599
  sql << DOT
594
- case c = qcr.column
595
- when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
596
- literal_append(sql, c)
597
- else
598
- quote_identifier_append(sql, c)
599
- end
600
+ identifier_append(sql, column)
600
601
  end
601
602
 
602
603
  # Adds quoting to identifiers (columns and tables). If identifiers are not
@@ -802,7 +803,7 @@ module Sequel
802
803
 
803
804
  # Prepare an SQL statement by calling all clause methods for the given statement type.
804
805
  def clause_sql(type)
805
- sql = @opts[:append_sql] || ''
806
+ sql = @opts[:append_sql] || sql_string_origin
806
807
  send("#{type}_clause_methods").each{|x| send(x, sql)}
807
808
  sql
808
809
  end
@@ -869,7 +870,7 @@ module Sequel
869
870
  c ||= true
870
871
  end
871
872
  end
872
-
873
+
873
874
  def empty_array_value(op, cols)
874
875
  if Sequel.empty_array_handle_nulls
875
876
  c = Array(cols)
@@ -887,7 +888,7 @@ module Sequel
887
888
  v2 = db.from_application_timestamp(v)
888
889
  fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
889
890
  if m == FORMAT_USEC
890
- format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
891
+ format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < V190 ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
891
892
  else
892
893
  if supports_timestamp_timezones?
893
894
  # Would like to just use %z format, but it doesn't appear to work on Windows
@@ -911,6 +912,24 @@ module Sequel
911
912
  sprintf(FORMAT_TIMESTAMP_USEC, usec)
912
913
  end
913
914
 
915
+ # Append the value, but special case regular (non-literal, non-blob) strings
916
+ # so that they are considered as identifiers and not SQL strings.
917
+ def identifier_append(sql, v)
918
+ if v.is_a?(String)
919
+ case v
920
+ when LiteralString
921
+ sql << v
922
+ when SQL::Blob
923
+ literal_append(sql, v)
924
+ else
925
+ quote_identifier_append(sql, v)
926
+ end
927
+ else
928
+ literal_append(sql, v)
929
+ end
930
+ end
931
+ alias table_ref_append identifier_append
932
+
914
933
  # Modify the identifier returned from the database based on the
915
934
  # identifier_output_method.
916
935
  def input_identifier(v)
@@ -937,11 +956,7 @@ module Sequel
937
956
  co = COMMA
938
957
  columns.each do |col|
939
958
  sql << co if c
940
- if col.is_a?(String) && !col.is_a?(LiteralString)
941
- quote_identifier_append(sql, col)
942
- else
943
- literal_append(sql, col)
944
- end
959
+ identifier_append(sql, col)
945
960
  c ||= true
946
961
  end
947
962
  sql << PAREN_CLOSE
@@ -1058,7 +1073,7 @@ module Sequel
1058
1073
  def literal_integer(v)
1059
1074
  v.to_s
1060
1075
  end
1061
-
1076
+
1062
1077
  # SQL fragment for nil
1063
1078
  def literal_nil
1064
1079
  NULL
@@ -1194,11 +1209,17 @@ module Sequel
1194
1209
  if group = @opts[:group]
1195
1210
  sql << GROUP_BY
1196
1211
  if go = @opts[:group_options]
1197
- sql << go.to_s.upcase
1198
- sql << PAREN_OPEN
1212
+ if uses_with_rollup?
1213
+ expression_list_append(sql, group)
1214
+ sql << SPACE_WITH << go.to_s.upcase
1215
+ else
1216
+ sql << go.to_s.upcase << PAREN_OPEN
1217
+ expression_list_append(sql, group)
1218
+ sql << PAREN_CLOSE
1219
+ end
1220
+ else
1221
+ expression_list_append(sql, group)
1199
1222
  end
1200
- expression_list_append(sql, group)
1201
- sql << PAREN_CLOSE if go
1202
1223
  end
1203
1224
  end
1204
1225
 
@@ -1300,7 +1321,7 @@ module Sequel
1300
1321
  co = COMMA
1301
1322
  sources.each do |s|
1302
1323
  sql << co if c
1303
- table_ref_append(sql, s)
1324
+ identifier_append(sql, s)
1304
1325
  c ||= true
1305
1326
  end
1306
1327
  end
@@ -1323,6 +1344,12 @@ module Sequel
1323
1344
  end
1324
1345
  end
1325
1346
 
1347
+ # The string that is appended to to create the SQL query, the empty
1348
+ # string by default
1349
+ def sql_string_origin
1350
+ ''
1351
+ end
1352
+
1326
1353
  # SQL to use if this dataset uses static SQL. Since static SQL
1327
1354
  # can be a PlaceholderLiteralString in addition to a String,
1328
1355
  # we literalize nonstrings.
@@ -1347,16 +1374,6 @@ module Sequel
1347
1374
  ds.clone(:append_sql=>sql).sql
1348
1375
  end
1349
1376
 
1350
- # SQL fragment specifying a table name.
1351
- def table_ref_append(sql, t)
1352
- if t.is_a?(String)
1353
- quote_identifier_append(sql, t)
1354
- else
1355
- literal_append(sql, t)
1356
- end
1357
- end
1358
- alias identifier_append table_ref_append
1359
-
1360
1377
  # The order of methods to call to build the UPDATE SQL statement
1361
1378
  def update_clause_methods
1362
1379
  UPDATE_CLAUSE_METHODS