composite_primary_keys 2.3.5.1 → 3.0.0.b2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/History.txt +26 -0
  2. data/README.txt +1 -1
  3. data/Rakefile +41 -51
  4. data/lib/composite_primary_keys.rb +19 -7
  5. data/lib/composite_primary_keys/association_preload.rb +75 -170
  6. data/lib/composite_primary_keys/associations.rb +98 -400
  7. data/lib/composite_primary_keys/associations/association_proxy.rb +32 -0
  8. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +30 -0
  9. data/lib/composite_primary_keys/associations/has_many_association.rb +72 -0
  10. data/lib/composite_primary_keys/associations/has_one_association.rb +19 -0
  11. data/lib/composite_primary_keys/associations/through_association_scope.rb +103 -0
  12. data/lib/composite_primary_keys/base.rb +148 -305
  13. data/lib/composite_primary_keys/calculations.rb +22 -64
  14. data/lib/composite_primary_keys/composite_arrays.rb +3 -10
  15. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
  16. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
  17. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +1 -1
  18. data/lib/composite_primary_keys/finder_methods.rb +71 -0
  19. data/lib/composite_primary_keys/fixtures.rb +1 -1
  20. data/lib/composite_primary_keys/read.rb +25 -0
  21. data/lib/composite_primary_keys/reflection.rb +30 -10
  22. data/lib/composite_primary_keys/relation.rb +31 -0
  23. data/lib/composite_primary_keys/validations/uniqueness.rb +106 -66
  24. data/lib/composite_primary_keys/version.rb +4 -4
  25. data/scripts/console.rb +1 -1
  26. data/tasks/Rakefile.rb +13 -0
  27. data/tasks/databases/mysql.rake +11 -15
  28. data/tasks/databases/oracle.rake +10 -11
  29. data/tasks/databases/postgresql.rake +10 -13
  30. data/tasks/databases/sqlite3.rake +9 -9
  31. data/test/README_tests.txt +1 -45
  32. data/test/abstract_unit.rb +17 -14
  33. data/test/connections/connection_spec.rb +19 -0
  34. data/test/connections/databases.example.yml +11 -0
  35. data/test/connections/databases.yml +13 -0
  36. data/test/connections/native_mysql/connection.rb +10 -2
  37. data/test/connections/native_oracle/connection.rb +7 -4
  38. data/test/connections/native_oracle_enhanced/connection.rb +23 -0
  39. data/test/connections/native_postgresql/connection.rb +13 -5
  40. data/test/connections/native_sqlite/connection.rb +7 -3
  41. data/test/fixtures/article_group.rb +4 -0
  42. data/test/fixtures/article_groups.yml +7 -0
  43. data/test/fixtures/db_definitions/postgresql.sql +2 -1
  44. data/test/fixtures/debug.log +133 -0
  45. data/test/fixtures/dorm.rb +3 -0
  46. data/test/fixtures/dorms.yml +2 -0
  47. data/test/fixtures/kitchen_sink.rb +3 -0
  48. data/test/fixtures/kitchen_sinks.yml +5 -0
  49. data/test/fixtures/reference_codes.yml +2 -0
  50. data/test/fixtures/reference_type.rb +1 -1
  51. data/test/fixtures/restaurant.rb +6 -0
  52. data/test/fixtures/restaurants.yml +5 -0
  53. data/test/fixtures/restaurants_suburbs.yml +11 -0
  54. data/test/fixtures/room.rb +10 -0
  55. data/test/fixtures/room_assignment.rb +4 -0
  56. data/test/fixtures/room_assignments.yml +4 -0
  57. data/test/fixtures/room_attribute.rb +3 -0
  58. data/test/fixtures/room_attribute_assignment.rb +5 -0
  59. data/test/fixtures/room_attribute_assignments.yml +4 -0
  60. data/test/fixtures/room_attributes.yml +3 -0
  61. data/test/fixtures/rooms.yml +3 -0
  62. data/test/fixtures/seat.rb +5 -0
  63. data/test/fixtures/seats.yml +4 -0
  64. data/test/fixtures/student.rb +4 -0
  65. data/test/fixtures/students.yml +2 -0
  66. data/test/test_associations.rb +27 -50
  67. data/test/test_attributes.rb +15 -19
  68. data/test/test_composite_arrays.rb +2 -21
  69. data/test/test_create.rb +3 -3
  70. data/test/test_delete.rb +7 -20
  71. data/test/test_exists.rb +3 -7
  72. data/test/test_find.rb +0 -8
  73. data/test/test_ids.rb +3 -17
  74. data/test/test_polymorphic.rb +5 -4
  75. data/test/test_suite.rb +19 -0
  76. data/test/{test_tutorial_examle.rb → test_tutorial_example.rb} +0 -0
  77. metadata +110 -72
  78. data/Manifest.txt +0 -123
  79. data/lib/adapter_helper/base.rb +0 -63
  80. data/lib/adapter_helper/mysql.rb +0 -13
  81. data/lib/adapter_helper/oracle.rb +0 -12
  82. data/lib/adapter_helper/postgresql.rb +0 -13
  83. data/lib/adapter_helper/sqlite3.rb +0 -13
  84. data/lib/composite_primary_keys/migration.rb +0 -20
  85. data/local/database_connections.rb.sample +0 -12
  86. data/local/paths.rb.sample +0 -2
  87. data/local/tasks.rb.sample +0 -2
  88. data/tasks/activerecord_selection.rake +0 -43
  89. data/tasks/databases.rake +0 -12
  90. data/tasks/deployment.rake +0 -22
  91. data/tasks/local_setup.rake +0 -13
  92. data/test/test_dummy.rb +0 -28
  93. data/tmp/test.db +0 -0
  94. data/website/index.html +0 -195
  95. data/website/index.txt +0 -159
  96. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  97. data/website/stylesheets/screen.css +0 -126
  98. data/website/template.js +0 -3
  99. data/website/template.rhtml +0 -53
  100. data/website/version-raw.js +0 -3
  101. data/website/version-raw.txt +0 -2
  102. data/website/version.js +0 -4
  103. data/website/version.txt +0 -3
