From 62b1350b8df4d6339e4a098e8e058c416f3a7280 Mon Sep 17 00:00:00 2001
From: Boaz Yaniv <boazyan@gmail.com>
Date: Tue, 16 Aug 2016 11:27:07 +0900
Subject: [PATCH 1/4] Added new parameter to Query::Exec

---
 README.md | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index a3ba1c9..070f3bc 100644
--- a/README.md
+++ b/README.md
@@ -337,12 +337,13 @@ Stacks around low-level query execution
 
   Callback stack wraps the emission of sql to the underlying dbms gem.
 
-      Env Field    | Description | Initialized
-    --- | --- | ---
-    `:result` | The result of the database query | *unset*
-    `:caller`     | The ActiveRecord::SchemaCreation instance | *context*
-    `:sql`        | The SQL string | *context*
-    `:binds`      | Values to substitute into the SQL string
+    Env Field     | Description                                  | Initialized
+    ------------- | -------------------------------------------- | -----------
+    `:result`     | The result of the database query             | *unset*
+    `:caller`     | The ActiveRecord::SchemaCreation instance    | *context*
+    `:sql`        | The SQL string                               | *context*
+    `:binds`      | Values to substitute into the SQL string     |
+    `:prepare`    | Whether to cache the prepared statement      |
     `:query_name` | Label sometimes used by ActiveRecord logging | *arg*
 
 

From 9d08e3bf992d922f0d7bfa64841797d88dc84d45 Mon Sep 17 00:00:00 2001
From: Boaz Yaniv <boazyan@gmail.com>
Date: Sun, 23 Oct 2016 12:54:22 +0900
Subject: [PATCH 2/4] Updated Gemfile specs to exclude Rails 5 beta versions.

---
 gemfiles/activerecord-5.0/Gemfile.base | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gemfiles/activerecord-5.0/Gemfile.base b/gemfiles/activerecord-5.0/Gemfile.base
index b583c0f..b18bf66 100644
--- a/gemfiles/activerecord-5.0/Gemfile.base
+++ b/gemfiles/activerecord-5.0/Gemfile.base
@@ -1,3 +1,3 @@
 eval File.read File.expand_path('../../Gemfile.base', __FILE__)
 
-gem "activerecord", ">= 5.0.0.beta1", "< 5.1"
+gem "activerecord", "~> 5.0.0"

From e900d25fe31021d600617c5f529f3f36fcc7e859 Mon Sep 17 00:00:00 2001
From: Boaz Yaniv <boazyan@gmail.com>
Date: Sun, 23 Oct 2016 12:55:12 +0900
Subject: [PATCH 3/4] Added dependency on schema_plus_compatibility

---
 schema_plus_core.gemspec | 1 +
 1 file changed, 1 insertion(+)

diff --git a/schema_plus_core.gemspec b/schema_plus_core.gemspec
index 39e7d5a..eae228d 100644
--- a/schema_plus_core.gemspec
+++ b/schema_plus_core.gemspec
@@ -27,6 +27,7 @@ Gem::Specification.new do |gem|
   gem.add_development_dependency "rspec", "~> 3.0.0"
   gem.add_development_dependency "rspec-given"
   gem.add_development_dependency "schema_dev", "~> 3.7"
+  #gem.add_development_dependency "schema_compatibility", "~> 0.3"
   gem.add_development_dependency "simplecov"
   gem.add_development_dependency "simplecov-gem-profile"
   gem.add_development_dependency "its-it"

From d5a6c1a665c2cf7d6d54905d5c78bed92425a4e7 Mon Sep 17 00:00:00 2001
From: Boaz Yaniv <boazyan@gmail.com>
Date: Sun, 23 Oct 2016 12:55:58 +0900
Subject: [PATCH 4/4] Connection#data_sources now supports custom WHERE
 constraints.

Middlewares can hook into Connection#data_sources and add their
own SQL WHERE constraints, e.g. to filter out tables.

The rails default implementation is overriden, but results should
be the same. Schema query implementation is unified for data sources
and views now.
---
 lib/schema_plus/core.rb                       |  1 +
 .../connection_adapters/abstract_adapter.rb   | 12 ++++++++
 .../connection_adapters/mysql2_adapter.rb     | 30 +++++++++++++++++--
 .../connection_adapters/postgresql_adapter.rb | 17 +++++++++--
 .../connection_adapters/sqlite3_adapter.rb    | 23 ++++++++++++--
 lib/schema_plus/core/middleware.rb            |  6 +++-
 spec/middleware_spec.rb                       | 25 +++++++++++++---
 spec/support/test_reporter.rb                 |  1 +
 8 files changed, 104 insertions(+), 11 deletions(-)

diff --git a/lib/schema_plus/core.rb b/lib/schema_plus/core.rb
index 73f22d5..70fef2c 100644
--- a/lib/schema_plus/core.rb
+++ b/lib/schema_plus/core.rb
@@ -1,4 +1,5 @@
 require "schema_monkey"
