composite_primary_keys 8.1.1 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -1,48 +1,48 @@
1
- module CompositePrimaryKeys
2
- module CompositeRelation
3
- include CompositePrimaryKeys::ActiveRecord::Batches
4
- include CompositePrimaryKeys::ActiveRecord::Calculations
5
- include CompositePrimaryKeys::ActiveRecord::FinderMethods
6
- include CompositePrimaryKeys::ActiveRecord::QueryMethods
7
-
8
- def delete(id_or_array)
9
- # CPK
10
- if self.composite?
11
- id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
12
- [id_or_array]
13
- else
14
- Array(id_or_array)
15
- end
16
-
17
- id_or_array.each do |id|
18
- # Is the passed in id actually a record?
19
- id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
20
- where(cpk_id_predicate(table, self.primary_key, id)).delete_all
21
- end
22
- else
23
- where(primary_key => id_or_array).delete_all
24
- end
25
- end
26
-
27
- def destroy(id_or_array)
28
- # Without CPK:
29
- #if id.is_a?(Array)
30
- # id.map { |one_id| destroy(one_id) }
31
- #else
32
- # find(id).destroy
33
- #end
34
-
35
- id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
36
- [id_or_array]
37
- else
38
- Array(id_or_array)
39
- end
40
-
41
- id_or_array.each do |id|
42
- where(cpk_id_predicate(table, self.primary_key, id)).each do |record|
43
- record.destroy
44
- end
45
- end
46
- end
47
- end
48
- end
1
+ module CompositePrimaryKeys
2
+ module CompositeRelation
3
+ include CompositePrimaryKeys::ActiveRecord::Batches
4
+ include CompositePrimaryKeys::ActiveRecord::Calculations
5
+ include CompositePrimaryKeys::ActiveRecord::FinderMethods
6
+ include CompositePrimaryKeys::ActiveRecord::QueryMethods
7
+
8
+ def delete(id_or_array)
9
+ # CPK
10
+ if self.composite?
11
+ id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
12
+ [id_or_array]
13
+ else
14
+ Array(id_or_array)
15
+ end
16
+
17
+ id_or_array.each do |id|
18
+ # Is the passed in id actually a record?
19
+ id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
20
+ where(cpk_id_predicate(table, self.primary_key, id)).delete_all
21
+ end
22
+ else
23
+ where(primary_key => id_or_array).delete_all
24
+ end
25
+ end
26
+
27
+ def destroy(id_or_array)
28
+ # Without CPK:
29
+ #if id.is_a?(Array)
30
+ # id.map { |one_id| destroy(one_id) }
31
+ #else
32
+ # find(id).destroy
33
+ #end
34
+
35
+ id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
36
+ [id_or_array]
37
+ else
38
+ Array(id_or_array)
39
+ end
40
+
41
+ id_or_array.each do |id|
42
+ where(cpk_id_predicate(table, self.primary_key, id)).each do |record|
43
+ record.destroy
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,10 +1,10 @@
1
1
  module ActiveRecord
2
2
  class Base
3
3
  def self.load_cpk_adapter(adapter)
4
- if (adapter.to_s =~ /postgresql/) or (adapter.to_s =~ /postgis/)
4
+ if adapter.to_s =~ /postgresql/ || adapter.to_s =~ /postgis/
5
5
  require "composite_primary_keys/connection_adapters/postgresql_adapter.rb"
6
6
  end
7
- if (adapter.to_s =~ /sqlserver/)
7
+ if adapter.to_s =~ /sqlserver/
8
8
  require "composite_primary_keys/connection_adapters/sqlserver_adapter.rb"
9
9
  end
10
10
  end
@@ -1,46 +1,19 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- class PostgreSQLAdapter
4
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
5
- unless pk
6
- # Extract the table from the insert sql. Yuck.
7
- table_ref = extract_table_ref_from_insert_sql(sql)
8
- pk = primary_key(table_ref) if table_ref
9
- end
10
-
11
- if pk
12
- # CPK
13
- # select_value("#{sql} RETURNING #{quote_column_name(pk)}")
14
- select_value("#{sql} RETURNING #{quote_column_names(pk)}")
15
- else
16
- super
17
- end
18
- end
19
- alias :create :insert
20
-
21
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
22
- unless pk
23
- # Extract the table from the insert sql. Yuck.
24
- table_ref = extract_table_ref_from_insert_sql(sql)
25
- pk = primary_key(table_ref) if table_ref
26
- end
27
-
28
- # CPK
29
- # sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
30
- sql = "#{sql} RETURNING #{quote_column_names(pk)}" if pk
31
-
32
- [sql, binds]
33
- end
34
-
35
- # Returns a single value if query returns a single element
36
- # otherwise returns an array coresponding to the composite keys
37
- #
38
- def last_inserted_id(result)
39
- row = result && result.rows.first
40
- if Array === row
41
- row.size == 1 ? row[0] : row
42
- end
43
- end
44
- end
45
- end
46
- end
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module Quoting
5
+ def quote_column_name(name)
6
+ # CPK
7
+ # PGconn.quote_ident(name.to_s)
8
+ if name.is_a?(Array)
9
+ name.map do |column|
10
+ PGconn.quote_ident(column.to_s)
11
+ end.join(', ')
12
+ else
13
+ PGconn.quote_ident(name.to_s)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,15 +3,19 @@ module ActiveRecord
3
3
  class SQLServerAdapter
