activerecord 6.0.0.beta3 → 6.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -6
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/associations.rb +3 -2
  6. data/lib/active_record/associations/association.rb +1 -1
  7. data/lib/active_record/associations/builder/association.rb +14 -18
  8. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  9. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  10. data/lib/active_record/associations/builder/has_many.rb +2 -0
  11. data/lib/active_record/associations/builder/has_one.rb +35 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  13. data/lib/active_record/associations/collection_proxy.rb +1 -1
  14. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  15. data/lib/active_record/associations/preloader.rb +11 -6
  16. data/lib/active_record/associations/preloader/association.rb +32 -30
  17. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  18. data/lib/active_record/attribute_methods.rb +4 -3
  19. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  20. data/lib/active_record/attribute_methods/dirty.rb +42 -14
  21. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  22. data/lib/active_record/attribute_methods/query.rb +2 -3
  23. data/lib/active_record/attribute_methods/read.rb +3 -9
  24. data/lib/active_record/attribute_methods/write.rb +6 -12
  25. data/lib/active_record/attributes.rb +13 -0
  26. data/lib/active_record/autosave_association.rb +13 -3
  27. data/lib/active_record/base.rb +0 -1
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
  29. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  30. data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
  31. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  32. data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
  35. data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
  36. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
  37. data/lib/active_record/connection_adapters/column.rb +17 -13
  38. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  39. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  41. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  42. data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
  43. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  44. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  45. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  46. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  47. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
  48. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  49. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  50. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
  53. data/lib/active_record/connection_handling.rb +17 -10
  54. data/lib/active_record/core.rb +15 -20
  55. data/lib/active_record/database_configurations.rb +14 -14
  56. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  57. data/lib/active_record/database_configurations/url_config.rb +12 -12
  58. data/lib/active_record/dynamic_matchers.rb +1 -1
  59. data/lib/active_record/enum.rb +6 -0
  60. data/lib/active_record/errors.rb +1 -1
  61. data/lib/active_record/gem_version.rb +1 -1
  62. data/lib/active_record/insert_all.rb +180 -0
  63. data/lib/active_record/integration.rb +13 -1
  64. data/lib/active_record/internal_metadata.rb +5 -1
  65. data/lib/active_record/locking/optimistic.rb +3 -4
  66. data/lib/active_record/log_subscriber.rb +1 -1
  67. data/lib/active_record/migration.rb +25 -18
  68. data/lib/active_record/migration/command_recorder.rb +28 -14
  69. data/lib/active_record/migration/compatibility.rb +10 -0
  70. data/lib/active_record/persistence.rb +206 -13
  71. data/lib/active_record/querying.rb +17 -12
  72. data/lib/active_record/railties/databases.rake +68 -6
  73. data/lib/active_record/reflection.rb +2 -2
  74. data/lib/active_record/relation.rb +98 -20
  75. data/lib/active_record/relation/calculations.rb +39 -39
  76. data/lib/active_record/relation/delegation.rb +22 -30
  77. data/lib/active_record/relation/finder_methods.rb +3 -9
  78. data/lib/active_record/relation/merger.rb +7 -16
  79. data/lib/active_record/relation/query_methods.rb +153 -38
  80. data/lib/active_record/relation/where_clause.rb +9 -5
  81. data/lib/active_record/sanitization.rb +3 -2
  82. data/lib/active_record/schema_dumper.rb +5 -0
  83. data/lib/active_record/schema_migration.rb +1 -1
  84. data/lib/active_record/scoping/default.rb +6 -7
  85. data/lib/active_record/scoping/named.rb +1 -1
  86. data/lib/active_record/statement_cache.rb +2 -2
  87. data/lib/active_record/store.rb +48 -0
  88. data/lib/active_record/table_metadata.rb +3 -3
  89. data/lib/active_record/tasks/database_tasks.rb +36 -1
  90. data/lib/active_record/touch_later.rb +2 -2
  91. data/lib/active_record/transactions.rb +52 -41
  92. data/lib/active_record/validations/uniqueness.rb +3 -5
  93. data/lib/arel/insert_manager.rb +3 -3
  94. data/lib/arel/nodes.rb +2 -1
  95. data/lib/arel/nodes/comment.rb +29 -0
  96. data/lib/arel/nodes/select_core.rb +16 -12
  97. data/lib/arel/nodes/unary.rb +1 -0
  98. data/lib/arel/nodes/values_list.rb +2 -17
  99. data/lib/arel/select_manager.rb +10 -10
  100. data/lib/arel/visitors/depth_first.rb +6 -1
  101. data/lib/arel/visitors/dot.rb +7 -2
  102. data/lib/arel/visitors/ibm_db.rb +13 -0
  103. data/lib/arel/visitors/informix.rb +6 -0
  104. data/lib/arel/visitors/mssql.rb +15 -1
  105. data/lib/arel/visitors/oracle12.rb +4 -5
  106. data/lib/arel/visitors/postgresql.rb +4 -10
  107. data/lib/arel/visitors/to_sql.rb +87 -108
  108. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  109. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  110. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  111. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  112. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  113. metadata +12 -11
  114. data/lib/active_record/collection_cache_key.rb +0 -53
  115. data/lib/arel/nodes/values.rb +0 -16
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
  if finder_class.primary_key