@@ -1,429 +1,127 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module Associations
4
- def self.append_features(base)
5
- super
6
- base.send(:extend, ClassMethods)
7
- end
8
-
9
- # Composite key versions of Association functions
10
- module ClassMethods
11
-
12
- def construct_counter_sql_with_included_associations(options, join_dependency)
13
- scope = scope(:find)
14
- sql = "SELECT COUNT(DISTINCT #{quoted_table_columns(primary_key)})"
15
-
16
- # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
17
- if !self.connection.supports_count_distinct?
18
- sql = "SELECT COUNT(*) FROM (SELECT DISTINCT #{quoted_table_columns(primary_key)}"
19
- end
20
-
21
- sql << " FROM #{quoted_table_name} "
22
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
23
-
24
- add_joins!(sql, options[:joins], scope)
25
- add_conditions!(sql, options[:conditions], scope)
26
- add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
27
-
28
- add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
29
-
30
- if !self.connection.supports_count_distinct?
31
- sql << ")"
1
+ module ActiveRecord
2
+ module Associations
3
+ module ClassMethods
4
+ class JoinDependency
5
+ class JoinBase
6
+ def column_names_with_alias
7
+ unless defined?(@column_names_with_alias)
8
+ @column_names_with_alias = []
9
+ keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
10
+ (keys + (column_names - keys)).each_with_index do |column_name, i|
11
+ @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
12
+ end
13
+ end
14
+ @column_names_with_alias
32
15
  end
33
-
34
- return sanitize_sql(sql)
35
16
  end
36
17
 
37
- def construct_finder_sql_with_included_associations(options, join_dependency)
38
- scope = scope(:find)
39
- sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
40
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
41
-
42
- add_joins!(sql, options[:joins], scope)
43
- add_conditions!(sql, options[:conditions], scope)
44
- add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && options[:limit]
45
-
46
- sql << "ORDER BY #{options[:order]} " if options[:order]
47
-
48
- add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
49
-
50
- return sanitize_sql(sql)
51
- end
52
-
53
- def table_columns(columns)
54
- columns.collect {|column| "#{self.quoted_table_name}.#{connection.quote_column_name(column)}"}
55
- end
56
-
57
- def quoted_table_columns(columns)
58
- table_columns(columns).join(ID_SEP)
59
- end
60
-
61
- end
62
-
63
- end
64
- end
65
- end
66
-
67
- module ActiveRecord::Associations::ClassMethods
68
- class JoinDependency
69
- def construct_association(record, join, row)
70
- case join.reflection.macro
71
- when :has_many, :has_and_belongs_to_many
72
- collection = record.send(join.reflection.name)
73
- collection.loaded
18
+ class JoinAssociation
19
+ # Ugly to include this twice, but I couldn't figure out how to make this
20
+ # work via a module
21
+ def composite_join_predicates(table1, keys1, table2, keys2)
22
+ attributes1 = [keys1].flatten.map do |key|
23
+ table1[key]
24
+ end
74
25
 
