From 500fe0a4720d5df3d98d6402f7ed29236bb7bd65 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Tue, 1 Feb 2022 17:45:47 +0100 Subject: [PATCH 01/14] copied fsateler solution from the draft pr to this https://github.com/rails-on-services/apartment/pull/143/files --- lib/apartment.rb | 1 + .../active_record/postgresql_adapter.rb | 18 +++++++++++++++++ lib/apartment/adapters/postgresql_adapter.rb | 20 ------------------- spec/examples/schema_adapter_examples.rb | 8 ++++++-- 4 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 lib/apartment/active_record/postgresql_adapter.rb diff --git a/lib/apartment.rb b/lib/apartment.rb index 8a774b5b..a4aba389 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -8,6 +8,7 @@ require_relative 'apartment/log_subscriber' +require_relative 'apartment/active_record/postgresql_adapter' if ActiveRecord.version.release >= Gem::Version.new('6.0') require_relative 'apartment/active_record/connection_handling' end diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb new file mode 100644 index 00000000..bf963266 --- /dev/null +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Apartment::PostgreSqlAdapterPatch + def default_sequence_name(table, _column) + res = super + schema_prefix = "#{Apartment::Tenant.current}." + if res&.starts_with?(schema_prefix) && Apartment.excluded_models.none?{|m| m.constantize.table_name == table} + res.delete_prefix!(schema_prefix) + end + res + end +end + +require 'active_record/connection_adapters/postgresql_adapter' + +class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter + include Apartment::PostgreSqlAdapterPatch +end diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index 6e9707de..47584911 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -41,7 +41,6 @@ def default_tenant def reset @current = default_tenant Apartment.connection.schema_search_path = full_search_path - reset_sequence_names end def init @@ -81,7 +80,6 @@ def connect_to_new(tenant = nil) # there is a issue for prepared statement with changing search_path. # https://www.postgresql.org/docs/9.3/static/sql-prepare.html Apartment.connection.clear_cache! if postgresql_version < 90_300 - reset_sequence_names rescue *rescuable_exceptions => e raise_schema_connect_to_new(tenant, e) end @@ -130,24 +128,6 @@ def postgresql_version Apartment.connection.send(:postgresql_version) end - def reset_sequence_names - # sequence_name contains the schema, so it must be reset after switch - # There is `reset_sequence_name`, but that method actually goes to the database - # to find out the new name. Therefore, we do this hack to only unset the name, - # and it will be dynamically found the next time it is needed - descendants_to_unset = ActiveRecord::Base.descendants - .select { |c| c.instance_variable_defined?(:@sequence_name) } - .reject do |c| - c.instance_variable_defined?(:@explicit_sequence_name) && - c.instance_variable_get(:@explicit_sequence_name) - end - descendants_to_unset.each do |c| - # NOTE: due to this https://github.com/rails-on-services/apartment/issues/81 - # unreproduceable error we're checking before trying to remove it - c.remove_instance_variable :@sequence_name if c.instance_variable_defined?(:@sequence_name) - end - end - def schema_exists?(schemas) return true unless Apartment.tenant_presence_check diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index 586dce62..f4ce50fb 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -27,6 +27,9 @@ Apartment::Tenant.init expect(Company.table_name).to eq('public.companies') + expect(Company.sequence_name).to eq('public.companies_id_seq') + expect(User.table_name).to eq('users') + expect(User.sequence_name).to eq('users_id_seq') end context 'with a default_tenant', default_tenant: true do @@ -34,6 +37,9 @@ Apartment::Tenant.init expect(Company.table_name).to eq("#{default_tenant}.companies") + expect(Company.sequence_name).to eq("#{default_tenant}.companies_id_seq") + expect(User.table_name).to eq('users') + expect(User.sequence_name).to eq('users_id_seq') end it 'sets the search_path correctly' do @@ -119,11 +125,9 @@ it 'connects and resets' do subject.switch(schema1) do expect(connection.schema_search_path).to start_with %("#{schema1}") - expect(User.sequence_name).to eq "#{schema1}.#{User.table_name}_id_seq" end expect(connection.schema_search_path).to start_with %("#{public_schema}") - expect(User.sequence_name).to eq "#{public_schema}.#{User.table_name}_id_seq" end it 'allows a list of schemas' do From 94a20d99b7680889318a44ac6bcd5c72039f973b Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 12:18:04 +0100 Subject: [PATCH 02/14] added some tests --- lib/apartment/active_record/postgresql_adapter.rb | 9 ++++++++- spec/examples/schema_adapter_examples.rb | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index bf963266..ea1f55d0 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true +# rubocop:disable Style/ClassAndModuleChildren + +# NOTE: This patch is meant to remove any schema_prefix appart from the ones for +# excluded models. The schema_prefix would be resolved by apartment's setting +# of search path module Apartment::PostgreSqlAdapterPatch def default_sequence_name(table, _column) res = super schema_prefix = "#{Apartment::Tenant.current}." - if res&.starts_with?(schema_prefix) && Apartment.excluded_models.none?{|m| m.constantize.table_name == table} + if res&.starts_with?(schema_prefix) && Apartment.excluded_models.none? { |m| m.constantize.table_name == table } res.delete_prefix!(schema_prefix) end res @@ -13,6 +18,8 @@ def default_sequence_name(table, _column) require 'active_record/connection_adapters/postgresql_adapter' +# NOTE: inject this into postgresql adapters class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter include Apartment::PostgreSqlAdapterPatch end +# rubocop:enable Style/ClassAndModuleChildren diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index f4ce50fb..25535390 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -125,9 +125,13 @@ it 'connects and resets' do subject.switch(schema1) do expect(connection.schema_search_path).to start_with %("#{schema1}") + expect(User.sequence_name).to eq "#{schema1}.#{User.table_name}_id_seq" + expect(Company.sequence_name).to eq "#{public_schema}.#{Company.table_name}_id_seq" end expect(connection.schema_search_path).to start_with %("#{public_schema}") + expect(User.sequence_name).to eq "#{User.table_name}_id_seq" + expect(Company.sequence_name).to eq "#{public_schema}.#{Company.table_name}_id_seq" end it 'allows a list of schemas' do From e69800319ce32ea2cf075ca3b784202cbf4fbb8c Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 14:11:31 +0100 Subject: [PATCH 03/14] if current seq name does not match current tenant, fix it --- lib/apartment/active_record/postgresql_adapter.rb | 9 +++++++-- spec/examples/schema_adapter_examples.rb | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index ea1f55d0..16ceb9ef 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -9,9 +9,14 @@ module Apartment::PostgreSqlAdapterPatch def default_sequence_name(table, _column) res = super schema_prefix = "#{Apartment::Tenant.current}." - if res&.starts_with?(schema_prefix) && Apartment.excluded_models.none? { |m| m.constantize.table_name == table } - res.delete_prefix!(schema_prefix) + + unless res.starts_with?(schema_prefix) + schema, _seq_name = extract_schema_qualified_name(res) + res.sub!("#{schema}.", schema_prefix) end + + res.delete_prefix!(schema_prefix) if Apartment.excluded_models.none? { |m| m.constantize.table_name == table } + res end end diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index 25535390..f007435f 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -125,7 +125,7 @@ it 'connects and resets' do subject.switch(schema1) do expect(connection.schema_search_path).to start_with %("#{schema1}") - expect(User.sequence_name).to eq "#{schema1}.#{User.table_name}_id_seq" + expect(User.sequence_name).to eq "#{User.table_name}_id_seq" expect(Company.sequence_name).to eq "#{public_schema}.#{Company.table_name}_id_seq" end From ad4abdd95edf3d90a211aaee33724d6e65743e1c Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 20:08:20 +0100 Subject: [PATCH 04/14] dont create two different addapters --- spec/adapters/postgresql_adapter_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/adapters/postgresql_adapter_spec.rb b/spec/adapters/postgresql_adapter_spec.rb index 7944c862..981440f6 100644 --- a/spec/adapters/postgresql_adapter_spec.rb +++ b/spec/adapters/postgresql_adapter_spec.rb @@ -6,7 +6,7 @@ describe Apartment::Adapters::PostgresqlAdapter, database: :postgresql do unless defined?(JRUBY_VERSION) - subject { Apartment::Tenant.postgresql_adapter config } + subject { Apartment::Tenant.adapter } it_behaves_like 'a generic apartment adapter callbacks' From f0f86d9651ee2b5b3cf777b811d2060ba76163bf Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 20:18:53 +0100 Subject: [PATCH 05/14] removed complex logic and fixed failed checks --- lib/apartment/active_record/postgresql_adapter.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index 16ceb9ef..05cfa0df 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -10,13 +10,13 @@ def default_sequence_name(table, _column) res = super schema_prefix = "#{Apartment::Tenant.current}." - unless res.starts_with?(schema_prefix) - schema, _seq_name = extract_schema_qualified_name(res) - res.sub!("#{schema}.", schema_prefix) + if res&.starts_with?(schema_prefix) + if Apartment.excluded_models.none? { |m| m.constantize.table_name == table } + res.delete_prefix!(schema_prefix) + else + res.sub!(schema_prefix, "#{Apartment::Tenant.default_tenant}.") + end end - - res.delete_prefix!(schema_prefix) if Apartment.excluded_models.none? { |m| m.constantize.table_name == table } - res end end From 8d17dc23304279279d56dd2445e47cc4149104f1 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 21:05:51 +0100 Subject: [PATCH 06/14] assure company is always part of the excluded models --- spec/examples/schema_adapter_examples.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index f007435f..1fe2efef 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -122,6 +122,12 @@ end describe '#switch' do + before do + Apartment.configure do |config| + config.excluded_models = ['Company'] + end + end + it 'connects and resets' do subject.switch(schema1) do expect(connection.schema_search_path).to start_with %("#{schema1}") From 4ba539db8c7b7e1490de875b414c5b07de68b733 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 21:47:22 +0100 Subject: [PATCH 07/14] using adapter in subject --- spec/adapters/mysql2_adapter_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/adapters/mysql2_adapter_spec.rb b/spec/adapters/mysql2_adapter_spec.rb index fea994f4..6ff7c56c 100644 --- a/spec/adapters/mysql2_adapter_spec.rb +++ b/spec/adapters/mysql2_adapter_spec.rb @@ -6,7 +6,7 @@ describe Apartment::Adapters::Mysql2Adapter, database: :mysql do unless defined?(JRUBY_VERSION) - subject(:adapter) { Apartment::Tenant.mysql2_adapter config } + subject(:adapter) { Apartment::Tenant.adapter } def tenant_names ActiveRecord::Base.connection.execute('SELECT schema_name FROM information_schema.schemata').collect do |row| From 71d3ce038e819aa652d65f39f1bafd6279096be9 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 21:53:32 +0100 Subject: [PATCH 08/14] using adapter for subject --- spec/adapters/jdbc_mysql_adapter_spec.rb | 2 +- spec/adapters/jdbc_postgresql_adapter_spec.rb | 2 +- spec/adapters/sqlite3_adapter_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/adapters/jdbc_mysql_adapter_spec.rb b/spec/adapters/jdbc_mysql_adapter_spec.rb index b757c96b..de9c3860 100644 --- a/spec/adapters/jdbc_mysql_adapter_spec.rb +++ b/spec/adapters/jdbc_mysql_adapter_spec.rb @@ -6,7 +6,7 @@ require 'apartment/adapters/jdbc_mysql_adapter' describe Apartment::Adapters::JDBCMysqlAdapter, database: :mysql do - subject { Apartment::Tenant.jdbc_mysql_adapter config.symbolize_keys } + subject(:adapter) { Apartment::Tenant.adapter } def tenant_names ActiveRecord::Base.connection.execute('SELECT schema_name FROM information_schema.schemata').collect do |row| diff --git a/spec/adapters/jdbc_postgresql_adapter_spec.rb b/spec/adapters/jdbc_postgresql_adapter_spec.rb index d1deabf3..4db0eb8b 100644 --- a/spec/adapters/jdbc_postgresql_adapter_spec.rb +++ b/spec/adapters/jdbc_postgresql_adapter_spec.rb @@ -6,7 +6,7 @@ require 'apartment/adapters/jdbc_postgresql_adapter' describe Apartment::Adapters::JDBCPostgresqlAdapter, database: :postgresql do - subject { Apartment::Tenant.jdbc_postgresql_adapter config.symbolize_keys } + subject(:adapter) { Apartment::Tenant.adapter } it_behaves_like 'a generic apartment adapter callbacks' diff --git a/spec/adapters/sqlite3_adapter_spec.rb b/spec/adapters/sqlite3_adapter_spec.rb index 1581a3e3..339cb38c 100644 --- a/spec/adapters/sqlite3_adapter_spec.rb +++ b/spec/adapters/sqlite3_adapter_spec.rb @@ -6,7 +6,7 @@ describe Apartment::Adapters::Sqlite3Adapter, database: :sqlite do unless defined?(JRUBY_VERSION) - subject { Apartment::Tenant.sqlite3_adapter config } + subject(:adapter) { Apartment::Tenant.adapter } it_behaves_like 'a generic apartment adapter callbacks' From 896e34c834ab849358750c881eb0137365c8e13f Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 21:55:01 +0100 Subject: [PATCH 09/14] ignore rubocop inline --- spec/examples/schema_adapter_examples.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index 1fe2efef..70c71504 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -128,6 +128,7 @@ end end + # rubocop:disable RSpec/MultipleExpectations it 'connects and resets' do subject.switch(schema1) do expect(connection.schema_search_path).to start_with %("#{schema1}") @@ -139,6 +140,7 @@ expect(User.sequence_name).to eq "#{User.table_name}_id_seq" expect(Company.sequence_name).to eq "#{public_schema}.#{Company.table_name}_id_seq" end + # rubocop:enable RSpec/MultipleExpectations it 'allows a list of schemas' do subject.switch([schema1, schema2]) do From 98b52c66690385290e96f81091b01bbc92842bf3 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Wed, 2 Feb 2022 23:42:55 +0100 Subject: [PATCH 10/14] reverse negative evaluation and skipping sub if not needed --- lib/apartment/active_record/postgresql_adapter.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index 05cfa0df..333ec2a6 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -11,14 +11,23 @@ def default_sequence_name(table, _column) schema_prefix = "#{Apartment::Tenant.current}." if res&.starts_with?(schema_prefix) - if Apartment.excluded_models.none? { |m| m.constantize.table_name == table } - res.delete_prefix!(schema_prefix) + default_tenant_prefix = "#{Apartment::Tenant.default_tenant}." + # NOTE: Excluded models should always access the sequence from the default + # tenant schema + if excluded_model?(table) && schema_prefix != default_tenant_prefix + res.sub!(schema_prefix, default_tenant_prefix) else - res.sub!(schema_prefix, "#{Apartment::Tenant.default_tenant}.") + res.delete_prefix!(schema_prefix) end end res end + + private + + def excluded_model?(table) + Apartment.excluded_models.any? { |m| m.constantize.table_name == table } + end end require 'active_record/connection_adapters/postgresql_adapter' From 6c7fec2704c48cbea10af3bdb7e2b9c9dbe780bd Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Thu, 3 Feb 2022 00:13:17 +0100 Subject: [PATCH 11/14] fix conditional sub --- lib/apartment/active_record/postgresql_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index 333ec2a6..c31fc4c3 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -14,8 +14,8 @@ def default_sequence_name(table, _column) default_tenant_prefix = "#{Apartment::Tenant.default_tenant}." # NOTE: Excluded models should always access the sequence from the default # tenant schema - if excluded_model?(table) && schema_prefix != default_tenant_prefix - res.sub!(schema_prefix, default_tenant_prefix) + if excluded_model?(table) + res.sub!(schema_prefix, default_tenant_prefix) if schema_prefix != default_tenant_prefix else res.delete_prefix!(schema_prefix) end From 472027c25798d7c0326e4bf088eed58d89ff2c62 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Thu, 3 Feb 2022 01:11:42 +0100 Subject: [PATCH 12/14] only require apartment PostgreSQLAdapter if were using postgres --- lib/apartment.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/apartment.rb b/lib/apartment.rb index a4aba389..8bb50440 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -8,7 +8,10 @@ require_relative 'apartment/log_subscriber' -require_relative 'apartment/active_record/postgresql_adapter' +if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) + require_relative 'apartment/active_record/postgresql_adapter' +end + if ActiveRecord.version.release >= Gem::Version.new('6.0') require_relative 'apartment/active_record/connection_handling' end From a826d25231ec3c6c2ecc59f367131697c85cad94 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Thu, 3 Feb 2022 01:42:02 +0100 Subject: [PATCH 13/14] move require of postgresql adapter patch to postgresql adapter --- lib/apartment.rb | 4 ---- lib/apartment/adapters/postgresql_adapter.rb | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/apartment.rb b/lib/apartment.rb index 8bb50440..8a774b5b 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -8,10 +8,6 @@ require_relative 'apartment/log_subscriber' -if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) - require_relative 'apartment/active_record/postgresql_adapter' -end - if ActiveRecord.version.release >= Gem::Version.new('6.0') require_relative 'apartment/active_record/connection_handling' end diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index 47584911..7b85aa51 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'apartment/adapters/abstract_adapter' +require 'apartment/active_record/postgresql_adapter' module Apartment module Tenant From 8d9bb82c72381f6f9d7f18cdd26ae208752d4f83 Mon Sep 17 00:00:00 2001 From: Rui Baltazar Date: Thu, 3 Feb 2022 01:50:17 +0100 Subject: [PATCH 14/14] cleanup default sequence name --- .../active_record/postgresql_adapter.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/apartment/active_record/postgresql_adapter.rb b/lib/apartment/active_record/postgresql_adapter.rb index c31fc4c3..ef878111 100644 --- a/lib/apartment/active_record/postgresql_adapter.rb +++ b/lib/apartment/active_record/postgresql_adapter.rb @@ -9,17 +9,17 @@ module Apartment::PostgreSqlAdapterPatch def default_sequence_name(table, _column) res = super schema_prefix = "#{Apartment::Tenant.current}." + default_tenant_prefix = "#{Apartment::Tenant.default_tenant}." - if res&.starts_with?(schema_prefix) - default_tenant_prefix = "#{Apartment::Tenant.default_tenant}." - # NOTE: Excluded models should always access the sequence from the default - # tenant schema - if excluded_model?(table) - res.sub!(schema_prefix, default_tenant_prefix) if schema_prefix != default_tenant_prefix - else - res.delete_prefix!(schema_prefix) - end + # NOTE: Excluded models should always access the sequence from the default + # tenant schema + if excluded_model?(table) + res.sub!(schema_prefix, default_tenant_prefix) if schema_prefix != default_tenant_prefix + return res end + + res.delete_prefix!(schema_prefix) if res&.starts_with?(schema_prefix) + res end