composite_primary_keys 9.0.4 → 9.0.5

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +20 -0
  3. data/Rakefile +37 -34
  4. data/lib/composite_primary_keys.rb +5 -10
  5. data/lib/composite_primary_keys/arel/in.rb +6 -6
  6. data/lib/composite_primary_keys/arel/sqlserver.rb +36 -0
  7. data/lib/composite_primary_keys/associations/association_scope.rb +51 -29
  8. data/lib/composite_primary_keys/attribute_methods/read.rb +3 -1
  9. data/lib/composite_primary_keys/connection_adapters/abstract_mysql_adapter.rb +22 -0
  10. data/lib/composite_primary_keys/relation.rb +30 -0
  11. data/lib/composite_primary_keys/relation/query_methods.rb +25 -36
  12. data/lib/composite_primary_keys/sanitization.rb +31 -47
  13. data/lib/composite_primary_keys/version.rb +1 -1
  14. data/tasks/databases/mysql.rake +40 -42
  15. data/tasks/databases/oracle.rake +29 -15
  16. data/tasks/databases/postgresql.rake +38 -47
  17. data/tasks/databases/sqlite.rake +25 -0
  18. data/tasks/databases/sqlserver.rake +32 -16
  19. data/test/abstract_unit.rb +12 -11
  20. data/test/connections/connection_spec.rb +27 -18
  21. data/test/connections/databases.ci.yml +5 -4
  22. data/test/connections/databases.example.yml +19 -4
  23. data/test/connections/databases.yml +25 -4
  24. data/test/fixtures/article.rb +6 -5
  25. data/test/fixtures/db_definitions/mysql.sql +16 -7
  26. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  27. data/test/fixtures/db_definitions/oracle.sql +25 -15
  28. data/test/fixtures/db_definitions/postgresql.sql +11 -2
  29. data/test/fixtures/db_definitions/sqlite.sql +8 -0
  30. data/test/fixtures/db_definitions/sqlserver.sql +19 -33
  31. data/test/fixtures/pk_called_id.rb +5 -0
  32. data/test/fixtures/pk_called_ids.yml +11 -0
  33. data/test/test_associations.rb +334 -332
  34. data/test/test_create.rb +9 -1
  35. data/test/test_delete.rb +17 -39
  36. data/test/test_ids.rb +113 -109
  37. data/test/test_preload.rb +94 -0
  38. data/test/test_suite.rb +1 -1
  39. data/test/test_update.rb +12 -7
  40. metadata +14 -24
  41. data/lib/composite_primary_keys/associations/singular_association.rb +0 -14
  42. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +0 -71
  43. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +0 -50
  44. data/lib/composite_primary_keys/dirty.rb +0 -19
  45. data/lib/composite_primary_keys/validations/uniqueness.rb +0 -41
  46. data/tasks/databases/oracle_enhanced.rake +0 -27
  47. data/tasks/databases/sqlite3.rake +0 -27
  48. data/test/connections/native_ibm_db/connection.rb +0 -19
  49. data/test/connections/native_mysql/connection.rb +0 -17
  50. data/test/connections/native_oracle/connection.rb +0 -11
  51. data/test/connections/native_oracle_enhanced/connection.rb +0 -16
  52. data/test/connections/native_postgresql/connection.rb +0 -13
  53. data/test/connections/native_sqlite3/connection.rb +0 -9
  54. data/test/connections/native_sqlserver/connection.rb +0 -11
  55. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -92
  56. data/test/test_delete_all.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5b83b2dfe1e8d6585047dd3a54fecc92c3b54d5
4
- data.tar.gz: ddaa05937db47260356966115e0a16daa13f9e44
3
+ metadata.gz: 2a7db1ee79736fa3b98274e9577f650d27c239cd
4
+ data.tar.gz: 4e121d1420ba01fc56073d176888687ca78d2177
5
5
  SHA512:
6
- metadata.gz: f30b4ef085f3aa5c0b48ddaf7805b8752838b928cafee0268951e6e281756c8f306e05f0bd4ff5972ddeb9b824c8d4a167818db4991eb4b01db9df531949aee9
7
- data.tar.gz: 0c13040b7a4e4dc05d994dcd04856ba3f383d469a7ecfea28df07b09a0a617d1e27f56bf1acad6dc62cc3bdc4656e50df96cf53dfe6cead575edc4e0c0ff1903
6
+ metadata.gz: 94556da0f6cfb30ed3285635ebcdbff725d5c7ad9098c3de8b63c3920c93ae1739ab27608f2c32113c8514a688ab1fadd5effddfe05b8c67ccaaed39c7f56b69
7
+ data.tar.gz: df406dbc3ec9c24df294088f033ec760af5602a3ce09891ad23f5f0200bdfbba4658cf4765fc65c5f4df86ca9f749ee9f7740fb3f7c430751d201e649dd7a7c1
@@ -1,3 +1,19 @@
1
+ == 9.0.5 (2017-01-02)
2
+
3
+ * Don't nest PK twice when looking up id, fixes #319 (Kerey Roper)
4
+ * Simplify database setup for tests (Charlie Savage)
5
+ * Revamp gem management (Charlie Savage)
6
+ * Add support for Relation#update_all when using joins (Charlie Savage)
7
+ * Add erb support to test database.yml files (Charlie Savage)
8
+ * Sqlserver fixes for ordering (Charlie Savage)
9
+ * Sqlserver refresh (Charlie Savage)
10
+ * Oracle refresh (Charlie Savage)
11
+ * Fix delete_all for MySql (Charlie Savage)
12
+ * Added test cases for preloading associations with and without conditions (Martin Körner)
13
+ * Revamp single association handling to correctly setup SQL binds (Charlie Savage)
14
+ * Remove references to ActiveModel - #352 (Charlie Savage)
15
+ * Fixes for #232, #359, #367 and #371 (Charlie Savage)
16
+
1
17
  == 9.0.4 (2016-08-17)
2
18
  * Do not set associations to readonly. See https://github.com/rails/rails/issues/24093 (Charlie Savage)
3
19
 
@@ -13,6 +29,10 @@
13
29
  == 9.0.0.beta1 (2016-04-16)
14
30
  * Rails 5 beta support (Sammy Larbi)
15
31
 
32
+ == 8.1.5 (2017-01-01)
33
+
34
+ * Don't nest PK twice when looking up id, fixes #319 (Kerey Roper)
35
+
16
36
  == 8.1.4 (2016-07-27)
17
37
 
18
38
  * Create OR predicates in a nicely balanced tree fixing #320 (Nathan Samson)
data/Rakefile CHANGED
@@ -1,34 +1,37 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rake/testtask'
5
- require 'rubygems/package_task'
6
-
7
- # Set global variable so other tasks can access them
8
- ::PROJECT_ROOT = File.expand_path(".")
9
- ::GEM_NAME = 'composite_primary_keys'
10
-
11
- # Read the spec file
12
- spec = Gem::Specification.load("#{GEM_NAME}.gemspec")
13
-
14
- # Setup Rake tasks for managing the gem
15
- Gem::PackageTask.new(spec).define
16
-
17
- # Now load in other task files
18
- Dir.glob('tasks/**/*.rake').each do |rake_file|
19
- load File.join(File.dirname(__FILE__), rake_file)
20
- end
21
-
22
- # Set up test tasks for each supported connection adapter
23
- %w(mysql sqlite3 oracle oracle_enhanced postgresql ibm_db sqlserver).each do |adapter|
24
- namespace adapter do
25
- desc "Run tests using the #{adapter} adapter"
26
- task "test" do
27
- ENV["ADAPTER"] = adapter
28
- Rake::TestTask.new("subtest_#{adapter}") do |t|
29
- t.libs << "test"
30
- end
31
- Rake::Task["subtest_#{adapter}"].invoke
32
- end
33
- end
34
- end
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rubygems/package_task'
6
+
7
+ # Set global variable so other tasks can access them
8
+ ::PROJECT_ROOT = File.expand_path(".")
9
+ ::GEM_NAME = 'composite_primary_keys'
10
+
11
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
12
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
13
+
14
+ # Read the spec file
15
+ spec = Gem::Specification.load("#{GEM_NAME}.gemspec")
16
+
17
+ # Setup Rake tasks for managing the gem
18
+ Gem::PackageTask.new(spec).define
19
+
20
+ # Now load in other task files
21
+ Dir.glob('tasks/**/*.rake').each do |rake_file|
22
+ load File.join(File.dirname(__FILE__), rake_file)
23
+ end
24
+
25
+ # Set up test tasks for each supported connection adapter
26
+ %w(mysql sqlite oracle oracle_enhanced postgresql ibm_db sqlserver).each do |adapter|
27
+ namespace adapter do
28
+ desc "Run tests using the #{adapter} adapter"
29
+ task "test" do
30
+ ENV["ADAPTER"] = adapter
31
+ Rake::TestTask.new("subtest_#{adapter}") do |t|
32
+ t.libs << "test"
33
+ end
34
+ Rake::Task["subtest_#{adapter}"].invoke
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2006-2012 Nic Williams and Charlie Savage
2
+ # Copyright (c) 2006-2016 Nic Williams and Charlie Savage
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -58,6 +58,7 @@ require 'active_record/locking/optimistic'
58
58
  require 'active_record/nested_attributes'