75
- join_aliased_primary_keys = join.active_record.composite? ?
76
- join.aliased_primary_key : [join.aliased_primary_key]
77
- return nil if
78
- record.id.to_s != join.parent.record_id(row).to_s or not
79
- join_aliased_primary_keys.select {|key| row[key].nil?}.blank?
80
- association = join.instantiate(row)
81
- collection.target.push(association) unless collection.target.include?(association)
82
- when :has_one, :belongs_to
83
- return if record.id.to_s != join.parent.record_id(row).to_s or
84
- [*join.aliased_primary_key].any? { |key| row[key].nil? }
85
- association = join.instantiate(row)
86
- record.send("set_#{join.reflection.name}_target", association)
87
- else
88
- raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
89
- end
90
- return association
91
- end
26
+ attributes2 = [keys2].flatten.map do |key|
27
+ table2[key]
28
+ end
92
29
 
93
- class JoinBase
94
- def aliased_primary_key
95
- active_record.composite? ?
96
- primary_key.inject([]) {|aliased_keys, key| aliased_keys << "#{ aliased_prefix }_r#{aliased_keys.length}"} :
97
- "#{ aliased_prefix }_r0"
98
- end
30
+ [attributes1, attributes2].transpose.map do |attribute1, attribute2|
31
+ attribute1.eq(attribute2)
32
+ end
33
+ end
99
34
 
100
- def record_id(row)
101
- active_record.composite? ?
102
- aliased_primary_key.map {|key| row[key]}.to_composite_ids :
103
- row[aliased_primary_key]
104
- end
35
+ def association_join
36
+ return @join if @join
105
37
 
106
- def column_names_with_alias
107
- unless @column_names_with_alias
108
- @column_names_with_alias = []
109
- keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
110
- (keys + (column_names - keys)).each_with_index do |column_name, i|
111
- @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
112
- end
113
- end
114
- return @column_names_with_alias
115
- end
116
- end
38
+ aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
39
+ parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
117
40
 
118
- class JoinAssociation < JoinBase
119
- alias single_association_join association_join
120
- def association_join
121
- reflection.active_record.composite? ? composite_association_join : single_association_join
122
- end
41
+ @join = case reflection.macro
42
+ when :has_and_belongs_to_many
43
+ join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
44
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
45
+ klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
123
46
 