26
26
  relation = relation.where.not(finder_class.primary_key => record.id_in_database)
27
27
  else
28
- raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
28
+ raise UnknownPrimaryKey.new(finder_class, "Cannot validate uniqueness for persisted record without primary key.")
29
29
  end
30
30
  end
31
31
  relation = scope_relation(record, relation)
@@ -60,10 +60,8 @@ module ActiveRecord
60
60
  comparison = relation.bind_attribute(attribute, value) do |attr, bind|
61
61
  return relation.none! if bind.unboundable?
62
62
 
63
- if bind.nil?
64
- attr.eq(bind)
65
- elsif !options.key?(:case_sensitive)
66
- klass.connection.default_uniqueness_comparison(attr, bind)
63
+ if !options.key?(:case_sensitive) || bind.nil?
64
+ klass.connection.default_uniqueness_comparison(attr, bind, klass)
67
65
  elsif options[:case_sensitive]
68
66
  klass.connection.case_sensitive_comparison(attr, bind)
69
67
  else
@@ -33,13 +33,13 @@ module Arel # :nodoc: all
33
33
  @ast.columns << column
34
34
  values << value
35
35
  end
36
- @ast.values = create_values values, @ast.columns
36
+ @ast.values = create_values(values)
37
37
  end
38
38
  self
39
39
  end
40
40
 
41
- def create_values(values, columns)
42
- Nodes::Values.new values, columns
41
+ def create_values(values)
42
+ Nodes::ValuesList.new([values])
43
43
  end
44
44
 
45
45
  def create_values_list(rows)
@@ -45,7 +45,6 @@ require "arel/nodes/and"
45
45
  require "arel/nodes/function"
46
46
  require "arel/nodes/count"
47
47
  require "arel/nodes/extract"
48
- require "arel/nodes/values"
49
48
  require "arel/nodes/values_list"
50
49
  require "arel/nodes/named_function"
51
50
 
@@ -62,6 +61,8 @@ require "arel/nodes/outer_join"
62
61
  require "arel/nodes/right_outer_join"
63
62
  require "arel/nodes/string_join"
64
63
 
64
+ require "arel/nodes/comment"
65
+
65
66
  require "arel/nodes/sql_literal"
66
67
 
67
68
  require "arel/nodes/casted"
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Nodes
5
+ class Comment < Arel::Nodes::Node
6
+ attr_reader :values
7
+
8
+ def initialize(values)
9
+ super()
10
+ @values = values
11
+ end
12
+
13
+ def initialize_copy(other)
14
+ super
15
+ @values = @values.clone
16
+ end
17
+
18
+ def hash
19
+ [@values].hash
20
+ end
21
+
22
+ def eql?(other)
23
+ self.class == other.class &&
24
+ self.values == other.values
25
+ end
26
+ alias :== :eql?
27
+ end
28
+ end
29
+ end
@@ -3,20 +3,22 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class SelectCore < Arel::Nodes::Node
6
- attr_accessor :projections, :wheres, :groups, :windows
7
- attr_accessor :havings, :source, :set_quantifier
6
+ attr_accessor :projections, :wheres, :groups, :windows, :comment
7
+ attr_accessor :havings, :source, :set_quantifier, :optimizer_hints
8
8
 
