db_leftovers 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +0 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +8 -0
- data/LICENSE.txt +0 -0
- data/README.html +16 -9
- data/README.md +13 -8
- data/Rakefile +0 -0
- data/TODO +0 -7
- data/VERSION +1 -1
- data/db_leftovers.gemspec +12 -4
- data/lib/db_leftovers.rb +3 -1
- data/lib/db_leftovers/constraint.rb +0 -0
- data/lib/db_leftovers/definition.rb +5 -2
- data/lib/db_leftovers/dsl.rb +13 -1
- data/lib/db_leftovers/foreign_key.rb +0 -0
- data/lib/db_leftovers/generic_database_interface.rb +67 -0
- data/lib/db_leftovers/index.rb +0 -0
- data/lib/db_leftovers/mysql_database_interface.rb +83 -0
- data/lib/db_leftovers/{database_interface.rb → postgres_database_interface.rb} +15 -58
- data/lib/db_leftovers/table_dsl.rb +0 -0
- data/lib/tasks/leftovers.rake +0 -0
- data/spec/config/database.yml.sample +27 -0
- data/spec/db_leftovers_spec.rb +112 -178
- data/spec/mysql_spec.rb +32 -0
- data/spec/postgres_spec.rb +103 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/mock_database_interface.rb +59 -0
- data/spec/support/shared_db_tests.rb +152 -0
- data/spec/support/sql_matcher.rb +11 -0
- metadata +12 -4
data/.document
CHANGED
File without changes
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -24,6 +24,10 @@ GEM
|
|
24
24
|
activesupport (= 3.1.3)
|
25
25
|
arel (~> 2.2.1)
|
26
26
|
tzinfo (~> 0.3.29)
|
27
|
+
activerecord-mysql2-adapter (0.0.3)
|
28
|
+
mysql2
|
29
|
+
activerecord-postgresql-adapter (0.0.1)
|
30
|
+
pg
|
27
31
|
activeresource (3.1.3)
|
28
32
|
activemodel (= 3.1.3)
|
29
33
|
activesupport (= 3.1.3)
|
@@ -47,6 +51,8 @@ GEM
|
|
47
51
|
treetop (~> 1.4.8)
|
48
52
|
mime-types (1.17.2)
|
49
53
|
multi_json (1.0.4)
|
54
|
+
mysql2 (0.3.11)
|
55
|
+
pg (0.14.1)
|
50
56
|
polyglot (0.3.3)
|
51
57
|
rack (1.3.6)
|
52
58
|
rack-cache (1.1)
|
@@ -99,6 +105,8 @@ PLATFORMS
|
|
99
105
|
ruby
|
100
106
|
|
101
107
|
DEPENDENCIES
|
108
|
+
activerecord-mysql2-adapter
|
109
|
+
activerecord-postgresql-adapter
|
102
110
|
bundler
|
103
111
|
jeweler (~> 1.6.4)
|
104
112
|
rails (>= 3.0.0)
|
data/LICENSE.txt
CHANGED
File without changes
|
data/README.html
CHANGED
@@ -3,12 +3,19 @@
|
|
3
3
|
<p>db_leftovers lets you define indexes, foreign keys, and CHECK constraints for your Rails app
|
4
4
|
in one place using an easy-to-read DSL,
|
5
5
|
then run a rake task to bring your database up-to-date.
|
6
|
-
|
7
|
-
This
|
8
|
-
But now that it's written, I'm finding it useful on non-Heroku projects as well.</p>
|
6
|
+
Whenever you edit the DSL, you can re-run the rake task and db_leftovers will alter your database accorindgly.
|
7
|
+
This is useful because of the following limitations in vanilla Rails:</p>
|
9
8
|
|
10
|
-
<
|
11
|
-
|
9
|
+
<ul>
|
10
|
+
<li>There are no built-in migration methods to create foreign keys or CHECK constraints.</li>
|
11
|
+
<li>Even if created, foreign keys and CHECK constraints won't appear in your schema.rb.</li>
|
12
|
+
<li>If you're using Heroku, <code>db:push</code> and <code>db:pull</code> won't transfer your foreign keys and CHECK constraints.</li>
|
13
|
+
<li>Creating indexes in your migrations makes it hard to manage them.</li>
|
14
|
+
</ul>
|
15
|
+
|
16
|
+
<p>That last point deserves some elaboration. Using <code>create_index</code> in your migrations is bug-prone because without rare developer discipline (My rule is "never change a migration after a <code>git push</code>, but I haven't seen this followed elsewhere."), you wind up missing indexes in some environments. It also means you don't have a central place to see all your indexes so you can analyze which are needed. With db_leftovers, you can rest assured that each environment conforms to a definition that is easy to read and checked into version control.</p>
|
17
|
+
|
18
|
+
<p>At present db_leftovers supports PostgreSQL and MySQL, although since MySQL doesn't support index WHERE clauses or CHECK constraints, using that functionality will raise errors. (If you need to share the same definitions across Postgres and MySQL, you can run arbitrary Ruby code inside the DSL to avoid defining unsupported objects when run against MySQL.)</p>
|
12
19
|
|
13
20
|
<h2>Configuration File</h2>
|
14
21
|
|
@@ -70,7 +77,7 @@ All parameters are strings or symbols.</p>
|
|
70
77
|
|
71
78
|
<h3>table(table_name, &block)</h3>
|
72
79
|
|
73
|
-
<p>The <code>table</code> call is just a convenience so you can group all a table's indexes
|
80
|
+
<p>The <code>table</code> call is just a convenience so you can group all a table's indexes et cetera together and not keep repeating the table name. You use it like this:</p>
|
74
81
|
|
75
82
|
<pre><code>table :books do
|
76
83
|
index :author_id
|
@@ -104,7 +111,8 @@ The second time you run db_leftovers, it will read the expression from Postgres
|
|
104
111
|
and so it will drop and re-create the constraint.
|
105
112
|
It will drop and re-create it every time you run the rake task.
|
106
113
|
To get around this, make sure your config file uses the same expression as printed by db_leftovers in the rake output.
|
107
|
-
This can also happen for index WHERE clauses, fixable by a similar workaround
|
114
|
+
This can also happen for index WHERE clauses, fixable by a similar workaround.
|
115
|
+
MySQL doesn't have this problem because it doesn't support CHECK constraints or index WHERE clauses.</p>
|
108
116
|
|
109
117
|
<p>To print messages even about indexes/foreign keys/constraints that haven't changed, you can say:</p>
|
110
118
|
|
@@ -119,8 +127,7 @@ This can also happen for index WHERE clauses, fixable by a similar workaround.</
|
|
119
127
|
<h2>Known Issues</h2>
|
120
128
|
|
121
129
|
<ul>
|
122
|
-
<li>db_leftovers
|
123
|
-
If you want to add support for something else, just send me a pull request!</li>
|
130
|
+
<li>When db_leftovers interrogates your database for the currently-defined indexes et cetera, it doesn't filter things by the current database name. So if you have mutliple Rails projects all accessible to the same user, you'll wind up changing more than you like (probably by DROPing things).</li>
|
124
131
|
</ul>
|
125
132
|
|
126
133
|
<h2>Contributing to db_leftovers</h2>
|
data/README.md
CHANGED
@@ -4,12 +4,17 @@ db\_leftovers
|
|
4
4
|
db\_leftovers lets you define indexes, foreign keys, and CHECK constraints for your Rails app
|
5
5
|
in one place using an easy-to-read DSL,
|
6
6
|
then run a rake task to bring your database up-to-date.
|
7
|
-
|
8
|
-
This
|
9
|
-
But now that it's written, I'm finding it useful on non-Heroku projects as well.
|
7
|
+
Whenever you edit the DSL, you can re-run the rake task and db\_leftovers will alter your database accorindgly.
|
8
|
+
This is useful because of the following limitations in vanilla Rails:
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
* There are no built-in migration methods to create foreign keys or CHECK constraints.
|
11
|
+
* Even if created, foreign keys and CHECK constraints won't appear in your schema.rb.
|
12
|
+
* If you're using Heroku, `db:push` and `db:pull` won't transfer your foreign keys and CHECK constraints.
|
13
|
+
* Creating indexes in your migrations makes it hard to manage them.
|
14
|
+
|
15
|
+
That last point deserves some elaboration. Using `create_index` in your migrations is bug-prone because without rare developer discipline (My rule is "never change a migration after a `git push`, but I haven't seen this followed elsewhere."), you wind up missing indexes in some environments. It also means you don't have a central place to see all your indexes so you can analyze which are needed. With db\_leftovers, you can rest assured that each environment conforms to a definition that is easy to read and checked into version control.
|
16
|
+
|
17
|
+
At present db\_leftovers supports PostgreSQL and MySQL, although since MySQL doesn't support index WHERE clauses or CHECK constraints, using that functionality will raise errors. (If you need to share the same definitions across Postgres and MySQL, you can run arbitrary Ruby code inside the DSL to avoid defining unsupported objects when run against MySQL.)
|
13
18
|
|
14
19
|
Configuration File
|
15
20
|
------------------
|
@@ -66,7 +71,7 @@ All parameters are strings or symbols.
|
|
66
71
|
|
67
72
|
### table(table\_name, &block)
|
68
73
|
|
69
|
-
The `table` call is just a convenience so you can group all a table's indexes
|
74
|
+
The `table` call is just a convenience so you can group all a table's indexes et cetera together and not keep repeating the table name. You use it like this:
|
70
75
|
|
71
76
|
table :books do
|
72
77
|
index :author_id
|
@@ -100,6 +105,7 @@ and so it will drop and re-create the constraint.
|
|
100
105
|
It will drop and re-create it every time you run the rake task.
|
101
106
|
To get around this, make sure your config file uses the same expression as printed by db\_leftovers in the rake output.
|
102
107
|
This can also happen for index WHERE clauses, fixable by a similar workaround.
|
108
|
+
MySQL doesn't have this problem because it doesn't support CHECK constraints or index WHERE clauses.
|
103
109
|
|
104
110
|
To print messages even about indexes/foreign keys/constraints that haven't changed, you can say:
|
105
111
|
|
@@ -115,8 +121,7 @@ or
|
|
115
121
|
Known Issues
|
116
122
|
------------
|
117
123
|
|
118
|
-
* db\_leftovers
|
119
|
-
If you want to add support for something else, just send me a pull request!
|
124
|
+
* When db\_leftovers interrogates your database for the currently-defined indexes et cetera, it doesn't filter things by the current database name. So if you have mutliple Rails projects all accessible to the same user, you'll wind up changing more than you like (probably by DROPing things).
|
120
125
|
|
121
126
|
|
122
127
|
|
data/Rakefile
CHANGED
File without changes
|
data/TODO
CHANGED
@@ -1,9 +1,2 @@
|
|
1
|
-
- Refactor DatabaseInterface so all methods are instance methods and Definition/DSL uses an instance, set according to the Rails database adapter and also settable to a test subclass, so we can change the definition from test to test.
|
2
|
-
|
3
|
-
- Add optional tests that use a real Postgres/MySQL/SQLite if available.
|
4
|
-
- Run all the same tests, but against a real DatabaseInterface instance.
|
5
|
-
|
6
1
|
- Make sure everything works if the current db user has access to multiple databases: only run the code on the db given in the Rails database.yml.
|
7
2
|
|
8
|
-
- Support MySQL
|
9
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/db_leftovers.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "db_leftovers"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Paul A. Jungwirth"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-10-02"
|
13
13
|
s.description = " Define indexes and foreign keys for your Rails app\n in one place using an easy-to-read DSL,\n then run a rake task to bring your database up-to-date.\n"
|
14
14
|
s.email = "pj@illuminatedcomputing.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -30,15 +30,23 @@ Gem::Specification.new do |s|
|
|
30
30
|
"db_leftovers.gemspec",
|
31
31
|
"lib/db_leftovers.rb",
|
32
32
|
"lib/db_leftovers/constraint.rb",
|
33
|
-
"lib/db_leftovers/database_interface.rb",
|
34
33
|
"lib/db_leftovers/definition.rb",
|
35
34
|
"lib/db_leftovers/dsl.rb",
|
36
35
|
"lib/db_leftovers/foreign_key.rb",
|
36
|
+
"lib/db_leftovers/generic_database_interface.rb",
|
37
37
|
"lib/db_leftovers/index.rb",
|
38
|
+
"lib/db_leftovers/mysql_database_interface.rb",
|
39
|
+
"lib/db_leftovers/postgres_database_interface.rb",
|
38
40
|
"lib/db_leftovers/table_dsl.rb",
|
39
41
|
"lib/tasks/leftovers.rake",
|
42
|
+
"spec/config/database.yml.sample",
|
40
43
|
"spec/db_leftovers_spec.rb",
|
41
|
-
"spec/
|
44
|
+
"spec/mysql_spec.rb",
|
45
|
+
"spec/postgres_spec.rb",
|
46
|
+
"spec/spec_helper.rb",
|
47
|
+
"spec/support/mock_database_interface.rb",
|
48
|
+
"spec/support/shared_db_tests.rb",
|
49
|
+
"spec/support/sql_matcher.rb"
|
42
50
|
]
|
43
51
|
s.homepage = "http://github.com/pjungwir/db_leftovers"
|
44
52
|
s.licenses = ["MIT"]
|
data/lib/db_leftovers.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
require 'db_leftovers/
|
1
|
+
require 'db_leftovers/generic_database_interface.rb'
|
2
|
+
require 'db_leftovers/postgres_database_interface.rb'
|
3
|
+
require 'db_leftovers/mysql_database_interface.rb'
|
2
4
|
require 'db_leftovers/index.rb'
|
3
5
|
require 'db_leftovers/foreign_key.rb'
|
4
6
|
require 'db_leftovers/constraint.rb'
|
File without changes
|
@@ -5,9 +5,12 @@ module DBLeftovers
|
|
5
5
|
opts = {
|
6
6
|
:do_indexes => true,
|
7
7
|
:do_foreign_keys => true,
|
8
|
-
:do_constraints => true
|
8
|
+
:do_constraints => true,
|
9
|
+
:db_interface => nil
|
9
10
|
}.merge(opts)
|
10
|
-
dsl = DSL.new(
|
11
|
+
dsl = DSL.new(
|
12
|
+
:verbose => ENV['DB_LEFTOVERS_VERBOSE'] || false,
|
13
|
+
:db_interface => opts[:db_interface])
|
11
14
|
dsl.define(&block)
|
12
15
|
dsl.record_indexes if opts[:do_indexes]
|
13
16
|
dsl.record_foreign_keys if opts[:do_foreign_keys]
|
data/lib/db_leftovers/dsl.rb
CHANGED
@@ -8,7 +8,7 @@ module DBLeftovers
|
|
8
8
|
|
9
9
|
def initialize(opts={})
|
10
10
|
@verbose = !!opts[:verbose]
|
11
|
-
@db =
|
11
|
+
@db = opts[:db_interface] || get_database_interface
|
12
12
|
|
13
13
|
@indexes_by_table = {} # Set from the DSL
|
14
14
|
@old_indexes = @db.lookup_all_indexes
|
@@ -226,6 +226,18 @@ module DBLeftovers
|
|
226
226
|
"fk_#{from_table}_#{from_column}"
|
227
227
|
end
|
228
228
|
|
229
|
+
def get_database_interface
|
230
|
+
db = ActiveRecord::Base.configurations[Rails.env]['adapter']
|
231
|
+
case db
|
232
|
+
when 'postgresql'
|
233
|
+
DBLeftovers::PostgresDatabaseInterface.new
|
234
|
+
when 'mysql2'
|
235
|
+
DBLeftovers::MySQLInterface.new
|
236
|
+
else
|
237
|
+
raise "Unsupported database: #{db}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
229
241
|
end
|
230
242
|
|
231
243
|
end
|
File without changes
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module DBLeftovers
|
2
|
+
|
3
|
+
class GenericDatabaseInterface
|
4
|
+
|
5
|
+
def lookup_all_indexes
|
6
|
+
raise "Should be overriden by a database-specific interface"
|
7
|
+
end
|
8
|
+
|
9
|
+
def lookup_all_foreign_keys
|
10
|
+
raise "Should be overriden by a database-specific interface"
|
11
|
+
end
|
12
|
+
|
13
|
+
def lookup_all_constraints
|
14
|
+
raise "Should be overriden by a database-specific interface"
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute_add_index(idx)
|
18
|
+
unique = idx.unique? ? 'UNIQUE' : ''
|
19
|
+
where = idx.where_clause.present? ? "WHERE #{idx.where_clause}" : ''
|
20
|
+
|
21
|
+
sql = <<-EOQ
|
22
|
+
CREATE #{unique} INDEX #{idx.index_name}
|
23
|
+
ON #{idx.table_name}
|
24
|
+
(#{idx.column_names.join(', ')})
|
25
|
+
#{where}
|
26
|
+
EOQ
|
27
|
+
execute_sql(sql)
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute_drop_index(table_name, index_name)
|
31
|
+
sql = <<-EOQ
|
32
|
+
DROP INDEX #{index_name}
|
33
|
+
EOQ
|
34
|
+
execute_sql(sql)
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute_add_foreign_key(fk)
|
38
|
+
on_delete = "ON DELETE CASCADE" if fk.cascade
|
39
|
+
on_delete = "ON DELETE SET NULL" if fk.set_null
|
40
|
+
execute_sql %{ALTER TABLE #{fk.from_table}
|
41
|
+
ADD CONSTRAINT #{fk.constraint_name}
|
42
|
+
FOREIGN KEY (#{fk.from_column})
|
43
|
+
REFERENCES #{fk.to_table} (#{fk.to_column})
|
44
|
+
#{on_delete}}
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute_drop_foreign_key(constraint_name, from_table, from_column)
|
48
|
+
execute_sql %{ALTER TABLE #{from_table} DROP CONSTRAINT #{constraint_name}}
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute_add_constraint(chk)
|
52
|
+
sql = <<-EOQ
|
53
|
+
ALTER TABLE #{chk.on_table} ADD CONSTRAINT #{chk.constraint_name} CHECK (#{chk.check})
|
54
|
+
EOQ
|
55
|
+
execute_sql sql
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute_drop_constraint(constraint_name, on_table)
|
59
|
+
execute_sql %{ALTER TABLE #{on_table} DROP CONSTRAINT #{constraint_name}}
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute_sql(sql)
|
63
|
+
@conn.execute(sql)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/db_leftovers/index.rb
CHANGED
File without changes
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module DBLeftovers
|
2
|
+
|
3
|
+
class MysqlDatabaseInterface < GenericDatabaseInterface
|
4
|
+
|
5
|
+
def initialize(conn=nil)
|
6
|
+
@conn = conn || ActiveRecord::Base.connection
|
7
|
+
end
|
8
|
+
|
9
|
+
def lookup_all_indexes
|
10
|
+
# TODO: Constrain it to the database for the current Rails project:
|
11
|
+
ret = {}
|
12
|
+
@conn.select_values("SHOW TABLES").each do |table_name|
|
13
|
+
indexes = {}
|
14
|
+
# Careful, MySQL automatically creates indexes whenever you define a foreign key.
|
15
|
+
# Use our foreign key naming convention to ignore these:
|
16
|
+
@conn.select_rows("SHOW INDEXES FROM #{table_name} WHERE key_name NOT LIKE 'fk_%'").each do |_, non_unique, key_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, has_nulls, index_type, comment|
|
17
|
+
unless key_name == 'PRIMARY'
|
18
|
+
# Combine rows for multi-column indexes
|
19
|
+
h = (indexes[key_name] ||= { unique: non_unique == 0, name: key_name, columns: {} })
|
20
|
+
h[:columns][seq_in_index.to_i] = column_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
indexes.each do |index_name, h|
|
25
|
+
ret[index_name] = Index.new(
|
26
|
+
table_name,
|
27
|
+
h[:columns].sort.map{|k, v| v},
|
28
|
+
unique: h[:unique],
|
29
|
+
name: h[:name]
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return ret
|
35
|
+
end
|
36
|
+
|
37
|
+
def lookup_all_foreign_keys
|
38
|
+
# TODO: Support multi-column foreign keys:
|
39
|
+
# TODO: Constrain it to the database for the current Rails project:
|
40
|
+
ret = {}
|
41
|
+
sql = <<-EOQ
|
42
|
+
SELECT c.constraint_name,
|
43
|
+
c.table_name,
|
44
|
+
k.column_name,
|
45
|
+
c.referenced_table_name,
|
46
|
+
k.referenced_column_name,
|
47
|
+
c.delete_rule
|
48
|
+
FROM information_schema.referential_constraints c,
|
49
|
+
information_schema.key_column_usage k
|
50
|
+
WHERE c.constraint_schema = k.constraint_schema
|
51
|
+
AND c.constraint_name = k.constraint_name
|
52
|
+
EOQ
|
53
|
+
@conn.select_rows(sql).each do |constr_name, from_table, from_column, to_table, to_column, del_type|
|
54
|
+
del_type = case del_type
|
55
|
+
when 'RESTRICT'; nil
|
56
|
+
when 'CASCADE'; :cascade
|
57
|
+
when 'SET NULL'; :set_null
|
58
|
+
else; raise "Unknown del type: #{del_type}"
|
59
|
+
end
|
60
|
+
ret[constr_name] = ForeignKey.new(constr_name, from_table, from_column, to_table, to_column, :on_delete => del_type)
|
61
|
+
end
|
62
|
+
return ret
|
63
|
+
end
|
64
|
+
|
65
|
+
def lookup_all_constraints
|
66
|
+
# TODO: Constrain it to the database for the current Rails project:
|
67
|
+
# MySQL doesn't support CHECK constraints:
|
68
|
+
return []
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute_drop_index(table_name, index_name)
|
72
|
+
sql = <<-EOQ
|
73
|
+
DROP INDEX #{index_name} ON #{table_name}
|
74
|
+
EOQ
|
75
|
+
execute_sql(sql)
|
76
|
+
end
|
77
|
+
|
78
|
+
def execute_drop_foreign_key(constraint_name, from_table, from_column)
|
79
|
+
execute_sql %{ALTER TABLE #{from_table} DROP FOREIGN KEY #{constraint_name}}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -1,9 +1,14 @@
|
|
1
1
|
module DBLeftovers
|
2
2
|
|
3
|
-
class
|
3
|
+
class PostgresDatabaseInterface < GenericDatabaseInterface
|
4
|
+
|
5
|
+
def initialize(conn=nil)
|
6
|
+
@conn = conn || ActiveRecord::Base.connection
|
7
|
+
end
|
4
8
|
|
5
9
|
def lookup_all_indexes
|
6
|
-
# TODO:
|
10
|
+
# TODO: Constrain it to the database for the current Rails project:
|
11
|
+
# (current_database(), current_schema())
|
7
12
|
ret = {}
|
8
13
|
sql = <<-EOQ
|
9
14
|
SELECT ix.indexrelid,
|
@@ -34,7 +39,7 @@ module DBLeftovers
|
|
34
39
|
ix.indpred
|
35
40
|
ORDER BY t.relname, i.relname
|
36
41
|
EOQ
|
37
|
-
|
42
|
+
@conn.select_rows(sql).each do |indexrelid, indrelid, table_name, index_name, is_unique, column_numbers, where_clause|
|
38
43
|
where_clause = remove_outer_parens(where_clause) if where_clause
|
39
44
|
ret[index_name] = Index.new(
|
40
45
|
table_name,
|
@@ -53,7 +58,8 @@ module DBLeftovers
|
|
53
58
|
# confdeltype: a=nil, c=cascade, n=null
|
54
59
|
ret = {}
|
55
60
|
# TODO: Support multi-column foreign keys:
|
56
|
-
# TODO:
|
61
|
+
# TODO: Constrain it to the database for the current Rails project:
|
62
|
+
# (current_database(), current_schema())
|
57
63
|
sql = <<-EOQ
|
58
64
|
SELECT c.conname,
|
59
65
|
t1.relname,
|
@@ -84,7 +90,7 @@ module DBLeftovers
|
|
84
90
|
AND pg_catalog.pg_table_is_visible(t1.oid)
|
85
91
|
AND pg_catalog.pg_table_is_visible(t2.oid)
|
86
92
|
EOQ
|
87
|
-
|
93
|
+
@conn.select_rows(sql).each do |constr_name, from_table, from_column, to_table, to_column, del_type|
|
88
94
|
del_type = case del_type
|
89
95
|
when 'a'; nil
|
90
96
|
when 'c'; :cascade
|
@@ -97,7 +103,8 @@ module DBLeftovers
|
|
97
103
|
end
|
98
104
|
|
99
105
|
def lookup_all_constraints
|
100
|
-
# TODO:
|
106
|
+
# TODO: Constrain it to the database for the current Rails project:
|
107
|
+
# (current_database(), current_schema())
|
101
108
|
ret = {}
|
102
109
|
sql = <<-EOQ
|
103
110
|
SELECT c.conname,
|
@@ -113,61 +120,12 @@ module DBLeftovers
|
|
113
120
|
AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
|
114
121
|
AND pg_catalog.pg_table_is_visible(t.oid)
|
115
122
|
EOQ
|
116
|
-
|
123
|
+
@conn.select_rows(sql).each do |constr_name, on_table, check_expr|
|
117
124
|
ret[constr_name] = Constraint.new(constr_name, on_table, remove_outer_parens(check_expr))
|
118
125
|
end
|
119
126
|
return ret
|
120
127
|
end
|
121
128
|
|
122
|
-
def execute_add_index(idx)
|
123
|
-
unique = idx.unique? ? 'UNIQUE' : ''
|
124
|
-
where = idx.where_clause.present? ? "WHERE #{idx.where_clause}" : ''
|
125
|
-
|
126
|
-
sql = <<-EOQ
|
127
|
-
CREATE #{unique} INDEX #{idx.index_name}
|
128
|
-
ON #{idx.table_name}
|
129
|
-
(#{idx.column_names.join(', ')})
|
130
|
-
#{where}
|
131
|
-
EOQ
|
132
|
-
execute_sql(sql)
|
133
|
-
end
|
134
|
-
|
135
|
-
def execute_drop_index(table_name, index_name)
|
136
|
-
sql = <<-EOQ
|
137
|
-
DROP INDEX #{index_name}
|
138
|
-
EOQ
|
139
|
-
execute_sql(sql)
|
140
|
-
end
|
141
|
-
|
142
|
-
def execute_add_foreign_key(fk)
|
143
|
-
on_delete = "ON DELETE CASCADE" if fk.cascade
|
144
|
-
on_delete = "ON DELETE SET NULL" if fk.set_null
|
145
|
-
execute_sql %{ALTER TABLE #{fk.from_table}
|
146
|
-
ADD CONSTRAINT #{fk.constraint_name}
|
147
|
-
FOREIGN KEY (#{fk.from_column})
|
148
|
-
REFERENCES #{fk.to_table} (#{fk.to_column})
|
149
|
-
#{on_delete}}
|
150
|
-
end
|
151
|
-
|
152
|
-
def execute_drop_foreign_key(constraint_name, from_table, from_column)
|
153
|
-
execute_sql %{ALTER TABLE #{from_table} DROP CONSTRAINT #{constraint_name}}
|
154
|
-
end
|
155
|
-
|
156
|
-
def execute_add_constraint(chk)
|
157
|
-
sql = <<-EOQ
|
158
|
-
ALTER TABLE #{chk.on_table} ADD CONSTRAINT #{chk.constraint_name} CHECK (#{chk.check})
|
159
|
-
EOQ
|
160
|
-
execute_sql sql
|
161
|
-
end
|
162
|
-
|
163
|
-
def execute_drop_constraint(constraint_name, on_table)
|
164
|
-
execute_sql %{ALTER TABLE #{on_table} DROP CONSTRAINT #{constraint_name}}
|
165
|
-
end
|
166
|
-
|
167
|
-
def execute_sql(sql)
|
168
|
-
ActiveRecord::Base.connection.execute(sql)
|
169
|
-
end
|
170
|
-
|
171
129
|
private
|
172
130
|
|
173
131
|
def column_names_for_index(table_id, column_numbers)
|
@@ -178,7 +136,7 @@ module DBLeftovers
|
|
178
136
|
WHERE attrelid = #{table_id}
|
179
137
|
AND attnum = #{c}
|
180
138
|
EOQ
|
181
|
-
|
139
|
+
@conn.select_value(sql)
|
182
140
|
end
|
183
141
|
end
|
184
142
|
|
@@ -186,6 +144,5 @@ module DBLeftovers
|
|
186
144
|
str ? str.gsub(/^\((.*)\)$/, '\1') : nil
|
187
145
|
end
|
188
146
|
|
189
|
-
|
190
147
|
end
|
191
148
|
end
|