124
- def composite_association_join
125
- join = case reflection.macro
126
- when :has_and_belongs_to_many
127
- " LEFT OUTER JOIN %s ON %s " % [
128
- table_alias_for(options[:join_table], aliased_join_table_name),
129
- composite_join_clause(
130
- full_keys(aliased_join_table_name, options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key),
131
- full_keys(reflection.active_record.table_name, reflection.active_record.primary_key)
132
- )
133
- ] +
134
- " LEFT OUTER JOIN %s ON %s " % [
135
- table_name_and_alias,
136
- composite_join_clause(
137
- full_keys(aliased_table_name, klass.primary_key),
138
- full_keys(aliased_join_table_name, options[:association_foreign_key] || klass.table_name.classify.foreign_key)
139
- )
47
+ [
48
+ join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
49
+ aliased_table[klass.primary_key].eq(join_table[klass_fk])
140
50
  ]
141
- when :has_many, :has_one
142
- case
143
- when reflection.macro == :has_many && reflection.options[:through]
144
- through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
51
+ when :has_many, :has_one
52
+ if reflection.options[:through]
53
+ join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
54
+ jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
55
+ first_key = second_key = as_extra = nil
56
+
145
57
  if through_reflection.options[:as] # has_many :through against a polymorphic join
146
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
58
+ jt_foreign_key = through_reflection.options[:as].to_s + '_id'
59
+ jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
147
60
  else
148
- if source_reflection.macro == :has_many && source_reflection.options[:as]
149
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
61
+ jt_foreign_key = through_reflection.primary_key_name
62
+ end
63
+
64
+ case source_reflection.macro
65
+ when :has_many
66
+ if source_reflection.options[:as]
67
+ first_key = "#{source_reflection.options[:as]}_id"
68
+ second_key = options[:foreign_key] || primary_key
69
+ as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
150
70
  else
151
- case source_reflection.macro
152
- when :belongs_to
153
- first_key = primary_key
154
- second_key = options[:foreign_key] || klass.to_s.classify.foreign_key
155
- when :has_many
156
- first_key = through_reflection.klass.to_s.classify.foreign_key
157
- second_key = options[:foreign_key] || primary_key
158
- end
71
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
72
+ second_key = options[:foreign_key] || primary_key
73
+ end
159
74
 
160
- " LEFT OUTER JOIN %s ON %s " % [
161
- table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
162
- composite_join_clause(
163
- full_keys(aliased_join_table_name, through_reflection.primary_key_name),
164
- full_keys(parent.aliased_table_name, parent.primary_key)
165
- )
166
- ] +
167
- " LEFT OUTER JOIN %s ON %s " % [
168
- table_name_and_alias,
169
- composite_join_clause(
170
- full_keys(aliased_table_name, first_key),
171
- full_keys(aliased_join_table_name, second_key)
172
- )
173
- ]
75
+ unless through_reflection.klass.descends_from_active_record?
76
+ jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
77
+ end
78
+ when :belongs_to
79
+ first_key = primary_key
80
+ if reflection.options[:source_type]
81
+ second_key = source_reflection.association_foreign_key
82
+ jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
83
+ else
84
+ second_key = source_reflection.primary_key_name
174
85
  end
175
86
  end
176
87
 
177
- when reflection.macro == :has_many && reflection.options[:as]
178
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
179
- when reflection.macro == :has_one && reflection.options[:as]
180
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
88
+ [
89
+ [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
90
+ aliased_table[first_key].eq(join_table[second_key])
91
+ ]
92
+ elsif reflection.options[:as]
93
+ id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
94
+ type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
95
+ [id_rel, type_rel]
181
96
  else
182
97
  foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
183
- primary_key = options[:primary_key] || parent.primary_key
184
- " LEFT OUTER JOIN %s ON %s " % [
185
- table_name_and_alias,
186
- composite_join_clause(
187
- full_keys(aliased_table_name, foreign_key),
188
- full_keys(parent.aliased_table_name, primary_key)),
189
- ]
98
+ # CPK
99
+ # [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
100
+ composite_join_predicates(aliased_table, foreign_key,
101
+ parent_table, reflection.options[:primary_key] || parent.primary_key)
102
+ end
103
+ when :belongs_to
104
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.cpk_primary_key])]
190
105
  end
