aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb13
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/test/cases/view_test.rb31
7 files changed, 102 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index ace8ac73dd..b5c064fdb6 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add `#views` and `#view_exists?` methods on connection adapters.
+
+ *Ryuta Kamizono*
+
* Correctly dump composite primary key.
Example:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index ec4d2de07e..035637f7ab 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -36,6 +36,19 @@ module ActiveRecord
tables.include?(table_name.to_s)
end
+ # Returns an array of view names defined in the database.
+ def views
+ raise NotImplementedError, "#views is not implemented"
+ end
+
+ # Checks to see if the view +view_name+ exists on the database.
+ #
+ # view_exists?(:ebooks)
+ #
+ def view_exists?(view_name)
+ views.include?(view_name.to_s)
+ end
+
# Returns an array of indexes for the given table.
# def indexes(table_name, name = nil) end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index f31f763803..3c592dad84 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -550,6 +550,22 @@ module ActiveRecord
tables(nil, schema, table).any?
end
+ def views # :nodoc:
+ select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
+ end
+
+ def view_exists?(view_name) # :nodoc:
+ return false unless view_name.present?
+
+ schema, name = view_name.to_s.split('.', 2)
+ schema, name = @config[:database], schema unless name # A view was provided without a schema
+
+ sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
+ sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
+
+ select_values(sql, 'SCHEMA').any?
+ end
+
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
indexes = []
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 04b1cc479f..66d311dd48 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -90,6 +90,30 @@ module ActiveRecord
SQL
end
+ def views # :nodoc:
+ select_values(<<-SQL, 'SCHEMA')
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
+ AND n.nspname = ANY (current_schemas(false))
+ SQL
+ end
+
+ def view_exists?(view_name) # :nodoc:
+ name = Utils.extract_schema_qualified_name(view_name.to_s)
+ return false unless name.identifier
+
+ select_values(<<-SQL, 'SCHEMA').any?
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
+ AND c.relname = '#{name.identifier}'
+ AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ SQL
+ end
+
def drop_table(table_name, options = {}) # :nodoc:
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 6a78d6d23b..e2f52707e8 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -324,6 +324,19 @@ module ActiveRecord
table_name && tables(nil, table_name).any?
end
+ def views # :nodoc:
+ select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
+ end
+
+ def view_exists?(view_name) # :nodoc:
+ return false unless view_name.present?
+
+ sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
+ sql << " AND name = #{quote(view_name)}"
+
+ select_values(sql, 'SCHEMA').any?
+ end
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
def columns(table_name) #:nodoc:
table_structure(table_name).map do |field|
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index b1f0be2a25..f1f827f163 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -89,7 +89,7 @@ HEADER
end
def tables(stream)
- sorted_tables = @connection.tables.sort
+ sorted_tables = @connection.tables.sort - @connection.views
sorted_tables.each do |table_name|
table(table_name, stream) unless ignored?(table_name)
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index 1eb1430065..d7b3e223ed 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -1,7 +1,9 @@
require "cases/helper"
require "models/book"
+require "support/schema_dumping_helper"
module ViewBehavior
+ include SchemaDumpingHelper
extend ActiveSupport::Concern
included do
@@ -31,6 +33,15 @@ module ViewBehavior
assert_equal ["Ruby for Rails"], books.map(&:name)
end
+ def test_views
+ assert_equal [Ebook.table_name], @connection.views
+ end
+
+ def test_view_exists
+ view_name = Ebook.table_name
+ assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ end
+
def test_table_exists
view_name = Ebook.table_name
assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
@@ -53,6 +64,11 @@ module ViewBehavior
end
assert_nil model.primary_key
end
+
+ def test_does_not_dump_view_as_table
+ schema = dump_table_schema "ebooks"
+ assert_no_match %r{create_table "ebooks"}, schema
+ end
end
if ActiveRecord::Base.connection.supports_views?
@@ -70,6 +86,7 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
end
class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
fixtures :books
class Paperback < ActiveRecord::Base; end
@@ -91,6 +108,15 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
assert_equal ["Agile Web Development with Rails"], books.map(&:name)
end
+ def test_views
+ assert_equal [Paperback.table_name], @connection.views
+ end
+
+ def test_view_exists
+ view_name = Paperback.table_name
+ assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ end
+
def test_table_exists
view_name = Paperback.table_name
assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
@@ -109,6 +135,11 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
def test_does_not_have_a_primary_key
assert_nil Paperback.primary_key
end
+
+ def test_does_not_dump_view_as_table
+ schema = dump_table_schema "paperbacks"
+ assert_no_match %r{create_table "paperbacks"}, schema
+ end
end
# sqlite dose not support CREATE, INSERT, and DELETE for VIEW