9
9
  def initialize
10
10
  super()
11
- @source = JoinSource.new nil
11
+ @source = JoinSource.new nil
12
12
 
13
13
  # https://ronsavage.github.io/SQL/sql-92.bnf.html#set%20quantifier
14
- @set_quantifier = nil
15
- @projections = []
16
- @wheres = []
17
- @groups = []
18
- @havings = []
19
- @windows = []
14
+ @set_quantifier = nil
15
+ @optimizer_hints = nil
16
+ @projections = []
17
+ @wheres = []
18
+ @groups = []
19
+ @havings = []
20
+ @windows = []
21
+ @comment = nil
20
22
  end
21
23
 
22
24
  def from
@@ -42,8 +44,8 @@ module Arel # :nodoc: all
42
44
 
43
45
  def hash
44
46
  [
45
- @source, @set_quantifier, @projections,
46
- @wheres, @groups, @havings, @windows
47
+ @source, @set_quantifier, @projections, @optimizer_hints,
48
+ @wheres, @groups, @havings, @windows, @comment
47
49
  ].hash
48
50
  end
49
51
 
@@ -51,11 +53,13 @@ module Arel # :nodoc: all
51
53
  self.class == other.class &&
52
54
  self.source == other.source &&
53
55
  self.set_quantifier == other.set_quantifier &&
56
+ self.optimizer_hints == other.optimizer_hints &&
54
57
  self.projections == other.projections &&
55
58
  self.wheres == other.wheres &&
56
59
  self.groups == other.groups &&
57
60
  self.havings == other.havings &&
58
- self.windows == other.windows
61
+ self.windows == other.windows &&
62
+ self.comment == other.comment
59
63
  end
60
64
  alias :== :eql?
61
65
  end
@@ -35,6 +35,7 @@ module Arel # :nodoc: all
35
35
  Not
36
36
  Offset
37
37
  On
38
+ OptimizerHints
38
39
  Ordering
39
40
  RollUp
40
41
  }.each do |name|
@@ -2,23 +2,8 @@
2
2
 
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
- class ValuesList < Node
6
- attr_reader :rows
7
-
8
- def initialize(rows)
9
- @rows = rows
10
- super()
11
- end
12
-
13
- def hash
14
- @rows.hash
15
- end
16
-
17
- def eql?(other)
18
- self.class == other.class &&
19
- self.rows == other.rows
20
- end
21
- alias :== :eql?
5
+ class ValuesList < Unary
6
+ alias :rows :expr
22
7
  end
23
8
  end
24
9
  end
@@ -146,6 +146,13 @@ module Arel # :nodoc: all
146
146
  @ctx.projections = projections
147
147
  end
148
148
 
149
+ def optimizer_hints(*hints)
150
+ unless hints.empty?
151
+ @ctx.optimizer_hints = Arel::Nodes::OptimizerHints.new(hints)
152
+ end
153
+ self
154
+ end
155
+
149
156
  def distinct(value = true)
150
157
  if value
151
158
  @ctx.set_quantifier = Arel::Nodes::Distinct.new
@@ -237,16 +244,9 @@ module Arel # :nodoc: all
237
244
  @ctx.source
238
245
  end
239
246
 
240
- class Row < Struct.new(:data) # :nodoc:
241
- def id
242
- data["id"]
243
- end
244
-
245
- def method_missing(name, *args)
246
- name = name.to_s
247
- return data[name] if data.key?(name)
248
- super
249
- end
247
+ def comment(*values)
248
+ @ctx.comment = Nodes::Comment.new(values)
249
+ self
250
250
  end
251
251
 
252
252
  private