+require "schema_plus_compatibility"
 require 'its-it'
 require "pathname"
 
diff --git a/lib/schema_plus/core/active_record/connection_adapters/abstract_adapter.rb b/lib/schema_plus/core/active_record/connection_adapters/abstract_adapter.rb
index 3e1f1b7..6742d5b 100644
--- a/lib/schema_plus/core/active_record/connection_adapters/abstract_adapter.rb
+++ b/lib/schema_plus/core/active_record/connection_adapters/abstract_adapter.rb
@@ -4,6 +4,18 @@ module ActiveRecord
       module ConnectionAdapters
         module AbstractAdapter
 
+          def _append_where_constraints(sql, where_constraints)
+            where_constraints.each do |where_constraints|
+              sql << " AND (#{where_constraints})"
+            end
+          end
+
+          def _select_data_sources(where_constraints = [], types = nil)
+            sql = _data_sources_sql types
+            _append_where_constraints sql, where_constraints
+            select_values sql, 'SCHEMA'
+          end
+
           def add_column(table_name, name, type, options = {})
             options = options.deep_dup
             SchemaMonkey::Middleware::Migration::Column.start(caller: self, operation: :add, table_name: table_name, column_name: name, type: type, implements_reference: options.delete(:_implements_reference), options: options) do |env|
diff --git a/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb b/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb
index 8721c7a..c6f213b 100644
--- a/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb
+++ b/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb
@@ -4,6 +4,26 @@ module ActiveRecord
       module ConnectionAdapters
         module Mysql2Adapter
 
+          def _data_sources_sql(types = nil)
+            sql = "SELECT table_name FROM information_schema.tables\n"
+            sql << "WHERE table_schema = #{quote(@config[:database])}"
+            if types
+              supported_types = types & %i[table view]
+              if supported_types.length == 0
+                raise 'No supported data source types: please specify at least one of :table, :view'
+              elsif supported_types.length == 1
+                # If both tables and views are requested, no need to add an extra clause
+                if supported_types[0] == :table
+                  table_type = 'BASE_TABLE'
+                else
+                  table_type = 'VIEW'
+                end
+                sql << " AND table_type = '#{table_type}'"
+              end
+            end
+            sql
+          end
+
           def change_column(table_name, name, type, options = {})
             SchemaMonkey::Middleware::Migration::Column.start(caller: self, operation: :change, table_name: table_name, column_name: name, type: type, options: options.deep_dup) do |env|
               super env.table_name, env.column_name, env.type, env.options
@@ -35,11 +55,17 @@ def indexes(table_name, query_name=nil)
           end
 
           def data_sources
-            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: []) { |env|
-              env.sources += super
+            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: [], where_constraints: []) { |env|
+              env.sources += _select_data_sources env.where_constraints
             }.sources
           end
 
+          def views
+            SchemaMonkey::Middleware::Schema::Views.start(connection: self, views: [], where_constraints: []) { |env|
+              env.views += _select_data_sources env.where_constraints, [:view]
+            }.views
+          end
+
           def select_rows(sql, name=nil, binds=[])
             SchemaMonkey::Middleware::Query::Exec.start(connection: self, sql: sql, query_name: name, binds: binds) { |env|
               env.result = super env.sql, env.query_name, env.binds
diff --git a/lib/schema_plus/core/active_record/connection_adapters/postgresql_adapter.rb b/lib/schema_plus/core/active_record/connection_adapters/postgresql_adapter.rb
index 618183d..1c5d970 100644
--- a/lib/schema_plus/core/active_record/connection_adapters/postgresql_adapter.rb
+++ b/lib/schema_plus/core/active_record/connection_adapters/postgresql_adapter.rb
@@ -3,6 +3,13 @@ module Core
     module ActiveRecord
       module ConnectionAdapters
         module PostgresqlAdapter
+          def _data_sources_sql(types = nil)
+            types ||= %i[table view materialized_view]
+            type_map = { table: 'r', view: 'v', materialized_view: 'm' }
+            types.map! {|type| type_map[type]}
+            types.compact!
+            _pg_relations_sql(types)
+          end
 
           # quick hack fix quoting of column default functions to allow eval() when we
           # capture the stream.
@@ -65,10 +72,16 @@ def indexes(table_name, query_name=nil)
           end
 
           def data_sources
-            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: []) { |env|
-              env.sources += super
+            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: [], where_constraints: []) { |env|
+              env.sources += _select_data_sources env.where_constraints
             }.sources
           end
+
+          def views
+            SchemaMonkey::Middleware::Schema::Views.start(connection: self, views: [], where_constraints: []) { |env|
+              env.views += _select_data_sources env.where_constraints, %i[view materialized_view]
+            }.views
+          end
         end
       end
     end
