composite_primary_keys 8.1.1 → 8.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +4 -1
  3. data/lib/composite_primary_keys.rb +5 -1
  4. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +24 -0
  5. data/lib/composite_primary_keys/associations/association_scope.rb +32 -58
  6. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -22
  7. data/lib/composite_primary_keys/associations/preloader/association.rb +12 -6
  8. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +19 -19
  9. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +1 -2
  10. data/lib/composite_primary_keys/attribute_methods/read.rb +2 -2
  11. data/lib/composite_primary_keys/attribute_methods/write.rb +1 -1
  12. data/lib/composite_primary_keys/composite_predicates.rb +55 -50
  13. data/lib/composite_primary_keys/composite_relation.rb +48 -48
  14. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -2
  15. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +19 -46
  16. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +8 -4
  17. data/lib/composite_primary_keys/fixtures.rb +26 -22
  18. data/lib/composite_primary_keys/locking/optimistic.rb +54 -55
  19. data/lib/composite_primary_keys/relation.rb +15 -8
  20. data/lib/composite_primary_keys/relation/batches.rb +23 -19
  21. data/lib/composite_primary_keys/relation/calculations.rb +4 -2
  22. data/lib/composite_primary_keys/relation/finder_methods.rb +33 -27
  23. data/lib/composite_primary_keys/relation/predicate_builder.rb +18 -3
  24. data/lib/composite_primary_keys/relation/query_methods.rb +41 -41
  25. data/lib/composite_primary_keys/sanitization.rb +7 -5
  26. data/lib/composite_primary_keys/validations/uniqueness.rb +20 -16
  27. data/lib/composite_primary_keys/version.rb +1 -1
  28. data/tasks/databases/oracle.rake +27 -25
  29. data/tasks/databases/oracle_enhanced.rake +27 -0
  30. data/test/connections/databases.ci.yml +15 -15
  31. data/test/connections/native_oracle/connection.rb +11 -11
  32. data/test/connections/native_oracle_enhanced/connection.rb +16 -16
  33. data/test/fixtures/comment.rb +7 -7
  34. data/test/fixtures/db_definitions/db2-create-tables.sql +126 -126
  35. data/test/fixtures/db_definitions/db2-drop-tables.sql +18 -18
  36. data/test/fixtures/db_definitions/oracle.drop.sql +48 -45
  37. data/test/fixtures/db_definitions/oracle.sql +236 -223
  38. data/test/fixtures/dorm.rb +2 -2
  39. data/test/fixtures/membership.rb +6 -6
  40. data/test/fixtures/membership_statuses.yml +16 -16
  41. data/test/fixtures/memberships.yml +10 -10
  42. data/test/fixtures/product_tariffs.yml +14 -14
  43. data/test/fixtures/reference_code.rb +7 -7
  44. data/test/fixtures/restaurants_suburb.rb +2 -2
  45. data/test/fixtures/suburb.rb +5 -5
  46. data/test/fixtures/topic.rb +5 -5
  47. data/test/fixtures/topic_source.rb +6 -6
  48. data/test/fixtures/topic_sources.yml +3 -3
  49. data/test/fixtures/topics.yml +8 -8
  50. data/test/fixtures/users.yml +10 -10
  51. data/test/test_attribute_methods.rb +63 -63
  52. data/test/test_calculations.rb +42 -37
  53. data/test/test_callbacks.rb +99 -99
  54. data/test/test_delete.rb +28 -21
  55. data/test/test_delete_all.rb +5 -4
  56. data/test/test_dumpable.rb +15 -15
  57. data/test/test_nested_attributes.rb +124 -124
  58. data/test/test_optimistic.rb +18 -18
  59. data/test/test_polymorphic.rb +1 -1
  60. data/test/test_predicates.rb +40 -40
  61. data/test/test_santiago.rb +23 -23
  62. data/test/test_suite.rb +34 -34
  63. data/test/test_touch.rb +23 -23
  64. data/test/test_update.rb +71 -71
  65. metadata +9 -8
  66. data/lib/composite_primary_keys/model_schema.rb +0 -15
@@ -21,7 +21,7 @@ module CompositePrimaryKeys
21
21
  column_alias = column_name
22
22
 