59
59
 
60
60
  require 'active_record/connection_adapters/abstract_adapter'
61
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
61
62
 
62
63
  require 'active_record/relation/batches'
63
64
  require 'active_record/relation/where_clause'
@@ -66,8 +67,6 @@ require 'active_record/relation/finder_methods'
66
67
  require 'active_record/relation/predicate_builder'
67
68
  require 'active_record/relation/query_methods'
68
69
 
69
- require 'active_record/validations/uniqueness' unless ENV["TESTING_CPK"] == "true"
70
-
71
70
  # CPK files
72
71
  require 'composite_primary_keys/persistence'
73
72
  require 'composite_primary_keys/base'
@@ -89,11 +88,8 @@ require 'composite_primary_keys/associations/join_dependency'
89
88
  require 'composite_primary_keys/associations/join_dependency/join_association'
90
89
  require 'composite_primary_keys/associations/preloader/association'
91
90
  require 'composite_primary_keys/associations/preloader/belongs_to'
92
- require 'composite_primary_keys/associations/singular_association'
93
91
  require 'composite_primary_keys/associations/collection_association'
94
92
 
95
- require 'composite_primary_keys/dirty'
96
-
97
93
  require 'composite_primary_keys/attribute_methods/primary_key'
98
94
  require 'composite_primary_keys/attribute_methods/read'
99
95
  require 'composite_primary_keys/attribute_methods/write'
@@ -101,7 +97,7 @@ require 'composite_primary_keys/locking/optimistic'
101
97
  require 'composite_primary_keys/nested_attributes'
102
98
 
103
99
  require 'composite_primary_keys/connection_adapters/abstract_adapter'
104
- require 'composite_primary_keys/connection_adapters/abstract/connection_specification_changes'
100
+ require 'composite_primary_keys/connection_adapters/abstract_mysql_adapter'
105
101
 
106
102
  require 'composite_primary_keys/relation/batches'
107
103
  require 'composite_primary_keys/relation/where_clause'
@@ -110,9 +106,8 @@ require 'composite_primary_keys/relation/finder_methods'
110
106
  require 'composite_primary_keys/relation/predicate_builder'
111
107
  require 'composite_primary_keys/relation/query_methods'
112
108
 
113
- require 'composite_primary_keys/validations/uniqueness'
114
-
115
109
  require 'composite_primary_keys/composite_relation'
116
110
 
117
111
  require 'composite_primary_keys/arel/in'