191
- when :belongs_to
192
- " LEFT OUTER JOIN %s ON %s " % [
193
- table_name_and_alias,
194
- composite_join_clause(
195
- full_keys(aliased_table_name, reflection.klass.primary_key),
196
- full_keys(parent.aliased_table_name, options[:foreign_key] || klass.to_s.foreign_key)),
197
- ]
198
- else
199
- ""
200
- end || ''
201
- join << %(AND %s.%s = %s ) % [
202
- aliased_table_name,
203
- reflection.active_record.connection.quote_column_name(reflection.active_record.inheritance_column),
204
- klass.connection.quote(klass.name)] unless klass.descends_from_active_record?
205
- join << "AND #{interpolate_sql(sanitize_sql(reflection.options[:conditions]))} " if reflection.options[:conditions]
206
- join
207
- end
208
-
209
- def full_keys(table_name, keys)
210
- connection = reflection.active_record.connection
211
- quoted_table_name = connection.quote_table_name(table_name)
212
- if keys.is_a?(Array)
213
- keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
214
- else
215
- "#{quoted_table_name}.#{connection.quote_column_name(keys)}"
216
- end
217
- end
218
-
219
- def composite_join_clause(full_keys1, full_keys2)
220
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
221
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
222
- where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
223
- "#{key1}=#{key2}"
224
- end.join(" AND ")
225
- "(#{where_clause})"
226
- end
227
- end
228
- end
229
- end
230
-
231
- module ActiveRecord::Associations
232
- class AssociationProxy #:nodoc:
233
-
234
- def composite_where_clause(full_keys, ids)
235
- full_keys = full_keys.split(CompositePrimaryKeys::ID_SEP) if full_keys.is_a?(String)
236
-
237
- if ids.is_a?(String)
238
- ids = [[ids]]
239
- elsif not ids.first.is_a?(Array) # if single comp key passed, turn into an array of 1
240
- ids = [ids.to_composite_ids]
241
- end
242
-
243
- where_clause = ids.map do |id_set|
244
- transposed = id_set.size == 1 ? [[full_keys, id_set.first]] : [full_keys, id_set].transpose
245
- transposed.map do |full_key, id|
246
- "#{full_key.to_s}=#{@reflection.klass.sanitize(id)}"
247
- end.join(" AND ")
248
- end.join(") OR (")
249
-
250
- "(#{where_clause})"
251
- end
252
-
253
- def composite_join_clause(full_keys1, full_keys2)
254
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
255
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
256
-
257
- where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
258
- "#{key1}=#{key2}"
259
- end.join(" AND ")
260
-
261
- "(#{where_clause})"
262
- end
263
-
264
- def full_composite_join_clause(table1, full_keys1, table2, full_keys2)
265
- connection = @reflection.active_record.connection
266
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
267
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
268
106
 
269
- quoted1 = connection.quote_table_name(table1)
270
- quoted2 = connection.quote_table_name(table2)
271
-
272
- where_clause = [full_keys1, full_keys2].transpose.map do |key_pair|
273
- "#{quoted1}.#{connection.quote_column_name(key_pair.first)}=#{quoted2}.#{connection.quote_column_name(key_pair.last)}"
274
- end.join(" AND ")
107
+ unless klass.descends_from_active_record?
108
+ sti_column = aliased_table[klass.inheritance_column]
109
+ sti_condition = sti_column.eq(klass.sti_name)
110
+ klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
275
111
 
276
- "(#{where_clause})"
277
- end
278
-
279
- def full_keys(table_name, keys)
280
- connection = @reflection.active_record.connection
281
- quoted_table_name = connection.quote_table_name(table_name)
282
- keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
283
- if keys.is_a?(Array)
284
- keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
285
- else
286
- "#{quoted_table_name}.#{connection.quote_column_name(keys)}"
287
- end
288
- end
289
-
290
- def full_columns_equals(table_name, keys, quoted_ids)
291
- connection = @reflection.active_record.connection
292
- quoted_table_name = connection.quote_table_name(table_name)
293
- if keys.is_a?(Symbol) or (keys.is_a?(String) and keys == keys.to_s.split(CompositePrimaryKeys::ID_SEP))
294
- return "#{quoted_table_name}.#{connection.quote_column_name(keys)} = #{quoted_ids}"
295
- end
296
- keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
297
- quoted_ids = quoted_ids.split(CompositePrimaryKeys::ID_SEP) if quoted_ids.is_a?(String)
298
- keys_ids = [keys, quoted_ids].transpose
299
- keys_ids.collect {|key, id| "(#{quoted_table_name}.#{connection.quote_column_name(key)} = #{id})"}.join(' AND ')
300
- end
301
-
302
- def set_belongs_to_association_for(record)
303
- if @reflection.options[:as]
304
- record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
305
- record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
306
- else
307
- key_values = @reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP).zip([@owner.id].flatten)
308
- key_values.each{|key, value| record[key] = value} unless @owner.new_record?
309
- end
310
- end
311
- end
312
-
313
- class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
314
- def construct_sql
315
- @reflection.options[:finder_sql] &&= interpolate_sql(@reflection.options[:finder_sql])
316
-
317
- if @reflection.options[:finder_sql]
318
- @finder_sql = @reflection.options[:finder_sql]
319
- else
320
- @finder_sql = full_columns_equals(@reflection.options[:join_table], @reflection.primary_key_name, @owner.quoted_id)
321
- @finder_sql << " AND (#{conditions})" if conditions
322
- end
323
-
324
- @join_sql = "INNER JOIN #{@reflection.active_record.connection.quote_table_name(@reflection.options[:join_table])} ON " +
325
- full_composite_join_clause(@reflection.klass.table_name, @reflection.klass.primary_key, @reflection.options[:join_table], @reflection.association_foreign_key)
326
- end
327
- end
328
-
329
- class HasManyAssociation < AssociationCollection #:nodoc:
330
- def construct_sql
331
- case
332
- when @reflection.options[:finder_sql]
333
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
334
-
335
- when @reflection.options[:as]
336
- @finder_sql =
337
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
338
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
339
- @finder_sql << " AND (#{conditions})" if conditions
340
-
341
- else
342
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
343
- @finder_sql << " AND (#{conditions})" if conditions
344
- end
345
-
346
- if @reflection.options[:counter_sql]
347
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
348
- elsif @reflection.options[:finder_sql]
349
- # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
350
- @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
351
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
352
- else
353
- @counter_sql = @finder_sql
354
- end
355
- end
356
-
357
- def delete_records(records)
358
- if @reflection.options[:dependent]
359
- records.each { |r| r.destroy }
360
- else
361
- connection = @reflection.active_record.connection
362
- field_names = @reflection.primary_key_name.split(',')
363
- field_names.collect! {|n| connection.quote_column_name(n) + " = NULL"}
364
- records.each do |r|
365
- where_clause = nil
366
-
367
- if r.quoted_id.to_s.include?(CompositePrimaryKeys::ID_SEP)
368
- where_clause_terms = [@reflection.klass.primary_key, r.quoted_id].transpose.map do |pair|
369
- "(#{connection.quote_column_name(pair[0])} = #{pair[1]})"
112
+ @join << sti_condition
370
113
  end
