aspgems-redhillonrails_core 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +194 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README +161 -0
- data/Rakefile +59 -0
- data/init.rb +1 -0
- data/lib/redhillonrails_core.rb +46 -0
- data/lib/redhillonrails_core/active_record/base.rb +45 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract/column.rb +14 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract/foreign_key_definition.rb +63 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract/index_definition.rb +11 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract/schema_statements.rb +23 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract/table_definition.rb +27 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/abstract_adapter.rb +84 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/mysql_adapter.rb +131 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/mysql_column.rb +8 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/postgresql_adapter.rb +131 -0
- data/lib/redhillonrails_core/active_record/connection_adapters/sqlite3_adapter.rb +115 -0
- data/lib/redhillonrails_core/active_record/schema.rb +25 -0
- data/lib/redhillonrails_core/active_record/schema_dumper.rb +75 -0
- data/lib/redhillonrails_core/version.rb +3 -0
- data/lib/tasks/db/comments.rake +9 -0
- data/redhillonrails_core.gemspec +27 -0
- data/spec/connections/mysql/connection.rb +18 -0
- data/spec/connections/mysql2/connection.rb +18 -0
- data/spec/connections/postgresql/connection.rb +15 -0
- data/spec/connections/sqlite3/connection.rb +14 -0
- data/spec/foreign_key_definition_spec.rb +21 -0
- data/spec/foreign_key_spec.rb +100 -0
- data/spec/index_definition_spec.rb +145 -0
- data/spec/index_spec.rb +67 -0
- data/spec/models/comment.rb +5 -0
- data/spec/models/post.rb +6 -0
- data/spec/models/user.rb +5 -0
- data/spec/schema/schema.rb +21 -0
- data/spec/schema_dumper_spec.rb +138 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/reference.rb +66 -0
- metadata +159 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord
|
2
|
+
module Base
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
class << base
|
10
|
+
alias_method_chain :abstract_class?, :redhillonrails_core
|
11
|
+
alias_method_chain :reset_column_information, :redhillonrails_core
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def base_class?
|
16
|
+
self == base_class
|
17
|
+
end
|
18
|
+
|
19
|
+
def abstract_class_with_redhillonrails_core?
|
20
|
+
abstract_class_without_redhillonrails_core? || !(name =~ /^Abstract/).nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_column_information_with_redhillonrails_core
|
24
|
+
reset_column_information_without_redhillonrails_core
|
25
|
+
@indexes = @foreign_keys = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def pluralized_table_name(table_name)
|
29
|
+
ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def indexes
|
33
|
+
@indexes ||= connection.indexes(table_name, "#{name} Indexes")
|
34
|
+
end
|
35
|
+
|
36
|
+
def foreign_keys
|
37
|
+
@foreign_keys ||= connection.foreign_keys(table_name, "#{name} Foreign Keys")
|
38
|
+
end
|
39
|
+
|
40
|
+
def reverse_foreign_keys
|
41
|
+
connection.reverse_foreign_keys(table_name, "#{name} Reverse Foreign Keys")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/redhillonrails_core/active_record/connection_adapters/abstract/foreign_key_definition.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
class ForeignKeyDefinition < Struct.new(:name, :table_name, :column_names, :references_table_name, :references_column_names, :on_update, :on_delete, :deferrable)
|
3
|
+
ACTIONS = {:cascade => "CASCADE", :restrict => "RESTRICT", :set_null => "SET NULL", :set_default => "SET DEFAULT", :no_action => "NO ACTION"}.freeze
|
4
|
+
|
5
|
+
def initialize(name, table_name, column_names, references_table_name, references_column_names, on_update = nil, on_delete = nil, deferrable = nil)
|
6
|
+
super(name, unquote(table_name), unquote(column_names), unquote(references_table_name), unquote(references_column_names), on_update, on_delete, deferrable)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_dump
|
10
|
+
dump = "add_foreign_key"
|
11
|
+
dump << " #{table_name.inspect}, [#{Array(column_names).collect { |name| name.inspect }.join(', ')}]"
|
12
|
+
dump << ", #{references_table_name.inspect}, [#{Array(references_column_names).collect { |name| name.inspect }.join(', ')}]"
|
13
|
+
dump << ", :on_update => :#{on_update}" if on_update
|
14
|
+
dump << ", :on_delete => :#{on_delete}" if on_delete
|
15
|
+
dump << ", :deferrable => #{deferrable}" if deferrable
|
16
|
+
dump << ", :name => #{name.inspect}" if name
|
17
|
+
dump
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_sql
|
21
|
+
sql = name ? "CONSTRAINT #{name} " : ""
|
22
|
+
sql << "FOREIGN KEY (#{Array(quoted_column_names).join(", ")}) REFERENCES #{quoted_references_table_name} (#{Array(quoted_references_column_names).join(", ")})"
|
23
|
+
sql << " ON UPDATE #{ACTIONS[on_update]}" if on_update
|
24
|
+
sql << " ON DELETE #{ACTIONS[on_delete]}" if on_delete
|
25
|
+
sql << " DEFERRABLE" if deferrable
|
26
|
+
sql
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :to_s :to_sql
|
30
|
+
|
31
|
+
def quoted_column_names
|
32
|
+
Array(column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def quoted_references_column_names
|
36
|
+
Array(references_column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# def quoted_table_name
|
40
|
+
# ::ActiveRecord::Base.connection.quote_table_name(table_name)
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def quoted_references_table_name
|
44
|
+
::ActiveRecord::Base.connection.quote_table_name(references_table_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def quote(name)
|
48
|
+
::ActiveRecord::Base.connection.quote(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def unquote(names)
|
52
|
+
if names.is_a?(Array)
|
53
|
+
names.collect { |name| __unqoute(name) }
|
54
|
+
else
|
55
|
+
__unqoute(names)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def __unqoute(value)
|
60
|
+
value.to_s.sub(/^["`](.*)["`]$/, '\1')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
module SchemaStatements
|
3
|
+
def self.included(base)
|
4
|
+
base.module_eval do
|
5
|
+
alias_method_chain :create_table, :redhillonrails_core
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_table_with_redhillonrails_core(name, options = {})
|
10
|
+
if options.include?(:comment)
|
11
|
+
options = options.dup
|
12
|
+
comment = options.delete(:comment)
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table_without_redhillonrails_core(name, options) do |table_defintion|
|
16
|
+
table_defintion.name = name
|
17
|
+
yield table_defintion if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
set_table_comment(name, comment) if comment
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
module TableDefinition
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
attr_accessor :name
|
6
|
+
alias_method_chain :initialize, :redhillonrails_core
|
7
|
+
alias_method_chain :to_sql, :redhillonrails_core
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize_with_redhillonrails_core(*args)
|
12
|
+
initialize_without_redhillonrails_core(*args)
|
13
|
+
@foreign_keys = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def foreign_key(column_names, references_table_name, references_column_names, options = {})
|
17
|
+
@foreign_keys << ForeignKeyDefinition.new(options[:name], nil, column_names, ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_sql_with_redhillonrails_core
|
22
|
+
sql = to_sql_without_redhillonrails_core
|
23
|
+
sql << ', ' << @foreign_keys * ', ' unless @foreign_keys.empty? || ActiveRecord::Schema.defining?
|
24
|
+
sql
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
module AbstractAdapter
|
3
|
+
def self.included(base)
|
4
|
+
base.module_eval do
|
5
|
+
alias_method_chain :initialize, :redhillonrails_core
|
6
|
+
alias_method_chain :drop_table, :redhillonrails_core
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize_with_redhillonrails_core(*args)
|
11
|
+
initialize_without_redhillonrails_core(*args)
|
12
|
+
adapter = nil
|
13
|
+
case adapter_name
|
14
|
+
# name of MySQL adapter depends on mysql gem
|
15
|
+
# * with mysql gem adapter is named MySQL
|
16
|
+
# * with mysql2 gem adapter is named Mysql2
|
17
|
+
# Here we handle this and hopefully futher adapter names
|
18
|
+
when /^MySQL/i
|
19
|
+
adapter = 'MysqlAdapter'
|
20
|
+
when 'PostgreSQL'
|
21
|
+
adapter = 'PostgresqlAdapter'
|
22
|
+
when 'SQLite'
|
23
|
+
adapter = 'Sqlite3Adapter'
|
24
|
+
end
|
25
|
+
if adapter
|
26
|
+
adapter_module = RedhillonrailsCore::ActiveRecord::ConnectionAdapters.const_get(adapter)
|
27
|
+
self.class.send(:include, adapter_module) unless self.class.include?(adapter_module)
|
28
|
+
end
|
29
|
+
# Needed from mysql2 >= 0.2.7
|
30
|
+
if adapter_name =~ /^Mysql2/i && defined?(ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition)
|
31
|
+
index_definition_module = RedhillonrailsCore::ActiveRecord::ConnectionAdapters::IndexDefinition
|
32
|
+
ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition.send(:include, index_definition_module) unless ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition.include?(index_definition_module)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_view(view_name, definition)
|
37
|
+
execute "CREATE VIEW #{view_name} AS #{definition}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def drop_view(view_name)
|
41
|
+
execute "DROP VIEW #{view_name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def views(name = nil)
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def view_definition(view_name, name = nil)
|
49
|
+
end
|
50
|
+
|
51
|
+
def foreign_keys(table_name, name = nil)
|
52
|
+
[]
|
53
|
+
end
|
54
|
+
|
55
|
+
def reverse_foreign_keys(table_name, name = nil)
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
|
60
|
+
foreign_key = ForeignKeyDefinition.new(options[:name], table_name, column_names, ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
|
61
|
+
execute "ALTER TABLE #{table_name} ADD #{foreign_key}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def remove_foreign_key(table_name, foreign_key_name)
|
65
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{foreign_key_name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def drop_table_with_redhillonrails_core(name, options = {})
|
69
|
+
reverse_foreign_keys(name).each do |foreign_key|
|
70
|
+
begin
|
71
|
+
remove_foreign_key(foreign_key.table_name, foreign_key.name)
|
72
|
+
rescue Exception => e
|
73
|
+
# TODO spec this
|
74
|
+
#there is a problem when using rollback if two tables have
|
75
|
+
#similar names. In that case, the plugin will try to remove a
|
76
|
+
#non-existing column and raise an exception. I rescue the
|
77
|
+
#exception so the migration can proceed
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
drop_table_without_redhillonrails_core(name, options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
module MysqlAdapter
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
alias_method_chain :remove_column, :redhillonrails_core
|
6
|
+
alias_method_chain :remove_columns, :redhillonrails_core
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_table_comment(table_name, comment)
|
11
|
+
execute "ALTER TABLE #{table_name} COMMENT='#{quote_string(comment)}'"
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_table_comment(table_name)
|
15
|
+
execute "ALTER TABLE #{table_name} COMMENT=''"
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_foreign_key(table_name, foreign_key_name)
|
19
|
+
execute "ALTER TABLE #{table_name} DROP FOREIGN KEY #{foreign_key_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_column_with_redhillonrails_core(table_name, *column_names)
|
23
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
24
|
+
column_names.flatten.each do |column_name|
|
25
|
+
foreign_keys(table_name).select { |foreign_key| foreign_key.column_names.include?(column_name.to_s) }.each do |foreign_key|
|
26
|
+
remove_foreign_key(table_name, foreign_key.name)
|
27
|
+
end
|
28
|
+
remove_column_without_redhillonrails_core(table_name, column_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias :remove_columns_with_redhillonrails_core :remove_column_with_redhillonrails_core
|
32
|
+
|
33
|
+
def foreign_keys(table_name, name = nil)
|
34
|
+
results = execute("SHOW CREATE TABLE #{quote_table_name(table_name)}", name)
|
35
|
+
|
36
|
+
foreign_keys = []
|
37
|
+
|
38
|
+
results.each do |row|
|
39
|
+
row[1].each_line do |line|
|
40
|
+
if line =~ /^ CONSTRAINT [`"](.+?)[`"] FOREIGN KEY \([`"](.+?)[`"]\) REFERENCES [`"](.+?)[`"] \((.+?)\)( ON DELETE (.+?))?( ON UPDATE (.+?))?,?$/
|
41
|
+
name = $1
|
42
|
+
column_names = $2
|
43
|
+
references_table_name = $3
|
44
|
+
references_column_names = $4
|
45
|
+
on_update = $8
|
46
|
+
on_delete = $6
|
47
|
+
on_update = on_update.downcase.gsub(' ', '_').to_sym if on_update
|
48
|
+
on_delete = on_delete.downcase.gsub(' ', '_').to_sym if on_delete
|
49
|
+
|
50
|
+
foreign_keys << ForeignKeyDefinition.new(name,
|
51
|
+
table_name, column_names.gsub('`', '').split(', '),
|
52
|
+
references_table_name, references_column_names.gsub('`', '').split(', '),
|
53
|
+
on_update, on_delete)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
foreign_keys
|
59
|
+
end
|
60
|
+
|
61
|
+
# def reverse_foreign_keys(table_name, name = nil)
|
62
|
+
# results = execute(<<-SQL, name)
|
63
|
+
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
64
|
+
# FROM information_schema.key_column_usage
|
65
|
+
# WHERE table_schema = SCHEMA()
|
66
|
+
# AND referenced_table_schema = table_schema
|
67
|
+
# ORDER BY constraint_name, ordinal_position;
|
68
|
+
# SQL
|
69
|
+
# current_foreign_key = nil
|
70
|
+
# foreign_keys = []
|
71
|
+
#
|
72
|
+
# results.each do |row|
|
73
|
+
# next unless table_name.casecmp(row[3]) == 0
|
74
|
+
# if current_foreign_key != row[0]
|
75
|
+
# foreign_keys << ForeignKeyDefinition.new(row[0], row[1], [], row[3], [])
|
76
|
+
# current_foreign_key = row[0]
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# foreign_keys.last.column_names << row[2]
|
80
|
+
# foreign_keys.last.references_column_names << row[4]
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# foreign_keys
|
84
|
+
# end
|
85
|
+
|
86
|
+
def reverse_foreign_keys(table_name, name = nil)
|
87
|
+
# @@schema ||= nil
|
88
|
+
# @@schema_version ||= 0
|
89
|
+
# current_version = ActiveRecord::Migrator.current_version
|
90
|
+
# if @@schema.nil? || @@schema_version != current_version
|
91
|
+
# @@schema_version = current_version
|
92
|
+
# ans = execute(<<-SQL, name)
|
93
|
+
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
94
|
+
# FROM information_schema.key_column_usage
|
95
|
+
# WHERE table_schema = SCHEMA()
|
96
|
+
# AND referenced_table_schema = table_schema
|
97
|
+
# ORDER BY constraint_name, ordinal_position;
|
98
|
+
# SQL
|
99
|
+
# @@schema = []
|
100
|
+
# ans.each do | row |
|
101
|
+
# @@schema << [row[0], row[1], row[2], row[3], row[4]]
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
# results = @@schema
|
105
|
+
|
106
|
+
results = execute(<<-SQL, name)
|
107
|
+
SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
108
|
+
FROM information_schema.key_column_usage
|
109
|
+
WHERE table_schema = SCHEMA()
|
110
|
+
AND referenced_table_schema = table_schema
|
111
|
+
ORDER BY constraint_name, ordinal_position;
|
112
|
+
SQL
|
113
|
+
|
114
|
+
current_foreign_key = nil
|
115
|
+
foreign_keys = []
|
116
|
+
|
117
|
+
results.each do |row|
|
118
|
+
next if row[3] != table_name
|
119
|
+
if current_foreign_key != row[0]
|
120
|
+
foreign_keys << ForeignKeyDefinition.new(row[0], row[1], [], row[3], [])
|
121
|
+
current_foreign_key = row[0]
|
122
|
+
end
|
123
|
+
|
124
|
+
foreign_keys.last.column_names << row[2]
|
125
|
+
foreign_keys.last.references_column_names << row[4]
|
126
|
+
end
|
127
|
+
|
128
|
+
foreign_keys
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
|
2
|
+
module PostgresqlAdapter
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
alias_method_chain :indexes, :redhillonrails_core
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_table_comment(table_name, comment)
|
10
|
+
execute "COMMENT ON TABLE #{table_name} IS '#{quote_string(comment)}'"
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear_table_comment(table_name)
|
14
|
+
execute "COMMENT ON TABLE #{table_name} IS NULL"
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_index(table_name, column_name, options = {})
|
18
|
+
column_names = Array(column_name)
|
19
|
+
index_name = index_name(table_name, :column => column_names)
|
20
|
+
|
21
|
+
if Hash === options # legacy support, since this param was a string
|
22
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
23
|
+
index_name = options[:name] || index_name
|
24
|
+
else
|
25
|
+
index_type = options
|
26
|
+
end
|
27
|
+
|
28
|
+
quoted_column_names = column_names.map { |e| options[:case_sensitive] == false && e.to_s !~ /_id$/ ? "LOWER(#{quote_column_name(e)})" : quote_column_name(e) }
|
29
|
+
|
30
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{table_name} (#{quoted_column_names.join(", ")})"
|
31
|
+
end
|
32
|
+
|
33
|
+
def indexes_with_redhillonrails_core(table_name, name = nil)
|
34
|
+
indexes = indexes_without_redhillonrails_core(table_name, name)
|
35
|
+
result = query(<<-SQL, name)
|
36
|
+
SELECT c2.relname, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
|
37
|
+
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
|
38
|
+
WHERE c.relname = '#{table_name}'
|
39
|
+
AND c.oid = i.indrelid AND i.indexrelid = c2.oid
|
40
|
+
AND i.indisprimary = 'f'
|
41
|
+
AND i.indexprs IS NOT NULL
|
42
|
+
ORDER BY 1
|
43
|
+
SQL
|
44
|
+
|
45
|
+
result.each do |row|
|
46
|
+
if row[2]=~ /\((.*LOWER\([^:]+(::text)?\).*)\)$/i
|
47
|
+
indexes.delete_if { |index| index.name == row[0] }
|
48
|
+
column_names = $1.split(", ").map do |name|
|
49
|
+
name = $1 if name =~ /^LOWER\(([^:]+)(::text)?\)$/i
|
50
|
+
name = $1 if name =~ /^"(.*)"$/
|
51
|
+
name
|
52
|
+
end
|
53
|
+
index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", column_names)
|
54
|
+
index.case_sensitive = false
|
55
|
+
indexes << index
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
indexes
|
60
|
+
end
|
61
|
+
|
62
|
+
def foreign_keys(table_name, name = nil)
|
63
|
+
load_foreign_keys(<<-SQL, name)
|
64
|
+
SELECT f.conname, pg_get_constraintdef(f.oid), t.relname
|
65
|
+
FROM pg_class t, pg_constraint f
|
66
|
+
WHERE f.conrelid = t.oid
|
67
|
+
AND f.contype = 'f'
|
68
|
+
AND t.relname = '#{table_name}'
|
69
|
+
SQL
|
70
|
+
end
|
71
|
+
|
72
|
+
def reverse_foreign_keys(table_name, name = nil)
|
73
|
+
load_foreign_keys(<<-SQL, name)
|
74
|
+
SELECT f.conname, pg_get_constraintdef(f.oid), t2.relname
|
75
|
+
FROM pg_class t, pg_class t2, pg_constraint f
|
76
|
+
WHERE f.confrelid = t.oid
|
77
|
+
AND f.conrelid = t2.oid
|
78
|
+
AND f.contype = 'f'
|
79
|
+
AND t.relname = '#{table_name}'
|
80
|
+
SQL
|
81
|
+
end
|
82
|
+
|
83
|
+
def views(name = nil)
|
84
|
+
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
85
|
+
query(<<-SQL, name).map { |row| row[0] }
|
86
|
+
SELECT viewname
|
87
|
+
FROM pg_views
|
88
|
+
WHERE schemaname IN (#{schemas})
|
89
|
+
SQL
|
90
|
+
end
|
91
|
+
|
92
|
+
def view_definition(view_name, name = nil)
|
93
|
+
result = query(<<-SQL, name)
|
94
|
+
SELECT pg_get_viewdef(oid)
|
95
|
+
FROM pg_class
|
96
|
+
WHERE relkind = 'v'
|
97
|
+
AND relname = '#{view_name}'
|
98
|
+
SQL
|
99
|
+
row = result.first
|
100
|
+
row.first unless row.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def load_foreign_keys(sql, name = nil)
|
106
|
+
foreign_keys = []
|
107
|
+
|
108
|
+
query(sql, name).each do |row|
|
109
|
+
if row[1] =~ /^FOREIGN KEY \((.+?)\) REFERENCES (.+?)\((.+?)\)( ON UPDATE (.+?))?( ON DELETE (.+?))?( (DEFERRABLE|NOT DEFERRABLE))?$/
|
110
|
+
name = row[0]
|
111
|
+
from_table_name = row[2]
|
112
|
+
column_names = $1
|
113
|
+
references_table_name = $2
|
114
|
+
references_column_names = $3
|
115
|
+
on_update = $5
|
116
|
+
on_delete = $7
|
117
|
+
deferrable = $9 == "DEFERRABLE"
|
118
|
+
on_update = on_update.downcase.gsub(' ', '_').to_sym if on_update
|
119
|
+
on_delete = on_delete.downcase.gsub(' ', '_').to_sym if on_delete
|
120
|
+
|
121
|
+
foreign_keys << ForeignKeyDefinition.new(name,
|
122
|
+
from_table_name, column_names.split(', '),
|
123
|
+
references_table_name.sub(/^"(.*)"$/, '\1'), references_column_names.split(', '),
|
124
|
+
on_update, on_delete, deferrable)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
foreign_keys
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|