118
- require 'composite_primary_keys/arel/to_sql'
112
+ require 'composite_primary_keys/arel/to_sql'
113
+ require 'composite_primary_keys/arel/sqlserver'
@@ -1,6 +1,6 @@
1
- module CompositePrimaryKeys
2
- module Nodes
3
- class In < ::Arel::Nodes::Equality
4
- end
5
- end
6
- end
1
+ module CompositePrimaryKeys
2
+ module Nodes
3
+ class In < ::Arel::Nodes::Equality
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,36 @@
1
+ module Arel
2
+ module Visitors
3
+ class SQLServer
4
+ def make_Fetch_Possible_And_Deterministic o
5
+ return if o.limit.nil? && o.offset.nil?
6
+ t = table_From_Statement o
7
+ pk = primary_Key_From_Table t
8
+ return unless pk
9
+ if o.orders.empty?
10
+ # Prefer deterministic vs a simple `(SELECT NULL)` expr.
11
+ # CPK
12
+ #o.orders = [pk.asc]
13
+ o.orders = pk.map {|a_pk| a_pk.asc}
14
+ end
15
+ end
16
+
17
+ def primary_Key_From_Table t
18
+ return unless t
19
+ column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.second.try(:name)
20
+
21
+ # CPK
22
+ # column_name ? t[column_name] : nil
23
+ case column_name
24
+ when Array
25
+ column_name.map do |name|
26
+ t[name]
27
+ end
28
+ when NilClass
29
+ [t[column_name]]
30
+ else
31
+ nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,44 +1,66 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope
4
- silence_warnings do
5
- def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
6
- join_keys = reflection.join_keys(association_klass)
7
- key = join_keys.key
8
- foreign_key = join_keys.foreign_key
9
- # CPK
10
- # constraint = table[key].eq(foreign_table[foreign_key])
11
- constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
12
4
 
5
+ def self.get_bind_values(owner, chain)
6
+ binds = []
7
+ last_reflection = chain.last
8
+
9
+ # CPK
10
+ # binds << last_reflection.join_id_for(owner)
11
+ values = last_reflection.join_id_for(owner)
12
+ binds += Array(values)
13
+
14
+ if last_reflection.type
15
+ binds << owner.class.base_class.name
16
+ end
17
+
18
+ chain.each_cons(2).each do |reflection, next_reflection|
13
19
  if reflection.type
14
- value = transform_value(next_reflection.klass.base_class.name)
15
- scope = scope.where(table.name => { reflection.type => value })
20
+ binds << next_reflection.klass.base_class.name
16
21
  end
22
+ end
23
+ binds
24
+ end
17
25
 
18
- scope = scope.joins(join(foreign_table, constraint))
26
+ def last_chain_scope(scope, table, reflection, owner, association_klass)
27
+ join_keys = reflection.join_keys(association_klass)
28
+ key = join_keys.key
29
+ foreign_key = join_keys.foreign_key
30
+
31
+ # CPK
32
+ #value = transform_value(owner[foreign_key])
33
+ #scope = scope.where(table.name => { key => value })
34
+ mappings = Array(key).zip(Array(foreign_key))
35
+ joins = mappings.reduce(Hash.new) do |hash, mapping|
36
+ hash[mapping.first] = transform_value(owner[mapping.last])
37
+ hash
19
38
  end
39
+ scope = scope.where(table.name => joins)
20
40
 
21
- def last_chain_scope(scope, table, reflection, owner, association_klass)
22
- join_keys = reflection.join_keys(association_klass)
23
- key = join_keys.key
24
- foreign_key = join_keys.foreign_key
25
-
26
- # CPK
27
- if key.kind_of?(Array) || foreign_key.kind_of?(Array)
28
- predicate = cpk_join_predicate(table, key, owner, foreign_key)
29
- scope = scope.where(predicate)
30
- else
31
- value = transform_value(owner[foreign_key])
32
- scope = scope.where(table.name => { key => value })
33
- end
41
+ if reflection.type
42
+ polymorphic_type = transform_value(owner.class.base_class.name)
43
+ scope = scope.where(table.name => { reflection.type => polymorphic_type })
44
+ end
34
45
 