23
23
  bind_values = nil
24
-
24
+
25
25
  # CPK
26
26
  # if operation == "count" && (relation.limit_value || relation.offset_value)
27
27
  if operation == "count"
@@ -36,6 +36,7 @@ module CompositePrimaryKeys
36
36
  select_value = operation_over_aggregate_column(column, operation, distinct)
37
37
 
38
38
  column_alias = select_value.alias
39
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
39
40
  relation.select_values = [select_value]
40
41
 
41
42
  query_builder = relation.arel
@@ -69,6 +70,7 @@ module CompositePrimaryKeys
69
70
  subquery = relation.arel.as(subquery_alias)
70
71
 
71
72
  sm = Arel::SelectManager.new relation.engine
73
+ sm.bind_values = relation.arel.bind_values
72
74
  # CPK
73
75
  # select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
74
76
  select_value = operation_over_aggregate_column(Arel.sql("*"), 'count', false)
@@ -76,4 +78,4 @@ module CompositePrimaryKeys
76
78
  end
77
79
  end
78
80
  end
79
- end
81
+ end
@@ -11,7 +11,7 @@ module CompositePrimaryKeys
11
11
  if relation.limit_value
12
12
  limited_ids = limited_ids_for(relation)
13
13
  # CPK
14
- #limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
14
+ # limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
15
15
  limited_ids.empty? ? relation.none! : relation.where!(cpk_in_predicate(table, self.primary_keys, limited_ids))
16
16
  end
17
17
  relation.except(:limit, :offset)
@@ -20,16 +20,17 @@ module CompositePrimaryKeys
20
20
 
21
21
  def limited_ids_for(relation)
22
22
  # CPK
23
- #values = @klass.connection.columns_for_distinct(
24
- # "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
23
+ # values = @klass.connection.columns_for_distinct(
24
+ # "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
25
25
  columns = @klass.primary_keys.map do |key|
26
26
  "#{quoted_table_name}.#{connection.quote_column_name(key)}"
27
27
  end
28
28
  values = @klass.connection.columns_for_distinct(columns, relation.order_values)
29
29
 
30
30
  relation = relation.except(:select).select(values).distinct!
31
+ arel = relation.arel
31
32
 
32
- id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
33
+ id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
33
34
 
34
35
  # CPK
35
36
  #id_rows.map {|row| row[primary_key]}
@@ -37,12 +38,14 @@ module CompositePrimaryKeys
37
38
  end
38
39
 
39
40
  def exists?(conditions = :none)
40
- # conditions can be:
41
- # Array - ['department_id = ? and location_id = ?', 1, 1]
42
- # Array -> [1,2]
43
- # CompositeKeys -> [1,2]
41
+ if ::ActiveRecord::Base === conditions
42
+ conditions = conditions.id
43
+ ::ActiveSupport::Deprecation.warn(<<-MSG.squish)
44
+ You are passing an instance of ActiveRecord::Base to `exists?`.
45
+ Please pass the id of the object by calling `.id`
46
+ MSG
47
+ end
44
48
 
45
- conditions = conditions.id if ::ActiveRecord::Base === conditions
46
49
  return false if !conditions
47
50
 
48
51
  relation = apply_join_dependency(self, construct_join_dependency)
@@ -50,14 +53,14 @@ module CompositePrimaryKeys
50
53
 