@@ -35,6 +35,8 @@ module Arel # :nodoc: all
35
35
  alias :visit_Arel_Nodes_Ascending :unary
36
36
  alias :visit_Arel_Nodes_Descending :unary
37
37
  alias :visit_Arel_Nodes_UnqualifiedColumn :unary
38
+ alias :visit_Arel_Nodes_OptimizerHints :unary
39
+ alias :visit_Arel_Nodes_ValuesList :unary
38
40
 
39
41
  def function(o)
40
42
  visit o.expressions
@@ -102,7 +104,6 @@ module Arel # :nodoc: all
102
104
  alias :visit_Arel_Nodes_Regexp :binary
103
105
  alias :visit_Arel_Nodes_RightOuterJoin :binary
104
106
  alias :visit_Arel_Nodes_TableAlias :binary
105
- alias :visit_Arel_Nodes_Values :binary
106
107
  alias :visit_Arel_Nodes_When :binary
107
108
 
108
109
  def visit_Arel_Nodes_StringJoin(o)
@@ -180,6 +181,10 @@ module Arel # :nodoc: all
180
181
  visit o.limit
181
182
  end
182
183
 
184
+ def visit_Arel_Nodes_Comment(o)
185
+ visit o.values
186
+ end
187
+
183
188
  def visit_Array(o)
184
189
  o.each { |i| visit i }
185
190
  end
@@ -46,8 +46,8 @@ module Arel # :nodoc: all
46
46
  visit_edge o, "distinct"
47
47
  end
48
48
 
49
- def visit_Arel_Nodes_Values(o)
50
- visit_edge o, "expressions"
49
+ def visit_Arel_Nodes_ValuesList(o)
50
+ visit_edge o, "rows"
51
51
  end
52
52
 
53
53
  def visit_Arel_Nodes_StringJoin(o)
@@ -82,6 +82,7 @@ module Arel # :nodoc: all
82
82
  alias :visit_Arel_Nodes_Offset :unary
83
83
  alias :visit_Arel_Nodes_On :unary
84
84
  alias :visit_Arel_Nodes_UnqualifiedColumn :unary
85
+ alias :visit_Arel_Nodes_OptimizerHints :unary
85
86
  alias :visit_Arel_Nodes_Preceding :unary
86
87
  alias :visit_Arel_Nodes_Following :unary
87
88
  alias :visit_Arel_Nodes_Rows :unary
@@ -233,6 +234,10 @@ module Arel # :nodoc: all
233
234
  end
234
235
  alias :visit_Set :visit_Array
235
236
 
237
+ def visit_Arel_Nodes_Comment(o)
238
+ visit_edge(o, "values")
239
+ end
240
+
236
241
  def visit_edge(o, method)
237
242
  edge(method) { visit o.send(method) }
238
243
  end
@@ -4,6 +4,15 @@ module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class IBM_DB < Arel::Visitors::ToSql
6
6
  private
7
+ def visit_Arel_Nodes_SelectCore(o, collector)
8
+ collector = super
9
+ maybe_visit o.optimizer_hints, collector
10
+ end
11
+
12
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
13
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join
14
+ collector << "/* <OPTGUIDELINES>#{hints}</OPTGUIDELINES> */"
15
+ end
7
16
 
8
17
  def visit_Arel_Nodes_Limit(o, collector)
9
18
  collector << "FETCH FIRST "
@@ -16,6 +25,10 @@ module Arel # :nodoc: all
16
25
  collector = visit [o.left, o.right, 0, 1], collector
17
26
  collector << ")"
18
27
  end
28
+
29
+ def collect_optimizer_hints(o, collector)
30
+ collector
31
+ end
19
32
  end
20
33
  end
21
34
  end
@@ -42,10 +42,16 @@ module Arel # :nodoc: all
42
42
  collector
43
43
  end
44
44
 
45
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
46
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
47
+ collector << "/*+ #{hints} */"
48
+ end
49
+
45
50
  def visit_Arel_Nodes_Offset(o, collector)
46
51
  collector << "SKIP "
47
52
  visit o.expr, collector
48
53
  end