35
- if reflection.type
36
- polymorphic_type = transform_value(owner.class.base_class.name)
37
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
38
- end
46
+ scope
47
+ end
39
48
 
40
- scope
49
+ def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
50
+ join_keys = reflection.join_keys(association_klass)
51
+ key = join_keys.key
52
+ foreign_key = join_keys.foreign_key
53
+
54
+ # CPK
55
+ # constraint = table[key].eq(foreign_table[foreign_key])
56
+ constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
57
+
58
+ if reflection.type
59
+ value = transform_value(next_reflection.klass.base_class.name)
60
+ scope = scope.where(table.name => { reflection.type => value })
41
61
  end
62
+
63
+ scope = scope.joins(join(foreign_table, constraint))
42
64
  end
43
65
  end
44
66
  end
@@ -8,7 +8,9 @@ module ActiveRecord
8
8
  _read_attribute(attr_name, &block)
9
9
  else
10
10
  name = attr_name.to_s
11
- name = self.class.primary_key if name == 'id'.freeze
11
+ # CPK
12
+ #name = self.class.primary_key if name == 'id'.freeze
13
+ name = self.class.primary_key if name == 'id'.freeze && !composite?
12
14
  _read_attribute(name, &block)
13
15
  end
14
16
  end
@@ -0,0 +1,22 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class AbstractMysqlAdapter
4
+ def subquery_for(key, select)
5
+ subsubselect = select.clone
6
+ subsubselect.projections = [key]
7
+
8
+ # Materialize subquery by adding distinct
9
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
10
+ subsubselect.distinct unless select.limit || select.offset || select.orders.any?
11
+
12
+ subselect = Arel::SelectManager.new(select.engine)
13
+
14
+ # CPK
15
+ #subselect.project Arel.sql(key.name)
16
+ subselect.project Arel.sql(Array(key).map(&:name).join(', '))
17
+
18
+ subselect.from subsubselect.as('__active_record_temp')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -47,6 +47,36 @@ module ActiveRecord
47
47
  end
48
48
  end
49
49
 
50
+ def update_all(updates)
51
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
52
+
53
+ stmt = Arel::UpdateManager.new
54
+
55
+ stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
56
+ stmt.table(table)
57
+
58
+ if joins_values.any?
59
+ # CPK
60
+ #@klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
61
+ if primary_key.kind_of?(Array)
62
+ attributes = primary_key.map do |key|
63
+ arel_attribute(key)
64
+ end
65
+ @klass.connection.join_to_update(stmt, arel, attributes.to_composite_keys)
66
+ else
67
+ @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
68
+ end
69
+ else
70
+ stmt.key = arel_attribute(primary_key)
71
+ stmt.take(arel.limit)
72
+ stmt.order(*arel.orders)
73
+ stmt.wheres = arel.constraints
74
+ end
75
+
76
+ @klass.connection.update stmt, 'SQL', bound_attributes
77
+ end
78
+
79
+
50
80
  def delete_all(conditions = nil)
51
81
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
52
82
  if MULTI_VALUE_METHODS.include?(method)
@@ -1,41 +1,30 @@
1
- module CompositePrimaryKeys::ActiveRecord::QueryMethods
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module QueryMethods
4
+ def reverse_sql_order(order_query)
5
+ # CPK
6
+ # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
2
7
 
3
- def reverse_sql_order(order_query)
4
- # CPK
5
- # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
8
+ # break apart CPKs
9
+ order_query = primary_key.map do |key|
10
+ "#{quoted_table_name}.#{connection.quote_column_name(key)} ASC"
11
+ end if order_query.empty?
6
12
 
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
13
+ order_query.map do |o|
14
+ case o
15
+ when Arel::Nodes::Ordering
16
+ o.reverse
17
+ when String, Symbol
18
+ o.to_s.split(',').collect do |s|
19
+ s.strip!
20
+ s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
21
+ end
22
+ else
23
+ o
24
+ end
25
+ end.flatten
36
26
  end
37
- arg
38
- end if composite?
39
- super(*args)
27
+ end
40
28
  end
41
29
  end
30
+