diff --git a/lib/schema_plus/core/active_record/connection_adapters/sqlite3_adapter.rb b/lib/schema_plus/core/active_record/connection_adapters/sqlite3_adapter.rb
index dcf8181..19b0c32 100644
--- a/lib/schema_plus/core/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/lib/schema_plus/core/active_record/connection_adapters/sqlite3_adapter.rb
@@ -3,6 +3,19 @@ module Core
     module ActiveRecord
       module ConnectionAdapters
         module Sqlite3Adapter
+          def _data_sources_sql(types = nil)
+            supported_types = %i[table view]
+            types = types & supported_types || supported_types
+            if types.length == 0
+              raise 'No supported data source types: please specify at least one of :table, :view'
+            elsif types.length == 1
+              type_query = "type = '#{types.first}'"
+            else
+              type_list = types.map{|x| "'#{x}'"}.join ','
+              type_query = "type IN (#{type_list})"
+            end
+            "SELECT name FROM sqlite_master WHERE #{type_query} AND name <> 'sqlite_sequence'"
+          end
 
           def rename_table(table_name, new_name)
             SchemaMonkey::Middleware::Migration::RenameTable.start(connection: self, table_name: table_name, new_name: new_name) do |env|
@@ -35,10 +48,16 @@ def indexes(table_name, query_name=nil)
           end
 
           def data_sources
-            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: []) { |env|
-              env.sources += super
+            SchemaMonkey::Middleware::Schema::DataSources.start(connection: self, sources: [], where_constraints: []) { |env|
+              env.sources += _select_data_sources env.where_constraints
             }.sources
           end
+
+          def views
+            SchemaMonkey::Middleware::Schema::Views.start(connection: self, views: [], where_constraints: []) { |env|
+              env.views += _select_data_sources env.where_constraints, [:view]
+            }.views
+          end
         end
       end
     end
diff --git a/lib/schema_plus/core/middleware.rb b/lib/schema_plus/core/middleware.rb
index 19653bc..bc04433 100644
--- a/lib/schema_plus/core/middleware.rb
+++ b/lib/schema_plus/core/middleware.rb
@@ -17,7 +17,11 @@ module Indexes
         end
 
         module DataSources
-          ENV = [:connection, :sources]
+          ENV = [:connection, :sources, :where_constraints]
+        end
+
+        module Views
+          ENV = [:connection, :views, :where_constraints]
         end
       end
 
diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb
index e49c8ec..3f9ff26 100644
--- a/spec/middleware_spec.rb
+++ b/spec/middleware_spec.rb
@@ -6,7 +6,7 @@
   let(:connection) { ::ActiveRecord::Base.connection }
 
   Given {
-    migration.create_table "things"
+    migration.create_table 'things'
     class Thing < ActiveRecord::Base ; end
   }
 
@@ -30,7 +30,12 @@ class Thing < ActiveRecord::Base ; end
     end
 
     context TestReporter::Middleware::Schema::DataSources do
-      Then { expect_middleware { connection.data_sources() } }
+      Given { migration.create_table 'other' }
+      Then { expect_middleware(env: { sources: contain_exactly('things', 'other')}) { connection.data_sources } }
+    end
+
+    context TestReporter::Middleware::Schema::Views do
+      Then { expect_middleware { connection.views } }
     end
 
     context TestReporter::Middleware::Schema::Indexes do
@@ -169,6 +174,18 @@ def env_match(env, matcher, bool: false)
         else
           expect(actual).to match val
         end
+      when Proc
+        if bool
+          return val.call(actual)
+        else
+          expect(actual).to val.call
+        end
+      when RSpec::Matchers::BuiltIn::BaseMatcher
+          if bool
+            return val.matches? actual
+          else
+            expect(actual).to val
+          end
       else
         if bool
           return false unless actual == val
@@ -180,10 +197,10 @@ def env_match(env, matcher, bool: false)
     true if bool
   end
 
-  def expect_middleware(env: {}, enable: {})
+  def expect_middleware(env: {}, enable: {}, before: nil)
     middleware = described_class
     begin
-      middleware.enable(-> (_env) { env_match(_env, enable, bool: true) })
+      middleware.enable -> _env { env_match(_env, enable, bool: true) }
       expect { yield }.to raise_error { |error|
         expect(error).to be_a TestReporter::Called
         expect(error.middleware).to eq middleware
diff --git a/spec/support/test_reporter.rb b/spec/support/test_reporter.rb
index a7a4f35..0a9b51f 100644
--- a/spec/support/test_reporter.rb
+++ b/spec/support/test_reporter.rb
@@ -28,6 +28,7 @@ module Schema
       module Define ;                   include Notify ; end
       module Indexes ;                  include Notify ; end
       module DataSources ;              include Notify ; end
+      module Views ;                    include Notify ; end
     end
 
     module Migration