54
+
49
55
  def visit_Arel_Nodes_Limit(o, collector)
50
56
  collector << "FIRST "
51
57
  visit o.expr, collector
@@ -76,6 +76,16 @@ module Arel # :nodoc: all
76
76
  end
77
77
  end
78
78
 
79
+ def visit_Arel_Nodes_SelectCore(o, collector)
80
+ collector = super
81
+ maybe_visit o.optimizer_hints, collector
82
+ end
83
+
84
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
85
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
86
+ collector << "OPTION (#{hints})"
87
+ end
88
+
79
89
  def get_offset_limit_clause(o)
80
90
  first_row = o.offset ? o.offset.expr.to_i + 1 : 1
81
91
  last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
@@ -97,12 +107,16 @@ module Arel # :nodoc: all
97
107
  collector = visit o.relation, collector
98
108
  if o.wheres.any?
99
109
  collector << " WHERE "
100
- inject_join o.wheres, collector, AND
110
+ inject_join o.wheres, collector, " AND "
101
111
  else
102
112
  collector
103
113
  end
104
114
  end
105
115
 
116
+ def collect_optimizer_hints(o, collector)
117
+ collector
118
+ end
119
+
106
120
  def determine_order_by(orders, x)
107
121
  if orders.any?
108
122
  orders
@@ -8,11 +8,10 @@ module Arel # :nodoc: all
8
8
  def visit_Arel_Nodes_SelectStatement(o, collector)
9
9
  # Oracle does not allow LIMIT clause with select for update
10
10
  if o.limit && o.lock
