pg, PostgreSQL::Name
to hold schema qualified names.
This commit is contained in:
parent
6963e33829
commit
7b8d95d58f
@ -150,12 +150,7 @@ def quote_string(s) #:nodoc:
|
||||
# - "schema.name".table_name
|
||||
# - "schema.name"."table.name"
|
||||
def quote_table_name(name)
|
||||
schema, table = Utils.extract_schema_and_table(name.to_s)
|
||||
if schema
|
||||
"#{quote_column_name(schema)}.#{quote_column_name(table)}"
|
||||
else
|
||||
quote_column_name(table)
|
||||
end
|
||||
Utils.extract_schema_qualified_name(name.to_s).quoted
|
||||
end
|
||||
|
||||
def quote_table_name_for_assignment(table, attr)
|
||||
|
@ -97,16 +97,16 @@ def tables(name = nil)
|
||||
# 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)
|
||||
schema, table = Utils.extract_schema_and_table(name.to_s)
|
||||
return false unless table
|
||||
name = Utils.extract_schema_qualified_name(name.to_s)
|
||||
return false unless name.identifier
|
||||
|
||||
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
||||
SELECT COUNT(*)
|
||||
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 = '#{table.gsub(/(^"|"$)/,'')}'
|
||||
AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
|
||||
AND c.relname = '#{name.identifier}'
|
||||
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
||||
SQL
|
||||
end
|
||||
|
||||
|
@ -1,13 +1,54 @@
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
module PostgreSQL
|
||||
# Value Object to hold a schema qualified name.
|
||||
# This is usually the name of a PostgreSQL relation but it can also represent
|
||||
# schema qualified type names. +schema+ and +identifier+ are unquoted to prevent
|
||||
# double quoting.
|
||||
class Name # :nodoc:
|
||||
SEPARATOR = "."
|
||||
attr_reader :schema, :identifier
|
||||
|
||||
def initialize(schema, identifier)
|
||||
@schema, @identifier = unquote(schema), unquote(identifier)
|
||||
end
|
||||
|
||||
def to_s
|
||||
parts.join SEPARATOR
|
||||
end
|
||||
|
||||
def quoted
|
||||
parts.map { |p| PGconn.quote_ident(p) }.join SEPARATOR
|
||||
end
|
||||
|
||||
def ==(o)
|
||||
o.class == self.class && o.parts == parts
|
||||
end
|
||||
alias_method :eql?, :==
|
||||
|
||||
def hash
|
||||
parts.hash
|
||||
end
|
||||
|
||||
protected
|
||||
def unquote(part)
|
||||
return unless part
|
||||
part.gsub(/(^"|"$)/,'')
|
||||
end
|
||||
|
||||
def parts
|
||||
@parts ||= [@schema, @identifier].compact
|
||||
end
|
||||
end
|
||||
|
||||
module Utils # :nodoc:
|
||||
extend self
|
||||
|
||||
# Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
|
||||
# +schema_name+ is nil if not specified in +name+.
|
||||
# +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
|
||||
# +name+ supports the range of schema/table references understood by PostgreSQL, for example:
|
||||
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
|
||||
# extracted from +string+.
|
||||
# +schema+ is nil if not specified in +string+.
|
||||
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
|
||||
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
|
||||
#
|
||||
# * <tt>table_name</tt>
|
||||
# * <tt>"table.name"</tt>
|
||||
@ -15,9 +56,9 @@ module Utils # :nodoc:
|
||||
# * <tt>schema_name."table.name"</tt>
|
||||
# * <tt>"schema_name".table_name</tt>
|
||||
# * <tt>"schema.name"."table name"</tt>
|
||||
def extract_schema_and_table(name)
|
||||
table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
|
||||
[schema, table]
|
||||
def extract_schema_qualified_name(string)
|
||||
table, schema = string.scan(/[^".\s]+|"[^"]*"/)[0..1].reverse
|
||||
PostgreSQL::Name.new(schema, table)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,9 +1,10 @@
|
||||
require 'cases/helper'
|
||||
|
||||
class PostgreSQLUtilsTest < ActiveSupport::TestCase
|
||||
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
|
||||
include ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
|
||||
|
||||
def test_extract_schema_and_table
|
||||
def test_extract_schema_qualified_name
|
||||
{
|
||||
%(table_name) => [nil,'table_name'],
|
||||
%("table.name") => [nil,'table.name'],
|
||||
@ -14,7 +15,47 @@ def test_extract_schema_and_table
|
||||
%("even spaces".table) => ['even spaces','table'],
|
||||
%(schema."table.name") => ['schema', 'table.name']
|
||||
}.each do |given, expect|
|
||||
assert_equal expect, extract_schema_and_table(given)
|
||||
assert_equal Name.new(*expect), extract_schema_qualified_name(given)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PostgreSQLNameTest < ActiveSupport::TestCase
|
||||
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
|
||||
|
||||
test "represents itself as schema.name" do
|
||||
obj = Name.new("public", "articles")
|
||||
assert_equal "public.articles", obj.to_s
|
||||
end
|
||||
|
||||
test "without schema, represents itself as name only" do
|
||||
obj = Name.new(nil, "articles")
|
||||
assert_equal "articles", obj.to_s
|
||||
end
|
||||
|
||||
test "quoted returns a string representation usable in a query" do
|
||||
assert_equal %("articles"), Name.new(nil, "articles").quoted
|
||||
assert_equal %("public"."articles"), Name.new("public", "articles").quoted
|
||||
end
|
||||
|
||||
test "prevents double quoting" do
|
||||
name = Name.new('"quoted_schema"', '"quoted_table"')
|
||||
assert_equal "quoted_schema.quoted_table", name.to_s
|
||||
assert_equal %("quoted_schema"."quoted_table"), name.quoted
|
||||
end
|
||||
|
||||
test "equality based on state" do
|
||||
assert_equal Name.new("access", "users"), Name.new("access", "users")
|
||||
assert_equal Name.new(nil, "users"), Name.new(nil, "users")
|
||||
assert_not_equal Name.new(nil, "users"), Name.new("access", "users")
|
||||
assert_not_equal Name.new("access", "users"), Name.new("public", "users")
|
||||
assert_not_equal Name.new("public", "users"), Name.new("public", "articles")
|
||||
end
|
||||
|
||||
test "can be used as hash key" do
|
||||
hash = {Name.new("schema", "article_seq") => "success"}
|
||||
assert_equal "success", hash[Name.new("schema", "article_seq")]
|
||||
assert_equal nil, hash[Name.new("schema", "articles")]
|
||||
assert_equal nil, hash[Name.new("public", "article_seq")]
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user