371
- where_clause = where_clause_terms.join(" AND ")
372
- else
373
- where_clause = connection.quote_column_name(@reflection.klass.primary_key) + ' = ' + r.quoted_id
374
- end
375
-
376
- @reflection.klass.update_all( field_names.join(',') , where_clause)
377
- end
378
- end
379
- end
380
- end
381
114
 
382
- class HasOneAssociation < BelongsToAssociation #:nodoc:
383
- def construct_sql
384
- case
385
- when @reflection.options[:as]
386
- @finder_sql =
387
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
388
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
389
- else
390
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
391
- end
392
-
393
- @finder_sql << " AND (#{conditions})" if conditions
394
- end
395
- end
396
-
397
- class HasManyThroughAssociation < HasManyAssociation #:nodoc:
398
- def construct_conditions_with_composite_keys
399
- if @reflection.through_reflection.options[:as]
400
- construct_conditions_without_composite_keys
401
- else
402
- conditions = full_columns_equals(@reflection.through_reflection.table_name, @reflection.through_reflection.primary_key_name, @owner.quoted_id)
403
- conditions << " AND (#{sql_conditions})" if sql_conditions
404
- conditions
405
- end
406
- end
407
- alias_method_chain :construct_conditions, :composite_keys
115
+ [through_reflection, reflection].each do |ref|
116
+ if ref && ref.options[:conditions]
117
+ @join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))
118
+ end
119
+ end
408
120
 
409
- def construct_joins_with_composite_keys(custom_joins = nil)
410
- if @reflection.through_reflection.options[:as] || @reflection.source_reflection.options[:as]
411
- construct_joins_without_composite_keys(custom_joins)
412
- else
413
- if @reflection.source_reflection.macro == :belongs_to
414
- reflection_primary_key = @reflection.klass.primary_key
415
- source_primary_key = @reflection.source_reflection.primary_key_name
416
- else
417
- reflection_primary_key = @reflection.source_reflection.primary_key_name
418
- source_primary_key = @reflection.klass.primary_key
121
+ @join
122
+ end
419
123
  end
420
-
421
- "INNER JOIN %s ON %s #{@reflection.options[:joins]} #{custom_joins}" % [
422
- @reflection.through_reflection.quoted_table_name,
423
- composite_join_clause(full_keys(@reflection.table_name, reflection_primary_key), full_keys(@reflection.through_reflection.table_name, source_primary_key))
424
- ]
425
124
  end
426
125
  end
427
- alias_method_chain :construct_joins, :composite_keys
428
126
  end
429
- end
127
+ end