51
54
  relation = relation.except(:select, :order).select(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit(1)
52
55
 
53
- # CPK
54
- #case conditions
55
- #when Array, Hash
56
- # relation = relation.where(conditions)
57
- #else
58
- # relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
59
- #end
60
-
56
+ # case conditions
57
+ # when Array, Hash
58
+ # relation = relation.where(conditions)
59
+ # else
60
+ # unless conditions == :none
61
+ # relation = relation.where(primary_key => conditions)
62
+ # end
63
+ # end
61
64
  case conditions
62
65
  when CompositePrimaryKeys::CompositeKeys
63
66
  relation = relation.where(cpk_id_predicate(table, primary_key, conditions))
@@ -80,14 +83,14 @@ module CompositePrimaryKeys
80
83
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
81
84
 
82
85
  # CPK
83
- #expects_array = ids.first.kind_of?(Array)
86
+ # expects_array = ids.first.kind_of?(Array)
84
87
  ids = CompositePrimaryKeys.normalize(ids)
85
88
  expects_array = ids.flatten != ids.flatten(1)
86
89
 
87
90
  return ids.first if expects_array && ids.first.empty?
88
91
 
89
92
  # CPK
90
- #ids = ids.flatten.compact.uniq
93
+ # ids = ids.flatten.compact.uniq
91
94
  ids = expects_array ? ids.first : ids
92
95
 
93
96
  case ids.size
@@ -105,24 +108,27 @@ module CompositePrimaryKeys
105
108
 
106
109
  def find_one(id)
107
110
  # CPK
108
- #id = id.id if ActiveRecord::Base === id
109
- id = id.id if ::ActiveRecord::Base === id
111
+ # if ActiveRecord::Base === id
112
+ if ::ActiveRecord::Base === id
113
+ id = id.id
114
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
115
+ You are passing an instance of ActiveRecord::Base to `find`.
116
+ Please pass the id of the object by calling `.id`
117
+ MSG
118
+ end
110
119
 
111
- # CPK
112
- #column = columns_hash[primary_key]
113
- #substitute = connection.substitute_at(column, bind_values.length)
114
- #relation = where(table[primary_key].eq(substitute))
115
- #relation.bind_values += [[column, id]]
116
- #record = relation.take
117
120
  relation = self
118
121
  values = primary_keys.each_with_index.map do |primary_key, i|
119
122
  column = columns_hash[primary_key]
120
123
  relation.bind_values += [[column, id[i]]]
121
124
  connection.substitute_at(column, bind_values.length - 1)
122
125
  end
126
+
123
127
  relation = relation.where(cpk_id_predicate(table, primary_keys, values))
124
128
  record = relation.take
129
+
125
130
  raise_record_not_found_exception!(id, 0, 1) unless record
131
+
126
132
  record
127
133
  end
128
134
 
@@ -2,12 +2,27 @@ module ActiveRecord
2
2
  class PredicateBuilder
3
3
  def self.expand(klass, table, column, value)
4
4
  queries = []
5
- if klass && reflection = klass.reflect_on_association(column.to_sym)
6
- if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
5
+
6
+ # Find the foreign key when using queries such as:
7
+ # Post.where(author: author)
8
+ #
9
+ # For polymorphic relationships, find the foreign key and type:
10
+ # PriceEstimate.where(estimate_of: treasure)
11
+ if klass && reflection = klass._reflect_on_association(column)
12
+ base_class = polymorphic_base_class_from_value(value)
13
+
14
+ if reflection.polymorphic? && base_class
7
15
  queries << build(table[reflection.foreign_type], base_class)
8
16
  end
9
17
 
10
18
  column = reflection.foreign_key
19
+
20
+ # CPK
21
+ # if base_class
22
+ if base_class && !(Base === value && value.composite?)
23
+ primary_key = reflection.association_primary_key(base_class)
24
+ value = convert_value_to_association_ids(value, primary_key)
25
+ end
11
26
  end
12
27
 
13
28
  #CPK
@@ -19,4 +34,4 @@ module ActiveRecord
19
34
  queries
20
35
  end
21
36
  end
22
- end
37
+ end
@@ -1,41 +1,41 @@
1
- module CompositePrimaryKeys::ActiveRecord::QueryMethods
2
-
3
- def reverse_sql_order(order_query)
4
- # CPK
5
- # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
6
-
7
- # break apart CPKs
8
- order_query = primary_key.map do |key|
9
- "#{quoted_table_name}.#{connection.quote_column_name(key)} ASC"
10
- end if order_query.empty?
11
-
12
- order_query.map do |o|
13
- case o
14
- when Arel::Nodes::Ordering
15
- o.reverse
16
- when String, Symbol
17
- o.to_s.split(',').collect do |s|
18
- s.strip!
19
- s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
20
- end
21
- else
22
- o
23
- end
24
- end.flatten
25
- end
26
-
27
-
28
- def order(*args)
29
- args.map! do |arg|
30
- if arg.is_a?(Arel::Nodes::Ordering) && arg.expr.name.is_a?(Array)
31
- arg = arg.expr.name.map do |key|
32
- cloned_node = arg.clone
33
- cloned_node.expr.name = key
34
- cloned_node
35
- end
36
- end
37
- arg
38
- end if composite?
39
- super(*args)
40
- end
41
- end
1
+ module CompositePrimaryKeys::ActiveRecord::QueryMethods
2
+
3
+ def reverse_sql_order(order_query)
4
+ # CPK
5
+ # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
6
+
7
+ # break apart CPKs
8
+ order_query = primary_key.map do |key|
9
+ "#{quoted_table_name}.#{connection.quote_column_name(key)} ASC"
10
+ end if order_query.empty?
11
+
12
+ order_query.map do |o|
13
+ case o
14
+ when Arel::Nodes::Ordering
15
+ o.reverse
16
+ when String, Symbol
17
+ o.to_s.split(',').collect do |s|
18
+ s.strip!
19
+ s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
20
+ end
21
+ else
22
+ o
23
+ end
24
+ end.flatten
25
+ end
26
+
27
+
28
+ def order(*args)
29
+ args.map! do |arg|
30
+ if arg.is_a?(Arel::Nodes::Ordering) && arg.expr.name.is_a?(Array)
31
+ arg = arg.expr.name.map do |key|
32
+ cloned_node = arg.clone
33
+ cloned_node.expr.name = key
34
+ cloned_node
35
+ end
36
+ end
37
+ arg
38
+ end if composite?
39
+ super(*args)
40
+ end
41
+ end
@@ -16,6 +16,8 @@ module ActiveRecord
16
16
  def expand_hash_conditions_for_aggregates(attrs)
17
17
  expanded_attrs = {}
18
18
  attrs.each do |attr, value|
19
+ # CPK
20
+ # if aggregation = reflect_on_aggregation(attr.to_sym)
19
21
  if attr.is_a?(CompositePrimaryKeys::CompositeKeys)
20
22
  attr.each_with_index do |key,i|
21
23
  expanded_attrs[key] = value.respond_to?(:flatten) ? value.flatten[i] : value
@@ -38,13 +40,13 @@ module ActiveRecord
38
40
 
39
41
  def quoted_id
40
42
  # CPK
41
- #quote_value(id, column_for_attribute(self.class.primary_key))
43
+ # self.class.quote_value(id, column_for_attribute(self.class.primary_key))
42
44
  if self.composite?
43
- [self.class.primary_keys, ids].
44
- transpose.
45
- map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}
45
+ [self.class.primary_keys, ids].transpose.map { |attr_name,id|
46
+ self.class.quote_value(id, column_for_attribute(attr_name))
47
+ }
46
48
  else
