torque-postgresql 2.4.4 → 3.0.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +0 -17
  3. data/lib/torque/postgresql/adapter/database_statements.rb +32 -74
  4. data/lib/torque/postgresql/adapter/oid/enum_set.rb +1 -1
  5. data/lib/torque/postgresql/adapter/oid.rb +0 -3
  6. data/lib/torque/postgresql/adapter/quoting.rb +12 -20
  7. data/lib/torque/postgresql/adapter/schema_creation.rb +1 -2
  8. data/lib/torque/postgresql/adapter/schema_definitions.rb +0 -37
  9. data/lib/torque/postgresql/adapter/schema_dumper.rb +2 -60
  10. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -74
  11. data/lib/torque/postgresql/adapter.rb +2 -11
  12. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +7 -6
  13. data/lib/torque/postgresql/associations/{association.rb → foreign_association.rb} +1 -4
  14. data/lib/torque/postgresql/associations/preloader/association.rb +53 -26
  15. data/lib/torque/postgresql/associations/preloader/loader_query.rb +36 -0
  16. data/lib/torque/postgresql/associations/preloader.rb +1 -0
  17. data/lib/torque/postgresql/associations.rb +6 -1
  18. data/lib/torque/postgresql/attributes/builder/period.rb +6 -2
  19. data/lib/torque/postgresql/auxiliary_statement/settings.rb +22 -75
  20. data/lib/torque/postgresql/auxiliary_statement.rb +40 -39
  21. data/lib/torque/postgresql/base.rb +13 -33
  22. data/lib/torque/postgresql/config.rb +3 -30
  23. data/lib/torque/postgresql/inheritance.rb +1 -3
  24. data/lib/torque/postgresql/migration/command_recorder.rb +2 -12
  25. data/lib/torque/postgresql/railtie.rb +1 -5
  26. data/lib/torque/postgresql/reflection/abstract_reflection.rb +44 -20
  27. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +2 -2
  28. data/lib/torque/postgresql/relation/auxiliary_statement.rb +15 -28
  29. data/lib/torque/postgresql/relation.rb +10 -12
  30. data/lib/torque/postgresql/schema_cache.rb +2 -7
  31. data/lib/torque/postgresql/version.rb +1 -1
  32. data/lib/torque/postgresql.rb +1 -2
  33. data/lib/torque-postgresql.rb +0 -1
  34. data/spec/schema.rb +14 -30
  35. data/spec/spec_helper.rb +1 -2
  36. data/spec/tests/arel_spec.rb +2 -4
  37. data/spec/tests/auxiliary_statement_spec.rb +35 -374
  38. data/spec/tests/belongs_to_many_spec.rb +2 -99
  39. data/spec/tests/distinct_on_spec.rb +1 -1
  40. data/spec/tests/enum_set_spec.rb +10 -10
  41. data/spec/tests/enum_spec.rb +0 -90
  42. data/spec/tests/has_many_spec.rb +0 -46
  43. data/spec/tests/relation_spec.rb +1 -1
  44. data/spec/tests/table_inheritance_spec.rb +15 -11
  45. metadata +11 -37
  46. data/lib/torque/postgresql/auxiliary_statement/recursive.rb +0 -149
  47. data/lib/torque/postgresql/table_name.rb +0 -41
  48. data/lib/torque/range.rb +0 -22
  49. data/spec/models/category.rb +0 -2
  50. data/spec/models/internal/user.rb +0 -5
  51. data/spec/tests/range_spec.rb +0 -36
  52. data/spec/tests/schema_spec.rb +0 -134