4
4
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
5
5
  sql = if pk && self.class.use_output_inserted
6
- # support composite primary keys consisting of more than one column name
6
+ # CPK
7
+ # quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
8
+ # sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
7
9
  quoted_pks = [pk].flatten.map {|pk| "INSERTED.#{SQLServer::Utils.extract_identifiers(pk).quoted}"}
8
10
  sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
9
- # p sql
10
11
  else
11
12
  "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
12
13
  end
14
+
15
+ # CPK
16
+ # super
13
17
  [sql, binds]
14
18
  end
15
- end
16
- end
19
+ end
20
+ end
17
21
  end
@@ -1,22 +1,26 @@
1
- module ActiveRecord
2
- class Fixture
3
- def find
4
- if model_class
5
- # CPK
6
- # model_class.find(fixture[model_class.primary_key])
7
- ids = self.ids(model_class.primary_key)
8
- model_class.find(ids)
9
- else
10
- raise FixtureClassNotFound, "No class attached to find."
11
- end
12
- end
13
-
14
- def ids(key)
15
- if key.is_a? Array
16
- key.map {|a_key| fixture[a_key.to_s] }
17
- else
18
- fixture[key]
19
- end
20
- end
21
- end
22
- end
1
+ module ActiveRecord
2
+ class Fixture
3
+ def find
4
+ if model_class
5
+ # CPK
6
+ # model_class.unscoped do
7
+ # model_class.find(fixture[model_class.primary_key])
8
+ # end
9
+ model_class.unscoped do
10
+ ids = self.ids(model_class.primary_key)
11
+ model_class.find(ids)
12
+ end
13
+ else
14
+ raise FixtureClassNotFound, "No class attached to find."
15
+ end
16
+ end
17
+
18
+ def ids(key)
19
+ if key.is_a? Array
20
+ key.map {|a_key| fixture[a_key.to_s] }
21
+ else
22
+ fixture[key]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,55 +1,54 @@
1
- module ActiveRecord
2
- module Locking
3
- module Optimistic
4
- private
5
- def _update_record(attribute_names = @attributes.keys) #:nodoc:
6
- return super unless locking_enabled?
7
- return 0 if attribute_names.empty?
8
-
9
- lock_col = self.class.locking_column
10
- previous_lock_value = send(lock_col).to_i
11
- increment_lock
12
-
13
- attribute_names += [lock_col]
14
- attribute_names.uniq!
15
-
16
- begin
17
- relation = self.class.unscoped
18
-
19
- if self.composite?
20
- stmt = relation.where(
21
- relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was).and(
22
- relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
23
- )
24
- ).arel.compile_update(
25
- arel_attributes_with_values_for_update(attribute_names),
26
- self.class.primary_key
27
- )
28
- else
29
- stmt = relation.where(
30
- relation.table[self.class.primary_key].eq(id).and(
31
- relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
32
- )
33
- ).arel.compile_update(
34
- arel_attributes_with_values_for_update(attribute_names),
35
- self.class.primary_key
36
- )
37
- end
38
-
39
- affected_rows = self.class.connection.update stmt
40
-
41
- unless affected_rows == 1
42
- raise ActiveRecord::StaleObjectError.new(self, "update")
43
- end
44
-
45
- affected_rows
46
-
47
- # If something went wrong, revert the version.
48
- rescue Exception
49
- send(lock_col + '=', previous_lock_value)
50
- raise
51
- end
52
- end
53
- end
54
- end
55
- end
1
+ module ActiveRecord
2
+ module Locking
3
+ module Optimistic
4
+ private
5
+ def _update_record(attribute_names = @attributes.keys) #:nodoc:
6
+ return super unless locking_enabled?
7
+ return 0 if attribute_names.empty?
8
+
9
+ lock_col = self.class.locking_column
10
+ previous_lock_value = send(lock_col).to_i
11
+ increment_lock
12
+
13
+ attribute_names += [lock_col]
14
+ attribute_names.uniq!
15
+
16
+ begin
17
+ relation = self.class.unscoped
18
+
19
+ if self.composite?
20
+ affected_rows = relation.where(
21
+ relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was)
22
+ ).where(
23
+ lock_col => previous_lock_value
24
+ ).update_all(
25
+ Hash[attributes_for_update(attribute_names).map do |name|
26
+ [name, _read_attribute(name)]
27
+ end]
28
+ )
29
+ else
30
+ affected_rows = relation.where(
31
+ self.class.primary_key => id,
32
+ lock_col => previous_lock_value,
33
+ ).update_all(
34
+ Hash[attributes_for_update(attribute_names).map do |name|
35
+ [name, _read_attribute(name)]
36
+ end]
37
+ )
38
+ end
39
+
40
+ unless affected_rows == 1
41
+ raise ActiveRecord::StaleObjectError.new(self, "update")
42
+ end
43
+
44
+ affected_rows
45
+
46
+ # If something went wrong, revert the version.
47
+ rescue Exception
48
+ send(lock_col + '=', previous_lock_value)
49
+ raise
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -16,20 +16,23 @@ module ActiveRecord
16
16
  extend CompositePrimaryKeys::CompositeRelation