11
- raise ArgumentError, <<-MSG
12
- 'Combination of limit and lock is not supported.
13
- because generated SQL statements
14
- `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.`
15
- MSG
11
+ raise ArgumentError, <<~MSG
12
+ Combination of limit and lock is not supported. Because generated SQL statements
13
+ `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.
14
+ MSG
16
15
  end
17
16
  super
18
17
  end
@@ -3,11 +3,6 @@
3
3
  module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class PostgreSQL < Arel::Visitors::ToSql
6
- CUBE = "CUBE"
7
- ROLLUP = "ROLLUP"
8
- GROUPING_SETS = "GROUPING SETS"
9
- LATERAL = "LATERAL"
10
-
11
6
  private
12
7
 
13
8
  def visit_Arel_Nodes_Matches(o, collector)
@@ -57,23 +52,22 @@ module Arel # :nodoc: all
57
52
  end
58
53
 
59
54
  def visit_Arel_Nodes_Cube(o, collector)
60
- collector << CUBE
55
+ collector << "CUBE"
61
56
  grouping_array_or_grouping_element o, collector
62
57
  end
63
58
 
64
59
  def visit_Arel_Nodes_RollUp(o, collector)
65
- collector << ROLLUP
60
+ collector << "ROLLUP"
66
61
  grouping_array_or_grouping_element o, collector
67
62
  end
68
63
 
69
64
  def visit_Arel_Nodes_GroupingSet(o, collector)
70
- collector << GROUPING_SETS
65
+ collector << "GROUPING SETS"
71
66
  grouping_array_or_grouping_element o, collector
72
67
  end
73
68
 
74
69
  def visit_Arel_Nodes_Lateral(o, collector)
75
- collector << LATERAL
76
- collector << SPACE
70
+ collector << "LATERAL "
77
71
  grouping_parentheses o, collector
78
72
  end
79
73
 
@@ -9,59 +9,6 @@ module Arel # :nodoc: all
9
9
  end
10
10
 
11
11
  class ToSql < Arel::Visitors::Visitor
12
- ##
13
- # This is some roflscale crazy stuff. I'm roflscaling this because
14
- # building SQL queries is a hotspot. I will explain the roflscale so that
15
- # others will not rm this code.
16
- #
17
- # In YARV, string literals in a method body will get duped when the byte
18
- # code is executed. Let's take a look:
19
- #
20
- # > puts RubyVM::InstructionSequence.new('def foo; "bar"; end').disasm
21
- #
22
- # == disasm: <RubyVM::InstructionSequence:foo@<compiled>>=====
23
- # 0000 trace 8
24
- # 0002 trace 1
25
- # 0004 putstring "bar"
26
- # 0006 trace 16
27
- # 0008 leave
28
- #
29
- # The `putstring` bytecode will dup the string and push it on the stack.
30
- # In many cases in our SQL visitor, that string is never mutated, so there
31
- # is no need to dup the literal.
32
- #
33
- # If we change to a constant lookup, the string will not be duped, and we
34
- # can reduce the objects in our system:
35
- #
36
- # > puts RubyVM::InstructionSequence.new('BAR = "bar"; def foo; BAR; end').disasm
37
- #
38
- # == disasm: <RubyVM::InstructionSequence:foo@<compiled>>========
39
- # 0000 trace 8
40
- # 0002 trace 1
41
- # 0004 getinlinecache 11, <ic:0>
42
- # 0007 getconstant :BAR
43
- # 0009 setinlinecache <ic:0>
44
- # 0011 trace 16
45
- # 0013 leave
46
- #
47
- # `getconstant` should be a hash lookup, and no object is duped when the
48
- # value of the constant is pushed on the stack. Hence the crazy
49
- # constants below.
50
- #
51
- # `matches` and `doesNotMatch` operate case-insensitively via Visitor subclasses
52
- # specialized for specific databases when necessary.
53
- #
54
-
55
- WHERE = " WHERE " # :nodoc:
56
- SPACE = " " # :nodoc:
57
- COMMA = ", " # :nodoc:
58
- GROUP_BY = " GROUP BY " # :nodoc:
59
- ORDER_BY = " ORDER BY " # :nodoc:
60
- WINDOW = " WINDOW " # :nodoc:
61
- AND = " AND " # :nodoc:
62
-
63
- DISTINCT = "DISTINCT" # :nodoc:
64
-
65
12
  def initialize(connection)
66
13
  super()
67
14
  @connection = connection
@@ -159,39 +106,20 @@ module Arel # :nodoc: all
159
106
  when Nodes::SqlLiteral, Nodes::BindParam
160
107
  collector = visit(value, collector)
161
108
  else
162
- collector << quote(value)
109
+ collector << quote(value).to_s
163
110
  end
164
- collector << COMMA unless k == row_len
111
+ collector << ", " unless k == row_len
165
112
  end
166
113
  collector << ")"
167
- collector << COMMA unless i == len
114
+ collector << ", " unless i == len
168
115
  }
169
116
  collector
170
117
  end
171
118
 
172
- def visit_Arel_Nodes_Values(o, collector)
173
- collector << "VALUES ("
174
-
175
- len = o.expressions.length - 1
176
- o.expressions.each_with_index { |value, i|
177
- case value
178
- when Nodes::SqlLiteral, Nodes::BindParam
179
- collector = visit value, collector
180
- else
181
- collector << quote(value).to_s
182
- end
183
- unless i == len
184
- collector << COMMA
185
- end
186
- }
187
-
188
- collector << ")"
189
- end
190
-
191
119
  def visit_Arel_Nodes_SelectStatement(o, collector)
192
120
  if o.with
193
121
  collector = visit o.with, collector
194
- collector << SPACE
122
+ collector << " "
195
123
  end
196
124
 
197
125
  collector = o.cores.inject(collector) { |c, x|
@@ -199,11 +127,11 @@ module Arel # :nodoc: all
199
127
  }
200
128
 
201
129
  unless o.orders.empty?
202
- collector << ORDER_BY
130
+ collector << " ORDER BY "
203
131
  len = o.orders.length - 1
204
132
  o.orders.each_with_index { |x, i|
205
133
  collector = visit(x, collector)
206
- collector << COMMA unless len == i
134
+ collector << ", " unless len == i
207
135
  }
208
136
  end
209
137
 
@@ -219,24 +147,34 @@ module Arel # :nodoc: all
219
147
  def visit_Arel_Nodes_SelectCore(o, collector)
220
148
  collector << "SELECT"
221
149
 
150
+ collector = collect_optimizer_hints(o, collector)
222
151
  collector = maybe_visit o.set_quantifier, collector
223
152
 
224
- collect_nodes_for o.projections, collector, SPACE
153
+ collect_nodes_for o.projections, collector, " "
225
154
 
226
155
  if o.source && !o.source.empty?
227
156
  collector << " FROM "
228
157
  collector = visit o.source, collector
229
158
  end
230
159
 
231
- collect_nodes_for o.wheres, collector, WHERE, AND
232
- collect_nodes_for o.groups, collector, GROUP_BY
233
- collect_nodes_for o.havings, collector, " HAVING ", AND
234
- collect_nodes_for o.windows, collector, WINDOW
160
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
161
+ collect_nodes_for o.groups, collector, " GROUP BY "
162
+ collect_nodes_for o.havings, collector, " HAVING ", " AND "
163
+ collect_nodes_for o.windows, collector, " WINDOW "
235
164
 
236
- collector
165
+ maybe_visit o.comment, collector
166
+ end
167
+
168
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
169
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(" ")
170
+ collector << "/*+ #{hints} */"
237
171
  end
238
172
 
239
- def collect_nodes_for(nodes, collector, spacer, connector = COMMA)
173
+ def visit_Arel_Nodes_Comment(o, collector)
174
+ collector << o.values.map { |v| "/* #{sanitize_as_sql_comment(v)} */" }.join(" ")
175
+ end
176
+
177
+ def collect_nodes_for(nodes, collector, spacer, connector = ", ")
240
178
  unless nodes.empty?
241
179
  collector << spacer
242
180
  inject_join nodes, collector, connector
@@ -248,7 +186,7 @@ module Arel # :nodoc: all
248
186
  end
249
187
 
250
188
  def visit_Arel_Nodes_Distinct(o, collector)
251
- collector << DISTINCT
189
+ collector << "DISTINCT"
252
190
  end
253
191
 
254
192
  def visit_Arel_Nodes_DistinctOn(o, collector)
@@ -257,12 +195,12 @@ module Arel # :nodoc: all
257
195
 
258
196
  def visit_Arel_Nodes_With(o, collector)
259
197
  collector << "WITH "
260
- inject_join o.children, collector, COMMA
198
+ inject_join o.children, collector, ", "
261
199
  end
262
200
 
263
201
  def visit_Arel_Nodes_WithRecursive(o, collector)
264
202
  collector << "WITH RECURSIVE "
265
- inject_join o.children, collector, COMMA
203
+ inject_join o.children, collector, ", "
266
204
  end
267
205
 
268
206
  def visit_Arel_Nodes_Union(o, collector)
@@ -295,13 +233,13 @@ module Arel # :nodoc: all
295
233
  collect_nodes_for o.partitions, collector, "PARTITION BY "
296
234
 
297
235
  if o.orders.any?
298
- collector << SPACE if o.partitions.any?
236
+ collector << " " if o.partitions.any?
299
237
  collector << "ORDER BY "
300
238
  collector = inject_join o.orders, collector, ", "
301
239
  end
302
240
 
303
241
  if o.framing
304
- collector << SPACE if o.partitions.any? || o.orders.any?
242
+ collector << " " if o.partitions.any? || o.orders.any?
305
243
  collector = visit o.framing, collector
306
244
  end
307
245
 
@@ -506,8 +444,8 @@ module Arel # :nodoc: all
506
444
  collector = visit o.left, collector
507
445
  end
508
446
  if o.right.any?
509
- collector << SPACE if o.left
510
- collector = inject_join o.right, collector, SPACE
447
+ collector << " " if o.left
448
+ collector = inject_join o.right, collector, " "
511
449
  end
512
450
  collector
513
451
  end
@@ -527,7 +465,7 @@ module Arel # :nodoc: all
527
465
  def visit_Arel_Nodes_FullOuterJoin(o, collector)
528
466
  collector << "FULL OUTER JOIN "
529
467
  collector = visit o.left, collector
530
- collector << SPACE
468
+ collector << " "
531
469
  visit o.right, collector
532
470
  end
533
471
 
@@ -541,7 +479,7 @@ module Arel # :nodoc: all
541
479
  def visit_Arel_Nodes_RightOuterJoin(o, collector)
542
480
  collector << "RIGHT OUTER JOIN "
543
481
  collector = visit o.left, collector
544
- collector << SPACE
482
+ collector << " "
545
483
  visit o.right, collector
546
484
  end
547
485
 
@@ -549,7 +487,7 @@ module Arel # :nodoc: all
549
487
  collector << "INNER JOIN "
550
488
  collector = visit o.left, collector
551
489
  if o.right
552
- collector << SPACE
490
+ collector << " "
553
491
  visit(o.right, collector)
554
492
  else
555
493
  collector
@@ -575,34 +513,66 @@ module Arel # :nodoc: all
575
513
  end
576
514
 
577
515
  def visit_Arel_Nodes_In(o, collector)
578
- if Array === o.right && !o.right.empty?
516
+ unless Array === o.right
517
+ return collect_in_clause(o.left, o.right, collector)
518
+ end
519
+
520
+ unless o.right.empty?
579
521
  o.right.delete_if { |value| unboundable?(value) }
580
522
  end
581
523
 
582
- if Array === o.right && o.right.empty?
583
- collector << "1=0"
524
+ return collector << "1=0" if o.right.empty?
525
+
526
+ in_clause_length = @connection.in_clause_length
527
+
528
+ if !in_clause_length || o.right.length <= in_clause_length
529
+ collect_in_clause(o.left, o.right, collector)
584
530
  else
585
- collector = visit o.left, collector
586
- collector << " IN ("
587
- visit(o.right, collector) << ")"
531
+ collector << "("
532
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
533
+ collector << " OR " unless i == 0
534
+ collect_in_clause(o.left, right, collector)
535
+ end
536
+ collector << ")"
588
537
  end
589
538
  end
590
539
 
540
+ def collect_in_clause(left, right, collector)
541
+ collector = visit left, collector
542
+ collector << " IN ("
543
+ visit(right, collector) << ")"
544
+ end
545
+
591
546
  def visit_Arel_Nodes_NotIn(o, collector)
592
- if Array === o.right && !o.right.empty?
547
+ unless Array === o.right
548
+ return collect_not_in_clause(o.left, o.right, collector)
549
+ end
550
+
551
+ unless o.right.empty?
593
552
  o.right.delete_if { |value| unboundable?(value) }
594
553
  end
595
554
 
596
- if Array === o.right && o.right.empty?
597
- collector << "1=1"
555
+ return collector << "1=1" if o.right.empty?
556
+
557
+ in_clause_length = @connection.in_clause_length
558
+
559
+ if !in_clause_length || o.right.length <= in_clause_length
560
+ collect_not_in_clause(o.left, o.right, collector)
598
561
  else
599
- collector = visit o.left, collector
600
- collector << " NOT IN ("
601
- collector = visit o.right, collector
602
- collector << ")"
562
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
563
+ collector << " AND " unless i == 0
564
+ collect_not_in_clause(o.left, right, collector)
565
+ end
566
+ collector
603
567
  end
604
568
  end
605
569
 
570
+ def collect_not_in_clause(left, right, collector)
571
+ collector = visit left, collector
572
+ collector << " NOT IN ("
573
+ visit(right, collector) << ")"
574
+ end
575
+
606
576
  def visit_Arel_Nodes_And(o, collector)
607
577
  inject_join o.children, collector, " AND "
608
578
  end
@@ -799,6 +769,15 @@ module Arel # :nodoc: all
799
769
  @connection.quote_column_name(name)
800
770
  end
801
771
 
772
+ def sanitize_as_sql_comment(value)
773
+ return value if Arel::Nodes::SqlLiteral === value
774
+ @connection.sanitize_as_sql_comment(value)
775
+ end
776
+
777
+ def collect_optimizer_hints(o, collector)
778
+ maybe_visit o.optimizer_hints, collector
779
+ end
780
+
802
781
  def maybe_visit(thing, collector)
803
782
  return collector unless thing
804
783
  collector << " "