@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Torque
4
- module PostgreSQL
5
- class AuxiliaryStatement
6
- class Recursive < AuxiliaryStatement
7
- # Setup any additional option in the recursive mode
8
- def initialize(*, **options)
9
- super
10
-
11
- @connect = options[:connect]&.to_a&.first
12
- @union_all = options[:union_all]
13
- @sub_query = options[:sub_query]
14
-
15
- if options.key?(:with_depth)
16
- @depth = options[:with_depth].values_at(:name, :start, :as)
17
- @depth[0] ||= 'depth'
18
- end
19
-
20
- if options.key?(:with_path)
21
- @path = options[:with_path].values_at(:name, :source, :as)
22
- @path[0] ||= 'path'
23
- end
24
- end
25
-
26
- private
27
-
28
- # Build the string or arel query
29
- def build_query(base)
30
- # Expose columns and get the list of the ones for select
31
- columns = expose_columns(base, @query.try(:arel_table))
32
- sub_columns = columns.dup
33
- type = @union_all.present? ? 'all' : ''
34
-
35
- # Build any extra columns that are dynamic and from the recursion
36
- extra_columns(base, columns, sub_columns)
37
-
38
- # Prepare the query depending on its type
39
- if @query.is_a?(String) && @sub_query.is_a?(String)
40
- args = @args.each_with_object({}) { |h, (k, v)| h[k] = base.connection.quote(v) }
41
- ::Arel.sql("(#{@query} UNION #{type.upcase} #{@sub_query})" % args)
42
- elsif relation_query?(@query)
43
- @query = @query.where(@where) if @where.present?
44
- @bound_attributes.concat(@query.send(:bound_attributes))
45
-
46
- if relation_query?(@sub_query)
47
- @bound_attributes.concat(@sub_query.send(:bound_attributes))
48
-
49
- sub_query = @sub_query.select(*sub_columns).arel
50
- sub_query.from([@sub_query.arel_table, table])
51
- else
52
- sub_query = ::Arel.sql(@sub_query)
53
- end
54
-
55
- @query.select(*columns).arel.union(type, sub_query)
56
- else
57
- raise ArgumentError, <<-MSG.squish
58
- Only String and ActiveRecord::Base objects are accepted as query and sub query
59
- objects, #{@query.class.name} given for #{self.class.name}.
60
- MSG
61
- end
62
- end
63
-
64
- # Setup the statement using the class configuration
65
- def prepare(base, settings)
66
- super
67
-
68
- prepare_sub_query(base, settings)
69
- end
70
-
71
- # Make sure that both parts of the union are ready
72
- def prepare_sub_query(base, settings)
73
- @union_all = settings.union_all if @union_all.nil?
74
- @sub_query ||= settings.sub_query
75
- @depth ||= settings.depth
76
- @path ||= settings.path
77
-
78
- # Collect the connection
79
- @connect ||= settings.connect || begin
80
- key = base.primary_key
81
- [key.to_sym, :"parent_#{key}"] unless key.nil?
82
- end
83
-
84
- raise ArgumentError, <<-MSG.squish if @sub_query.nil? && @query.is_a?(String)
85
- Unable to generate sub query from a string query. Please provide a `sub_query`
86
- property on the "#{table_name}" settings.
87
- MSG
88
-
89
- if @sub_query.nil?
90
- raise ArgumentError, <<-MSG.squish if @connect.blank?
91
- Unable to generate sub query without setting up a proper way to connect it
92
- with the main query. Please provide a `connect` property on the "#{table_name}"
93
- settings.
94
- MSG
95
-
96
- left, right = @connect.map(&:to_s)
97
- condition = @query.arel_table[right].eq(table[left])
98
-
99
- if @query.where_values_hash.key?(right)
100
- @sub_query = @query.unscope(where: right.to_sym).where(condition)
101
- else
102
- @sub_query = @query.where(condition)
103
- @query = @query.where(right => nil)
104
- end
105
- elsif @sub_query.respond_to?(:call)
106
- # Call a proc to get the real sub query
107
- call_args = @sub_query.try(:arity) === 0 ? [] : [OpenStruct.new(@args)]
108
- @sub_query = @sub_query.call(*call_args)
109
- end
110
- end
111
-
112
- # Add depth and path if they were defined in settings
113
- def extra_columns(base, columns, sub_columns)
114
- return if @query.is_a?(String) || @sub_query.is_a?(String)
115
-
116
- # Add the connect attribute to the query
117
- if defined?(@connect)
118
- columns.unshift(@query.arel_table[@connect[0]])
119
- sub_columns.unshift(@sub_query.arel_table[@connect[0]])
120
- end
121
-
122
- # Build a column to represent the depth of the recursion
123
- if @depth.present?
124
- name, start, as = @depth
125
- col = table[name]
126
- base.select_extra_values += [col.as(as)] unless as.nil?
127
-
128
- columns << ::Arel.sql(start.to_s).as(name)
129
- sub_columns << (col + ::Arel.sql('1')).as(name)
130
- end
131
-
132
- # Build a column to represent the path of the record access
133
- if @path.present?
134
- name, source, as = @path
135
- source = @query.arel_table[source || @connect[0]]
136
-
137
- col = table[name]
138
- base.select_extra_values += [col.as(as)] unless as.nil?
139
- parts = [col, source.cast(:varchar)]
140
-
141
- columns << ::Arel.array([source]).cast(:varchar, true).as(name)
142
- sub_columns << ::Arel::Nodes::NamedFunction.new('array_append', parts).as(name)
143
- end
144
- end
145
-
146
- end
147
- end
148
- end
149
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Torque
4
- module PostgreSQL
5
- class TableName < Delegator
6
- def initialize(klass, table_name)
7
- @klass = klass
8
- @table_name = table_name
9
- end
10
-
11
- def schema
12
- return @schema if defined?(@schema)
13
-
14
- @schema = ([@klass] + @klass.module_parents[0..-2]).find do |klass|
15
- next unless klass.respond_to?(:schema) && !(value = klass.schema).nil?
16
- break value
17
- end
18
- end
19
-
20
- def to_s
21
- schema.nil? ? @table_name : "#{schema}.#{@table_name}"
22
- end
23
-
24
- alias __getobj__ to_s
25
-
26
- def ==(other)
27
- other.to_s =~ /("?#{schema | search_path_schemes.join('|')}"?\.)?"?#{@table_name}"?/
28
- end
29
-
30
- def __setobj__(value)
31
- @table_name = value
32
- end
33
-
34
- private
35
-
36
- def search_path_schemes
37
- klass.connection.schemas_search_path_sanitized
38
- end
39
- end
40
- end
41
- end
data/lib/torque/range.rb DELETED
@@ -1,22 +0,0 @@
1
- module Torque
2
- module Range
3
- def intersection(other)
4
- ActiveSupport::Deprecation.warn('Range extensions will be removed in future versions')
5
- raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
6
-
7
- new_min = self.cover?(other.min) ? other.min : other.cover?(min) ? min : nil
8
- new_max = self.cover?(other.max) ? other.max : other.cover?(max) ? max : nil
9
-
10
- new_min && new_max ? new_min..new_max : nil
11
- end
12
- alias_method :&, :intersection
13
-
14
- def union(other)
15
- ActiveSupport::Deprecation.warn('Range extensions will be removed in future versions')
16
- raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
17
-
18
- ([min, other.min].min)..([max, other.max].max)
19
- end
20
- alias_method :|, :union
21
- end
22
- end
@@ -1,2 +0,0 @@
1
- class Category < ActiveRecord::Base
2
- end
@@ -1,5 +0,0 @@
1
- module Internal
2
- class User < ActiveRecord::Base
3
- self.schema = 'internal'
4
- end
5
- end
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.xdescribe 'Range' do
4
- let(:sample) { (5..15) }
5
-
6
- it 'has intersection' do
7
- expect { sample.intersection(10) }.to raise_error(ArgumentError, /Range/)
8
-
9
- result = sample & (10..15)
10
- expect(result).to be_a(Range)
11
- expect(result.min).to be_eql(10)
12
- expect(result.max).to be_eql(15)
13
-
14
- result = sample & (15..20)
15
- expect(result).to be_a(Range)
16
- expect(result.min).to be_eql(15)
17
- expect(result.max).to be_eql(15)
18
-
19
- result = sample & (0..10)
20
- expect(result).to be_a(Range)
21
- expect(result.min).to be_eql(5)
22
- expect(result.max).to be_eql(10)
23
-
24
- result = sample & (-10..0)
25
- expect(result).to be_nil
26
- end
27
-
28
- it 'has union' do
29
- expect { sample.union(10) }.to raise_error(ArgumentError, /Range/)
30
-
31
- result = sample | (0..10)
32
- expect(result).to be_a(Range)
33
- expect(result.min).to be_eql(0)
34
- expect(result.max).to be_eql(15)
35
- end
36
- end
@@ -1,134 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe 'Schema' do
4
- let(:connection) { ActiveRecord::Base.connection }
5
-
6
- before do
7
- connection.instance_variable_set(:@schmeas_blacklist, nil)
8
- connection.instance_variable_set(:@schmeas_whitelist, nil)
9
- end
10
-
11
- context 'on migration' do
12
- it 'can check for existance' do
13
- expect(connection.schema_exists?(:information_schema)).to be_falsey
14
- expect(connection.schema_exists?(:information_schema, filtered: false)).to be_truthy
15
- end
16
-
17
- it 'can be created' do
18
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_falsey
19
- connection.create_schema(:legacy)
20
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_truthy
21
- end
22
-
23
- it 'can be deleted' do
24
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_falsey
25
-
26
- connection.create_schema(:legacy)
27
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_truthy
28
-
29
- connection.drop_schema(:legacy)
30
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_falsey
31
- end
32
-
33
- it 'works with whitelist' do
34
- expect(connection.schema_exists?(:legacy)).to be_falsey
35
- connection.create_schema(:legacy)
36
-
37
- expect(connection.schema_exists?(:legacy)).to be_falsey
38
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_truthy
39
-
40
- connection.schemas_whitelist.push('legacy')
41
- expect(connection.schema_exists?(:legacy)).to be_truthy
42
- end
43
-
44
- context 'reverting' do
45
- let(:migration) { ActiveRecord::Migration::Current.new('Testing') }
46
-
47
- before { connection.create_schema(:legacy) }
48
-
49
- it 'reverts the creation of a schema' do
50
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_truthy
51
- migration.revert { migration.connection.create_schema(:legacy) }
52
- expect(connection.schema_exists?(:legacy, filtered: false)).to be_falsey
53
- end
54
-
55
- it 'reverts the creation of a table' do
56
- connection.create_table(:users, schema: :legacy) { |t| t.string(:name) }
57
-
58
- expect(connection.table_exists?('legacy.users')).to be_truthy
59
- migration.revert { migration.connection.create_table(:users, schema: :legacy) }
60
- expect(connection.table_exists?('legacy.users')).to be_falsey
61
- end
62
- end
63
- end
64
-
65
- context 'on schema' do
66
- let(:dump_result) do
67
- ActiveRecord::SchemaDumper.dump(connection, (dump_result = StringIO.new))
68
- dump_result.string
69
- end
70
-
71
- it 'does not add when there is no extra schemas' do
72
- connection.drop_schema(:internal, force: :cascade)
73
- expect(dump_result).not_to match /Custom schemas defined in this database/
74
- end
75
-
76
- it 'does not include tables from blacklisted schemas' do
77
- connection.schemas_blacklist.push('internal')
78
- expect(dump_result).not_to match /create_table \"users\",.*schema: +"internal"/
79
- end
80
-
81
- context 'with internal schema whitelisted' do
82
- before { connection.schemas_whitelist.push('internal') }
83
-
84
- it 'dumps the schemas' do
85
- expect(dump_result).to match /create_schema \"internal\"/
86
- end
87
-
88
- it 'shows the internal users table in the connection tables list' do
89
- expect(connection.tables).to include('internal.users')
90
- end
91
-
92
- it 'dumps tables on whitelisted schemas' do
93
- expect(dump_result).to match /create_table \"users\",.*schema: +"internal"/
94
- end
95
- end
96
-
97
- it 'does not affect serial ids' do
98
- connection.create_table(:primary_keys, id: :serial) do |t|
99
- t.string :title
100
- end
101
-
102
- parts = '"primary_keys", id: :serial, force: :cascade'
103
- expect(dump_result).to match(/create_table #{parts} do /)
104
- end
105
- end
106
-
107
- context 'on relation' do
108
- let(:model) { Internal::User }
109
- let(:table_name) { Torque::PostgreSQL::TableName.new(model, 'users') }
110
-
111
- it 'adds the schema to the query' do
112
- model.reset_table_name
113
- expect(table_name.to_s).to eq('internal.users')
114
- expect(model.all.to_sql).to match(/FROM "internal"."users"/)
115
- end
116
-
117
- it 'can load the schema from the module' do
118
- allow(Internal).to receive(:schema).and_return('internal')
119
- allow(model).to receive(:schema).and_return(nil)
120
-
121
- model.reset_table_name
122
- expect(table_name.to_s).to eq('internal.users')
123
- expect(model.all.to_sql).to match(/FROM "internal"."users"/)
124
- end
125
-
126
- it 'does not change anything if the model has not configured a schema' do
127
- allow(model).to receive(:schema).and_return(nil)
128
-
129
- model.reset_table_name
130
- expect(table_name.to_s).to eq('users')
131
- expect(model.all.to_sql).to match(/FROM "users"/)
132
- end
133
- end
134
- end