47
- quote_value(id, column_for_attribute(self.class.primary_key))
49
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
48
50
  end
49
51
  end
50
52
  end
@@ -6,24 +6,28 @@ module ActiveRecord
6
6
  table = finder_class.arel_table
7
7
  value = map_enum_attribute(finder_class, attribute, value)
8
8
 
9
- relation = build_relation(finder_class, table, attribute, value)
10
- # CPK
11
- # relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
12
- if record.persisted?
13
- not_eq_conditions = Array(finder_class.primary_key).zip(Array(record.send(:id))).map do |name, value|
14
- table[name.to_sym].not_eq(value)
15
- end
9
+ begin
10
+ relation = build_relation(finder_class, table, attribute, value)
11
+ # CPK
12
+ # relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
13
+ if record.persisted?
14
+ not_eq_conditions = Array(finder_class.primary_key).zip(Array(record.send(:id))).map do |name, value|
15
+ table[name.to_sym].not_eq(value)
16
+ end
16
17
 
17
- condition = not_eq_conditions.shift
18
- not_eq_conditions.each do |not_eq_condition|
19
- condition = condition.or(not_eq_condition)
18
+ condition = not_eq_conditions.shift
19
+ not_eq_conditions.each do |not_eq_condition|
20
+ condition = condition.or(not_eq_condition)
21
+ end
22
+ relation = relation.and(condition)
20
23
  end
