composite_primary_keys 9.0.4 → 9.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
+