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.
- checksums.yaml +4 -4
- data/README.rdoc +0 -17
- data/lib/torque/postgresql/adapter/database_statements.rb +32 -74
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +1 -1
- data/lib/torque/postgresql/adapter/oid.rb +0 -3
- data/lib/torque/postgresql/adapter/quoting.rb +12 -20
- data/lib/torque/postgresql/adapter/schema_creation.rb +1 -2
- data/lib/torque/postgresql/adapter/schema_definitions.rb +0 -37
- data/lib/torque/postgresql/adapter/schema_dumper.rb +2 -60
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -74
- data/lib/torque/postgresql/adapter.rb +2 -11
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +7 -6
- data/lib/torque/postgresql/associations/{association.rb → foreign_association.rb} +1 -4
- data/lib/torque/postgresql/associations/preloader/association.rb +53 -26
- data/lib/torque/postgresql/associations/preloader/loader_query.rb +36 -0
- data/lib/torque/postgresql/associations/preloader.rb +1 -0
- data/lib/torque/postgresql/associations.rb +6 -1
- data/lib/torque/postgresql/attributes/builder/period.rb +6 -2
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +22 -75
- data/lib/torque/postgresql/auxiliary_statement.rb +40 -39
- data/lib/torque/postgresql/base.rb +13 -33
- data/lib/torque/postgresql/config.rb +3 -30
- data/lib/torque/postgresql/inheritance.rb +1 -3
- data/lib/torque/postgresql/migration/command_recorder.rb +2 -12
- data/lib/torque/postgresql/railtie.rb +1 -5
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +44 -20
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +2 -2
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +15 -28
- data/lib/torque/postgresql/relation.rb +10 -12
- data/lib/torque/postgresql/schema_cache.rb +2 -7
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +1 -2
- data/lib/torque-postgresql.rb +0 -1
- data/spec/schema.rb +14 -30
- data/spec/spec_helper.rb +1 -2
- data/spec/tests/arel_spec.rb +2 -4
- data/spec/tests/auxiliary_statement_spec.rb +35 -374
- data/spec/tests/belongs_to_many_spec.rb +2 -99
- data/spec/tests/distinct_on_spec.rb +1 -1
- data/spec/tests/enum_set_spec.rb +10 -10
- data/spec/tests/enum_spec.rb +0 -90
- data/spec/tests/has_many_spec.rb +0 -46
- data/spec/tests/relation_spec.rb +1 -1
- data/spec/tests/table_inheritance_spec.rb +15 -11
- metadata +11 -37
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +0 -149
- data/lib/torque/postgresql/table_name.rb +0 -41
- data/lib/torque/range.rb +0 -22
- data/spec/models/category.rb +0 -2
- data/spec/models/internal/user.rb +0 -5
- data/spec/tests/range_spec.rb +0 -36
- 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
|
data/spec/models/category.rb
DELETED
data/spec/tests/range_spec.rb
DELETED
@@ -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
|
data/spec/tests/schema_spec.rb
DELETED
@@ -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
|