Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Extra SQL where constraints for Views and DataSources middleware #17

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*


Expand Down
2 changes: 1 addition & 1 deletion gemfiles/activerecord-5.0/Gemfile.base
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions lib/schema_plus/core.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "schema_monkey"
require "schema_plus_compatibility"
require 'its-it'
require "pathname"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion lib/schema_plus/core/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions schema_plus_core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 21 additions & 4 deletions spec/middleware_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let(:connection) { ::ActiveRecord::Base.connection }

Given {
migration.create_table "things"
migration.create_table 'things'
class Thing < ActiveRecord::Base ; end
}

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions spec/support/test_reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down