17
17
  end
18
18
 
19
- # CPK adds this so that it finds the Equality nodes beneath the And node:
20
- # equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
21
- # node.left.relation.name == table_name
22
- # }
23
19
  alias :where_values_hash_without_cpk :where_values_hash
24
20
  def where_values_hash(relation_table_name = table_name)
25
- nodes_from_and = where_values.grep(Arel::Nodes::And).map {|and_node| and_node.children.grep(Arel::Nodes::Equality) }.flatten
21
+ # CPK
22
+ nodes_from_and = where_values.grep(Arel::Nodes::And).map { |and_node|
23
+ and_node.children.grep(Arel::Nodes::Equality)
24
+ }.flatten
26
25
 
26
+ # CPK
27
+ # equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
28
+ # node.left.relation.name == relation_table_name
29
+ # }
27
30
  equalities = (nodes_from_and + where_values.grep(Arel::Nodes::Equality)).find_all { |node|
28
31
  node.left.relation.name == relation_table_name
29
32
  }
30
33
 
31
34
  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
32
-
35
+
33
36
  Hash[equalities.map { |where|
34
37
  name = where.left.name
35
38
  [name, binds.fetch(name.to_s) {
@@ -48,6 +51,7 @@ module ActiveRecord
48
51
  # CPK
49
52
  um = if self.composite?
50
53
  relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, @klass.primary_key, id_was || id))
54
+
51
55
  relation.arel.compile_update(substitutes, @klass.primary_key)
52
56
  else
53
57
  scope = @klass.unscoped
@@ -55,8 +59,11 @@ module ActiveRecord
55
59
  if @klass.finder_needs_type_condition?
56
60
  scope.unscope!(where: @klass.inheritance_column)
57
61
  end
58
-
59
- scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
62
+
63
+ relation = scope.where(@klass.primary_key => (id_was || id))
64
+ binds += relation.bind_values
65
+
66
+ relation.arel.compile_update(substitutes, @klass.primary_key)
60
67
  end
61
68
 
62
69
  @klass.connection.update(
@@ -2,33 +2,39 @@ module CompositePrimaryKeys
2
2
  module ActiveRecord
3
3
  module Batches
4
4
  def find_in_batches(options = {})
5
+ options.assert_valid_keys(:start, :batch_size)
6
+
5
7
  relation = self
8
+ start = options[:start]
9
+ batch_size = options[:batch_size] || 1000
6
10
 
7
- unless arel.orders.blank? && arel.taken.blank?
8
- ::ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
11
+ unless block_given?
12
+ return to_enum(:find_in_batches, options) do
13
+ total = start ? where(table[primary_key].gteq(start)).size : size
14
+ (total - 1).div(batch_size) + 1
15
+ end
9
16
  end
10
17
 
11
- if (finder_options = options.except(:start, :batch_size)).present?
12
- raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
13
- raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
14
-
15
- relation = apply_finder_options(finder_options)
18
+ if logger && (arel.orders.present? || arel.taken.present?)
19
+ logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
16
20
  end
17
21
 
18
- start = options.delete(:start).to_i
19
- batch_size = options.delete(:batch_size) || 1000
20
-
21
22
  relation = relation.reorder(batch_order).limit(batch_size)
22
23
 
23
24
  # CPK
24
- # records = relation.where(table[primary_key].gteq(start)).all
25
- records = self.primary_key.reduce(relation) do |rel, key|
26
- rel.where(table[key].gteq(start))
27
- end
25
+ # records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
26
+ records = if start
27
+ self.primary_key.reduce(relation) do |rel, key|
28
+ rel.where(table[key].gteq(start))
29
+ end
30
+ else
31
+ relation.to_a
32
+ end
28
33
 
29
34
  while records.any?
30
35
  records_size = records.size
31
36
  primary_key_offset = records.last.id
37
+ raise "Primary key not included in the custom select clause" unless primary_key_offset
32
38
 
33
39
  yield records
34
40
 
@@ -51,11 +57,9 @@ module CompositePrimaryKeys
51
57
 
52
58
  Arel::Nodes::Grouping.new(and_clause)
53
59
  end.reduce(:or)
54
-
55
- records = relation.where(query)
56
- else
57
- raise "Primary key not included in the custom select clause"
58
60
  end
61
+
62
+ records = relation.where(query)
59
63
  end
60
64
  end
61
65
 
@@ -70,7 +74,7 @@ module CompositePrimaryKeys
70
74
 
71
75
  def batch_order
72
76
  # CPK
73
- #"#{quoted_table_name}.#{quoted_primary_key} ASC"
77
+ # "#{quoted_table_name}.#{quoted_primary_key} ASC"
74
78
  self.primary_key.map do |key|
75
79
  "#{quoted_table_name}.#{key} ASC"
76
80
  end.join(",")