21
- relation = relation.and(condition)
24
+ # End CPK
25
+ relation = scope_relation(record, table, relation)
26
+ relation = finder_class.unscoped.where(relation)
27
+ relation = relation.merge(options[:conditions]) if options[:conditions]
28
+ rescue RangeError
29
+ relation = finder_class.none
22
30
  end
23
-
24
- relation = scope_relation(record, table, relation)
25
- relation = finder_class.unscoped.where(relation)
26
- relation = relation.merge(options[:conditions]) if options[:conditions]
27
31
 
28
32
  if relation.exists?
29
33
  error_options = options.except(:case_sensitive, :scope, :conditions)
@@ -34,4 +38,4 @@ module ActiveRecord
34
38
  end
35
39
  end
36
40
  end
37
- end
41
+ end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 8
4
4
  MINOR = 1
5
- TINY = 1
5
+ TINY = 2
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -1,25 +1,27 @@
1
- require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
- require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
-
4
- namespace :oracle do
5
- desc 'Build the Oracle test database'
6
- task :build_database => :load_connection do
7
- options_str = connection_string
8
-
9
- schema = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
- sh %( sqlplus #{options_str} < #{schema} )
11
- end
12
-
13
- desc 'Drop the Oracle test database'
14
- task :drop_database => :load_connection do
15
- options_str = connection_string
16
- sh %( sqlplus #{options_str} < #{File.join(SCHEMA_PATH, 'oracle.drop.sql')} )
17
- end
18
-
19
- desc 'Rebuild the Oracle test database'
20
- task :rebuild_database => [:drop_database, :build_database]
21
-
22
- task :load_connection do
23
- require File.join(PROJECT_ROOT, "test", "connections", "native_oracle", "connection")
24
- end
25
- end
1
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
+
4
+ namespace :oracle do
5
+ desc 'Build the Oracle test database'
6
+ task :build_database => :load_connection do
7
+ options_str = connection_string
8
+
9
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
+ sh %( sqlplus #{options_str} < #{sql} )
11
+ end
12
+
13
+ desc 'Drop the Oracle test database'
14
+ task :drop_database => :load_connection do
15
+ options_str = connection_string
16
+
17
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.drop.sql')
18
+ sh %( sqlplus #{options_str} < #{sql} )
19
+ end
20
+
21
+ desc 'Rebuild the Oracle test database'
22
+ task :rebuild_database => [:drop_database, :build_database]
23
+
24
+ task :load_connection do
25
+ require File.join(PROJECT_ROOT, "test", "connections", "native_oracle", "connection")
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
+
4
+ namespace :oracle_enhanced do
5
+ desc 'Build the Oracle test database'
6
+ task :build_database => :load_connection do
7
+ options_str = connection_string
8
+
9
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
+ sh %( sqlplus #{options_str} < #{sql} )
11
+ end
12
+
13
+ desc 'Drop the Oracle test database'
14
+ task :drop_database => :load_connection do
15
+ options_str = connection_string
16
+
17
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.drop.sql')
18
+ sh %( sqlplus #{options_str} < #{sql} )
19
+ end
20
+
21
+ desc 'Rebuild the Oracle test database'
22
+ task :rebuild_database => [:drop_database, :build_database]
23
+
24
+ task :load_connection do
25
+ require File.join(PROJECT_ROOT, "test", "connections", "native_oracle_enhanced", "connection")
26
+ end
27
+ end
@@ -1,15 +1,15 @@
1
- mysql:
2
- adapter: mysql2
3
- username: travis
4
- password: ""
5
- database: composite_primary_keys_unittest
6
-
7
- sqlite3:
8
- adapter: sqlite3
9
- database: db/composite_primary_keys_unittest.sqlite
10
-
11
- postgresql:
12
- adapter: postgresql
13
- database: composite_primary_keys_unittest
14
- username: postgres
15
- host: localhost
1
+ mysql:
2
+ adapter: mysql2
3
+ username: travis
4
+ password: ""
5
+ database: composite_primary_keys_unittest
6
+
7
+ sqlite3:
8
+ adapter: sqlite3
9
+ database: db/composite_primary_keys_unittest.sqlite
10
+
11
+ postgresql:
12
+ adapter: postgresql
13
+ database: composite_primary_keys_unittest
14
+ username: postgres
15
+ host: localhost