提交 e2e63770 编写于 作者: R Rafael França 提交者: GitHub

Merge pull request #28068 from kamipo/refactor_data_sources

Extract `data_source_sql` to refactor data source statements
......@@ -31,6 +31,8 @@ def table_alias_for(table_name)
# Returns the relation names useable to back Active Record models.
# For most adapters this means all #tables and #views.
def data_sources
select_values(data_source_sql, "SCHEMA")
rescue NotImplementedError
tables | views
end
......@@ -39,12 +41,14 @@ def data_sources
# data_source_exists?(:ebooks)
#
def data_source_exists?(name)
select_values(data_source_sql(name), "SCHEMA").any? if name.present?
rescue NotImplementedError
data_sources.include?(name.to_s)
end
# Returns an array of table names defined in the database.
def tables
raise NotImplementedError, "#tables is not implemented"
select_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
end
# Checks to see if the table +table_name+ exists on the database.
......@@ -52,12 +56,14 @@ def tables
# table_exists?(:developers)
#
def table_exists?(table_name)
select_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
rescue NotImplementedError
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"
select_values(data_source_sql(type: "VIEW"), "SCHEMA")
end
# Checks to see if the view +view_name+ exists on the database.
......@@ -65,6 +71,8 @@ def views
# view_exists?(:ebooks)
#
def view_exists?(view_name)
select_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
rescue NotImplementedError
views.include?(view_name.to_s)
end
......@@ -1304,6 +1312,14 @@ def extract_new_default_value(default_or_changes)
def can_remove_index_by_name?(options)
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
end
def data_source_sql(name = nil, type: nil)
raise NotImplementedError
end
def quoted_scope(name = nil, type: nil)
raise NotImplementedError
end
end
end
end
......@@ -6,6 +6,7 @@
require "active_record/connection_adapters/mysql/schema_creation"
require "active_record/connection_adapters/mysql/schema_definitions"
require "active_record/connection_adapters/mysql/schema_dumper"
require "active_record/connection_adapters/mysql/schema_statements"
require "active_record/connection_adapters/mysql/type_metadata"
require "active_support/core_ext/string/strip"
......@@ -15,6 +16,7 @@ module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
include MySQL::Quoting
include MySQL::ColumnDumper
include MySQL::SchemaStatements
def update_table_definition(table_name, base) # :nodoc:
MySQL::Table.new(table_name, base)
......@@ -310,57 +312,6 @@ def collation
show_variable "collation_database"
end
def tables # :nodoc:
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
sql << " AND table_schema = #{quote(@config[:database])}"
select_values(sql, "SCHEMA")
end
def views # :nodoc:
select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA")
end
def data_sources # :nodoc:
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(@config[:database])}"
select_values(sql, "SCHEMA")
end
def table_exists?(table_name) # :nodoc:
return false unless table_name.present?
schema, name = extract_schema_qualified_name(table_name)
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
select_values(sql, "SCHEMA").any?
end
def data_source_exists?(table_name) # :nodoc:
return false unless table_name.present?
schema, name = extract_schema_qualified_name(table_name)
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
select_values(sql, "SCHEMA").any?
end
def view_exists?(view_name) # :nodoc:
return false unless view_name.present?
schema, name = extract_schema_qualified_name(view_name)
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
def truncate(table_name, name = nil)
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
......@@ -406,13 +357,13 @@ def new_column_from_field(table_name, field) # :nodoc:
end
def table_comment(table_name) # :nodoc:
schema, name = extract_schema_qualified_name(table_name)
scope = quoted_scope(table_name)
select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
WHERE table_schema = #{quote(schema)}
AND table_name = #{quote(name)}
WHERE table_schema = #{scope[:schema]}
AND table_name = #{scope[:name]}
SQL
end
......@@ -512,7 +463,7 @@ def add_sql_comment!(sql, comment) # :nodoc:
def foreign_keys(table_name)
raise ArgumentError unless table_name.present?
schema, name = extract_schema_qualified_name(table_name)
scope = quoted_scope(table_name)
fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
......@@ -525,9 +476,9 @@ def foreign_keys(table_name)
JOIN information_schema.referential_constraints rc
USING (constraint_schema, constraint_name)
WHERE fk.referenced_column_name IS NOT NULL
AND fk.table_schema = #{quote(schema)}
AND fk.table_name = #{quote(name)}
AND rc.table_name = #{quote(name)}
AND fk.table_schema = #{scope[:schema]}
AND fk.table_name = #{scope[:name]}
AND rc.table_name = #{scope[:name]}
SQL
fk_info.map do |row|
......@@ -599,14 +550,14 @@ def show_variable(name)
def primary_keys(table_name) # :nodoc:
raise ArgumentError unless table_name.present?
schema, name = extract_schema_qualified_name(table_name)
scope = quoted_scope(table_name)
select_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
AND table_schema = #{quote(schema)}
AND table_name = #{quote(name)}
AND table_schema = #{scope[:schema]}
AND table_name = #{scope[:name]}
ORDER BY ordinal_position
SQL
end
......@@ -940,12 +891,6 @@ def mismatched_foreign_key(message)
)
end
def extract_schema_qualified_name(string) # :nodoc:
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
schema, name = @config[:database], schema unless name
[schema, name]
end
def integer_to_sql(limit) # :nodoc:
case limit
when 1; "tinyint"
......
module ActiveRecord
module ConnectionAdapters
module MySQL
module SchemaStatements # :nodoc:
private
def data_source_sql(name = nil, type: nil)
scope = quoted_scope(name, type: type)
sql = "SELECT table_name FROM information_schema.tables"
sql << " WHERE table_schema = #{scope[:schema]}"
sql << " AND table_name = #{scope[:name]}" if scope[:name]
sql << " AND table_type = #{scope[:type]}" if scope[:type]
sql
end
def quoted_scope(name = nil, type: nil)
schema, name = extract_schema_qualified_name(name)
scope = {}
scope[:schema] = schema ? quote(schema) : "database()"
scope[:name] = quote(name) if name
scope[:type] = quote(type) if type
scope
end
def extract_schema_qualified_name(string)
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
schema, name = nil, schema unless name
[schema, name]
end
end
end
end
end
......@@ -54,81 +54,13 @@ def drop_database(name) #:nodoc:
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
end
# Returns the list of all tables in the schema search path.
def tables
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", "SCHEMA")
end
def data_sources # :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 ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
AND n.nspname = ANY (current_schemas(false))
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
# Returns true if table exists.
# If the schema is not specified as part of +name+ then it will only find tables within
# the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
select_values(<<-SQL, "SCHEMA").any?
SELECT tablename
FROM pg_tables
WHERE tablename = #{quote(name.identifier)}
AND schemaname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
def data_source_exists?(name) # :nodoc:
name = Utils.extract_schema_qualified_name(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 ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
AND c.relname = #{quote(name.identifier)}
AND n.nspname = #{name.schema ? quote(name.schema) : "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 = #{quote(name.identifier)}
AND n.nspname = #{name.schema ? quote(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
# Returns true if schema exists.
def schema_exists?(name)
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", "SCHEMA").to_i > 0
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
end
# Verifies existence of an index with a given name.
......@@ -138,8 +70,8 @@ def index_name_exists?(table_name, index_name, default = nil)
Passing default to #index_name_exists? is deprecated without replacement.
MSG
end
table = Utils.extract_schema_qualified_name(table_name.to_s)
index = Utils.extract_schema_qualified_name(index_name.to_s)
table = quoted_scope(table_name)
index = quoted_scope(index_name)
select_value(<<-SQL, "SCHEMA").to_i > 0
SELECT COUNT(*)
......@@ -148,9 +80,9 @@ def index_name_exists?(table_name, index_name, default = nil)
INNER JOIN pg_class i ON d.indexrelid = i.oid
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
WHERE i.relkind = 'i'
AND i.relname = '#{index.identifier}'
AND t.relname = '#{table.identifier}'
AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
AND i.relname = #{index[:name]}
AND t.relname = #{table[:name]}
AND n.nspname = #{index[:schema]}
SQL
end
......@@ -162,7 +94,7 @@ def indexes(table_name, name = nil) # :nodoc:
MSG
end
table = Utils.extract_schema_qualified_name(table_name.to_s)
scope = quoted_scope(table_name)
result = query(<<-SQL, "SCHEMA")
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
......@@ -176,8 +108,8 @@ def indexes(table_name, name = nil) # :nodoc:
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
WHERE i.relkind = 'i'
AND d.indisprimary = 'f'
AND t.relname = '#{table.identifier}'
AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
AND t.relname = #{scope[:name]}
AND n.nspname = #{scope[:schema]}
ORDER BY i.relname
SQL
......@@ -239,22 +171,22 @@ def table_options(table_name) # :nodoc:
# Returns a comment stored in database for given table
def table_comment(table_name) # :nodoc:
name = Utils.extract_schema_qualified_name(table_name.to_s)
if name.identifier
scope = quoted_scope(table_name, type: "BASE TABLE")
if scope[:name]
select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = #{quote(name.identifier)}
AND c.relkind IN ('r') -- (r)elation/table
AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
WHERE c.relname = #{scope[:name]}
AND c.relkind IN (#{scope[:type]})
AND n.nspname = #{scope[:schema]}
SQL
end
end
# Returns the current database name.
def current_database
select_value("select current_database()", "SCHEMA")
select_value("SELECT current_database()", "SCHEMA")
end
# Returns the current schema name.
......@@ -430,17 +362,15 @@ def pk_and_sequence_for(table) #:nodoc:
end
def primary_keys(table_name) # :nodoc:
name = Utils.extract_schema_qualified_name(table_name.to_s)
scope = quoted_scope(table_name)
select_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc
ON kcu.table_name = tc.table_name
AND kcu.table_schema = tc.table_schema
AND kcu.constraint_name = tc.constraint_name
USING (table_schema, table_name, constraint_name)
WHERE constraint_type = 'PRIMARY KEY'
AND kcu.table_name = #{quote(name.identifier)}
AND kcu.table_schema = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
AND kcu.table_name = #{scope[:name]}
AND kcu.table_schema = #{scope[:schema]}
ORDER BY kcu.ordinal_position
SQL
end
......@@ -579,6 +509,7 @@ def rename_index(table_name, old_name, new_name)
end
def foreign_keys(table_name)
scope = quoted_scope(table_name)
fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
FROM pg_constraint c
......@@ -588,8 +519,8 @@ def foreign_keys(table_name)
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
JOIN pg_namespace t3 ON c.connamespace = t3.oid
WHERE c.contype = 'f'
AND t1.relname = #{quote(table_name)}
AND t3.nspname = ANY (current_schemas(false))
AND t1.relname = #{scope[:name]}
AND t3.nspname = #{scope[:schema]}
ORDER BY c.conname
SQL
......@@ -673,6 +604,39 @@ def fetch_type_metadata(column_name, sql_type, oid, fmod)
)
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
end
private
def data_source_sql(name = nil, type: nil)
scope = quoted_scope(name, type: type)
scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view
sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
sql << " WHERE n.nspname = #{scope[:schema]}"
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
sql << " AND c.relkind IN (#{scope[:type]})"
sql
end
def quoted_scope(name = nil, type: nil)
schema, name = extract_schema_qualified_name(name)
type = \
case type
when "BASE TABLE"
"'r'"
when "VIEW"
"'v','m'"
end
scope = {}
scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
scope[:name] = quote(name) if name
scope[:type] = type if type
scope
end
def extract_schema_qualified_name(string)
name = Utils.extract_schema_qualified_name(string.to_s)
[name.schema, name.identifier]
end
end
end
end
......
......@@ -3,6 +3,7 @@
require "pg"
require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/statement_pool"
require "active_record/connection_adapters/postgresql/column"
require "active_record/connection_adapters/postgresql/database_statements"
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
......@@ -15,7 +16,6 @@
require "active_record/connection_adapters/postgresql/schema_statements"
require "active_record/connection_adapters/postgresql/type_metadata"
require "active_record/connection_adapters/postgresql/utils"
require "active_record/connection_adapters/statement_pool"
module ActiveRecord
module ConnectionHandling # :nodoc:
......
module ActiveRecord
module ConnectionAdapters
module SQLite3
module SchemaStatements # :nodoc:
private
def data_source_sql(name = nil, type: nil)
scope = quoted_scope(name, type: type)
scope[:type] ||= "'table','view'"
sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
sql << " AND name = #{scope[:name]}" if scope[:name]
sql << " AND type IN (#{scope[:type]})"
sql
end
def quoted_scope(name = nil, type: nil)
type = \
case type
when "BASE TABLE"
"'table'"
when "VIEW"
"'view'"
end
scope = {}
scope[:name] = quote(name) if name
scope[:type] = type if type
scope
end
end
end
end
end
......@@ -5,6 +5,7 @@
require "active_record/connection_adapters/sqlite3/schema_creation"
require "active_record/connection_adapters/sqlite3/schema_definitions"
require "active_record/connection_adapters/sqlite3/schema_dumper"
require "active_record/connection_adapters/sqlite3/schema_statements"
gem "sqlite3", "~> 1.3.6"
require "sqlite3"
......@@ -55,6 +56,7 @@ class SQLite3Adapter < AbstractAdapter
include SQLite3::Quoting
include SQLite3::ColumnDumper
include SQLite3::SchemaStatements
NATIVE_DATABASE_TYPES = {
primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
......@@ -265,45 +267,6 @@ def exec_rollback_db_transaction #:nodoc:
# SCHEMA STATEMENTS ========================================
def tables # :nodoc:
select_values("SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'", "SCHEMA")
end
def data_sources # :nodoc:
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA")
end
def views # :nodoc:
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA")
end
def table_exists?(table_name) # :nodoc:
return false unless table_name.present?
sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'"
sql << " AND name = #{quote(table_name)}"
select_values(sql, "SCHEMA").any?
end
def data_source_exists?(table_name) # :nodoc:
return false unless table_name.present?
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
sql << " AND name = #{quote(table_name)}"
select_values(sql, "SCHEMA").any?
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
def new_column_from_field(table_name, field) # :nondoc:
case field["dflt_value"]
when /^null$/i
......@@ -535,7 +498,7 @@ def copy_table_contents(from, to, columns, rename = {})
end
def sqlite_version
@sqlite_version ||= SQLite3Adapter::Version.new(select_value("select sqlite_version(*)"))
@sqlite_version ||= SQLite3Adapter::Version.new(select_value("SELECT sqlite_version(*)"))
end
def translate_exception(exception, message)
......
......@@ -260,8 +260,7 @@ def test_tables
def test_tables_logs_name
sql = <<-SQL
SELECT name FROM sqlite_master
WHERE type = 'table' AND name <> 'sqlite_sequence'
SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence' AND type IN ('table')
SQL
assert_logged [[sql.squish, "SCHEMA", []]] do
@conn.tables
......@@ -279,8 +278,7 @@ def test_indexes_logs_name
def test_table_exists_logs_name
with_example_table do
sql = <<-SQL
SELECT name FROM sqlite_master
WHERE type = 'table' AND name <> 'sqlite_sequence' AND name = 'ex'
SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence' AND name = 'ex' AND type IN ('table')
SQL
assert_logged [[sql.squish, "SCHEMA", []]] do
assert @conn.table_exists?("ex")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册