pg_examiner 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/TODO.txt +133 -0
- data/lib/pg_examiner/result/base.rb +28 -0
- data/lib/pg_examiner/result/column.rb +27 -0
- data/lib/pg_examiner/result/constraint.rb +7 -0
- data/lib/pg_examiner/result/extension.rb +16 -0
- data/lib/pg_examiner/result/function.rb +28 -0
- data/lib/pg_examiner/result/index.rb +16 -0
- data/lib/pg_examiner/result/language.rb +7 -0
- data/lib/pg_examiner/result/schema.rb +25 -0
- data/lib/pg_examiner/result/table.rb +39 -0
- data/lib/pg_examiner/result/trigger.rb +16 -0
- data/lib/pg_examiner/result.rb +145 -0
- data/lib/pg_examiner/version.rb +3 -0
- data/lib/pg_examiner.rb +10 -0
- data/pg_examiner.gemspec +24 -0
- data/spec/constraint_spec.rb +151 -0
- data/spec/extension_spec.rb +26 -0
- data/spec/function_spec.rb +452 -0
- data/spec/index_spec.rb +164 -0
- data/spec/language_spec.rb +9 -0
- data/spec/schema_spec.rb +23 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/table_spec.rb +268 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3febec877bf8581fc25e2d473161b5c49a7342a0
|
4
|
+
data.tar.gz: cc1fb74e5cec4fa2423f57fb9c39c75e1446f31a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57219283bfba79a7768afb659f7b0c245d3eecf7277454570d9e3a70c92c9e3f34626a07732566ab33eed49dd0b58650a66da335449a62eda31cea94e38a8e5e
|
7
|
+
data.tar.gz: bbe34e55e39676b49dcbdc68f7f434ebd70c4186f11f67ac909b89de05fef68386dc5a0987e20f0885153aad236394f729873cc1860314653c65066c10f9004d
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Chris Hanks
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# PGExaminer
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'pg_examiner'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install pg_examiner
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/pg_examiner/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/TODO.txt
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
(Notes on other system tables and columns that seem useful)
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
pg_aggregate (http://www.postgresql.org/docs/9.3/static/catalog-pg-aggregate.html)
|
8
|
+
aggfnoid
|
9
|
+
aggtransfn
|
10
|
+
aggfinalfn
|
11
|
+
aggsortopaggtranstype
|
12
|
+
agginitval
|
13
|
+
|
14
|
+
pg_attribute: (http://www.postgresql.org/docs/9.3/static/catalog-pg-attribute.html)
|
15
|
+
attstorage for toast storage configuration
|
16
|
+
attinhcount: to do with inheritance?
|
17
|
+
attcollation: defined collation for a column
|
18
|
+
attoptions:
|
19
|
+
In a dropped column's pg_attribute entry, atttypid is reset to zero, but attlen and the other fields copied from pg_type are still valid. This arrangement is needed to cope with the situation where the dropped column's data type was later dropped, and so there is no pg_type row anymore. attlen and the other fields can be used to interpret the contents of a row of the table.
|
20
|
+
|
21
|
+
pg_class: (http://www.postgresql.org/docs/9.3/static/catalog-pg-class.html)
|
22
|
+
relam: index type
|
23
|
+
relkind: table/index/view/sequence/matview/composite type/...
|
24
|
+
|
25
|
+
pg_constraint:
|
26
|
+
connamespace: schema oid?
|
27
|
+
contype: check/foreign key/primary key/exclusion/unique/trigger
|
28
|
+
condeferrable:
|
29
|
+
condeferred:
|
30
|
+
contypid: constraint on domain?
|
31
|
+
conindid:
|
32
|
+
confrelid: which table a foreign key is on
|
33
|
+
confmatchtype: MATCH TYPE
|
34
|
+
conkey
|
35
|
+
confkey
|
36
|
+
conexclop
|
37
|
+
|
38
|
+
pg_description (http://www.postgresql.org/docs/9.3/static/catalog-pg-description.html)
|
39
|
+
objoid
|
40
|
+
classoid
|
41
|
+
objsubid
|
42
|
+
description
|
43
|
+
|
44
|
+
pg_enum (http://www.postgresql.org/docs/9.3/static/catalog-pg-enum.html)
|
45
|
+
enumtypid
|
46
|
+
enumsortorder (don't care about exact values, just order)
|
47
|
+
enumlabel
|
48
|
+
|
49
|
+
pg_event_trigger (http://www.postgresql.org/docs/9.3/static/catalog-pg-event-trigger.html)
|
50
|
+
evtname
|
51
|
+
evtevent
|
52
|
+
evtfoid
|
53
|
+
|
54
|
+
pg_index (http://www.postgresql.org/docs/9.3/static/catalog-pg-index.html)
|
55
|
+
indisexclusion
|
56
|
+
indimmediate
|
57
|
+
indisclustered
|
58
|
+
indisvalid?
|
59
|
+
indisready?
|
60
|
+
indislive?
|
61
|
+
indnatts?
|
62
|
+
|
63
|
+
pg_inherits (http://www.postgresql.org/docs/9.3/static/catalog-pg-inherits.html)
|
64
|
+
inhrelid
|
65
|
+
inhparent
|
66
|
+
inhseqno
|
67
|
+
|
68
|
+
pg_language (http://www.postgresql.org/docs/9.3/static/catalog-pg-language.html)
|
69
|
+
lanname
|
70
|
+
|
71
|
+
pg_proc (http://www.postgresql.org/docs/9.3/static/catalog-pg-proc.html)
|
72
|
+
proname
|
73
|
+
pronamespace
|
74
|
+
procost
|
75
|
+
prorows?
|
76
|
+
provariadic
|
77
|
+
proisagg
|
78
|
+
proiswindow
|
79
|
+
proleakproof?
|
80
|
+
proisstrict
|
81
|
+
proretset
|
82
|
+
provolatile
|
83
|
+
pronargs
|
84
|
+
pronargdefaults
|
85
|
+
prorettype
|
86
|
+
proargtypes
|
87
|
+
proallargtypes
|
88
|
+
proargmodes
|
89
|
+
proargnames
|
90
|
+
nodeToString(proargdefaults)
|
91
|
+
|
92
|
+
pg_trigger (http://www.postgresql.org/docs/9.3/static/catalog-pg-trigger.html)
|
93
|
+
tgrelid
|
94
|
+
tgname
|
95
|
+
tgfoid
|
96
|
+
tgtype
|
97
|
+
tgisinternal
|
98
|
+
tgconstrrelid
|
99
|
+
togconstrindid
|
100
|
+
tgconstraint
|
101
|
+
tgdeferrable
|
102
|
+
tginitdeferred
|
103
|
+
tgnargs
|
104
|
+
tgattr
|
105
|
+
tgargs
|
106
|
+
tgqual (nodeToString())
|
107
|
+
|
108
|
+
pg_type (http://www.postgresql.org/docs/9.3/static/catalog-pg-type.html)
|
109
|
+
typname
|
110
|
+
typnamespace
|
111
|
+
typlen
|
112
|
+
typbyval
|
113
|
+
typtype
|
114
|
+
typdelim
|
115
|
+
typrelid
|
116
|
+
typelem
|
117
|
+
typarray
|
118
|
+
typalign
|
119
|
+
typstorage
|
120
|
+
typnotnull
|
121
|
+
typbasetype
|
122
|
+
typndims
|
123
|
+
typdefault
|
124
|
+
|
125
|
+
pg_views (http://www.postgresql.org/docs/9.3/static/view-pg-views.html)
|
126
|
+
schemaname
|
127
|
+
viewname
|
128
|
+
definition? or can get all this from pg_class?
|
129
|
+
|
130
|
+
pg_matviews (http://www.postgresql.org/docs/9.3/static/view-pg-matviews.html)
|
131
|
+
schemaname
|
132
|
+
matviewname
|
133
|
+
definition
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Base
|
4
|
+
attr_reader :result, :row, :parent
|
5
|
+
|
6
|
+
def initialize(result, row, parent = nil)
|
7
|
+
@result, @row, @parent = result, row, parent
|
8
|
+
end
|
9
|
+
|
10
|
+
def oid
|
11
|
+
@row['oid']
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
@row['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
columns = self.class::COMPARISON_COLUMNS
|
20
|
+
self.class == other.class && row.values_at(*columns) == other.row.values_at(*columns)
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class} @row=#{@row.inspect}>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Column < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name attndims attnotnull atttypmod)
|
5
|
+
|
6
|
+
def type
|
7
|
+
@type ||= result.pg_type.find{|t| t['oid'] == row['atttypid']}['name']
|
8
|
+
end
|
9
|
+
|
10
|
+
def default
|
11
|
+
# Have to dance a bit so that the lack of a default becomes nil, but isn't recalculated each time.
|
12
|
+
if @default_calculated
|
13
|
+
@default
|
14
|
+
else
|
15
|
+
@default_calculated = true
|
16
|
+
@default = result.pg_attrdef.find{|d| d['adrelid'] == row['attrelid']}['default'] if row['atthasdef'] == 't'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
super &&
|
22
|
+
type == other.type &&
|
23
|
+
default == other.default
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Extension < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name extversion)
|
5
|
+
|
6
|
+
def schema
|
7
|
+
@schema ||= result.schemas.find { |s| s.oid == row['extnamespace'] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
super &&
|
12
|
+
(schema && schema.name) == (other.schema && other.schema.name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Function < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name proargmodes definition)
|
5
|
+
|
6
|
+
def argument_types
|
7
|
+
@argument_types ||= @row['proargtypes'].split.map do |oid|
|
8
|
+
result.pg_type.find{|t| t['oid'] == oid}['name']
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def return_type
|
13
|
+
@return_type ||= result.pg_type.find{|t| t['oid'] == @row['prorettype']}['name']
|
14
|
+
end
|
15
|
+
|
16
|
+
def language
|
17
|
+
@language ||= result.pg_language.find{|l| l['oid'] == @row['prolang']}['name']
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
super &&
|
22
|
+
argument_types == other.argument_types &&
|
23
|
+
return_type == other.return_type &&
|
24
|
+
language == other.language
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Index < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name filter indisunique indisprimary)
|
5
|
+
|
6
|
+
def expression
|
7
|
+
@row['expression'] || @row['indkey'].split.map{|i| parent.columns.find{|c| c.row['attnum'] == i}}.map(&:name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
super &&
|
12
|
+
expression == other.expression
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Schema < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name)
|
5
|
+
|
6
|
+
def tables
|
7
|
+
@tables ||= result.pg_class.select do |c|
|
8
|
+
c['relnamespace'] == oid && c['relkind'] == 'r'
|
9
|
+
end.map{|row| Table.new(result, row, self)}.sort_by(&:name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def functions
|
13
|
+
@functions ||= result.pg_proc.select do |c|
|
14
|
+
c['pronamespace'] == oid
|
15
|
+
end.map{|row| Function.new(result, row, self)}.sort_by(&:name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
super &&
|
20
|
+
tables == other.tables &&
|
21
|
+
functions == other.functions
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Table < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name relpersistence reloptions)
|
5
|
+
|
6
|
+
def columns
|
7
|
+
@columns ||= result.pg_attribute.select do |c|
|
8
|
+
c['attrelid'] == oid
|
9
|
+
end.sort_by{|c| c['attnum'].to_i}.map { |row| Column.new(result, row, self) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def indexes
|
13
|
+
@indexes ||= result.pg_index.select do |c|
|
14
|
+
c['indrelid'] == oid
|
15
|
+
end.map{|row| Index.new(result, row, self)}.sort_by(&:name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def constraints
|
19
|
+
@constraints ||= result.pg_constraint.select do |c|
|
20
|
+
c['conrelid'] == oid
|
21
|
+
end.map{|row| Constraint.new(result, row, self)}.sort_by(&:name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def triggers
|
25
|
+
@triggers ||= result.pg_trigger.select do |t|
|
26
|
+
t['tgrelid'] == oid
|
27
|
+
end.map{|row| Trigger.new(result, row, self)}.sort_by(&:name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
super &&
|
32
|
+
columns == other.columns &&
|
33
|
+
indexes == other.indexes &&
|
34
|
+
constraints == other.constraints &&
|
35
|
+
triggers == other.triggers
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PGExaminer
|
2
|
+
class Result
|
3
|
+
class Trigger < Base
|
4
|
+
COMPARISON_COLUMNS = %w(name tgtype)
|
5
|
+
|
6
|
+
def function
|
7
|
+
@function ||= result.pg_proc.find{|f| f['oid'] == @row['tgfoid']}['name']
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
super &&
|
12
|
+
function == other.function
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'pg_examiner/result/base'
|
2
|
+
require 'pg_examiner/result/column'
|
3
|
+
require 'pg_examiner/result/constraint'
|
4
|
+
require 'pg_examiner/result/extension'
|
5
|
+
require 'pg_examiner/result/function'
|
6
|
+
require 'pg_examiner/result/index'
|
7
|
+
require 'pg_examiner/result/language'
|
8
|
+
require 'pg_examiner/result/schema'
|
9
|
+
require 'pg_examiner/result/table'
|
10
|
+
require 'pg_examiner/result/trigger'
|
11
|
+
|
12
|
+
module PGExaminer
|
13
|
+
class Result
|
14
|
+
attr_reader :pg_namespace,
|
15
|
+
:pg_class,
|
16
|
+
:pg_type,
|
17
|
+
:pg_index,
|
18
|
+
:pg_attrdef,
|
19
|
+
:pg_attribute,
|
20
|
+
:pg_extension,
|
21
|
+
:pg_constraint,
|
22
|
+
:pg_proc,
|
23
|
+
:pg_language,
|
24
|
+
:pg_trigger
|
25
|
+
|
26
|
+
def initialize(connection)
|
27
|
+
@conn = connection
|
28
|
+
load_schema
|
29
|
+
end
|
30
|
+
|
31
|
+
def schemas
|
32
|
+
@schemas ||= @pg_namespace.map{|row| Schema.new(self, row)}.sort_by(&:name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def extensions
|
36
|
+
@extensions ||= @pg_extension.map{|row| Extension.new(self, row)}.sort_by(&:name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def languages
|
40
|
+
@languages ||= @pg_language.map{|row| Language.new(self, row)}.sort_by(&:name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(other)
|
44
|
+
other.is_a?(Result) &&
|
45
|
+
schemas == other.schemas &&
|
46
|
+
extensions == other.extensions &&
|
47
|
+
languages == other.languages
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
"#<#{self.class} @schemas=#{@schemas.inspect}, @extensions=#{@extensions.inspect}>"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def execute(*args)
|
57
|
+
@conn.async_exec(*args).to_a
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_schema
|
61
|
+
# Get all relevant schemas/namespaces, which includes public but not
|
62
|
+
# information_schema or system schemas, which are prefixed with pg_. It
|
63
|
+
# wouldn't be a good practice for anyone to name a custom schema
|
64
|
+
# starting with pg_ anyway.
|
65
|
+
@pg_namespace = execute <<-SQL
|
66
|
+
SELECT oid, nspname AS name
|
67
|
+
FROM pg_namespace
|
68
|
+
WHERE nspname != 'information_schema'
|
69
|
+
AND nspname NOT LIKE 'pg_%'
|
70
|
+
SQL
|
71
|
+
|
72
|
+
@pg_class = load_table @pg_namespace.map{|ns| ns['oid']}, <<-SQL
|
73
|
+
SELECT oid, relname AS name, relkind, relpersistence, reloptions, relnamespace
|
74
|
+
FROM pg_class
|
75
|
+
WHERE relnamespace IN (?)
|
76
|
+
SQL
|
77
|
+
|
78
|
+
@pg_attribute = load_table @pg_class.map{|ns| ns['oid']}, <<-SQL
|
79
|
+
SELECT atttypid, attname AS name, attndims, attnotnull, atttypmod, attrelid, atthasdef, attnum
|
80
|
+
FROM pg_attribute
|
81
|
+
WHERE attrelid IN (?)
|
82
|
+
AND attnum > 0 -- No system columns
|
83
|
+
AND NOT attisdropped -- Still active
|
84
|
+
SQL
|
85
|
+
|
86
|
+
@pg_type = execute <<-SQL
|
87
|
+
SELECT oid, typname AS name
|
88
|
+
FROM pg_type
|
89
|
+
SQL
|
90
|
+
|
91
|
+
@pg_index = load_table @pg_class.map{|ns| ns['oid']}, <<-SQL
|
92
|
+
SELECT c.relname AS name, i.indrelid, i.indkey, indisunique, indisprimary,
|
93
|
+
pg_get_expr(i.indpred, i.indexrelid) AS filter,
|
94
|
+
pg_get_expr(i.indexprs, i.indrelid) AS expression
|
95
|
+
FROM pg_index i
|
96
|
+
JOIN pg_class c ON c.oid = i.indexrelid
|
97
|
+
WHERE c.oid IN (?)
|
98
|
+
SQL
|
99
|
+
|
100
|
+
@pg_constraint = load_table @pg_class.map{|ns| ns['oid']}, <<-SQL
|
101
|
+
SELECT oid, conname AS name, conrelid,
|
102
|
+
pg_get_constraintdef(oid) AS definition
|
103
|
+
FROM pg_constraint c
|
104
|
+
WHERE conrelid IN (?)
|
105
|
+
SQL
|
106
|
+
|
107
|
+
@pg_trigger = load_table @pg_class.map{|ns| ns['oid']}, <<-SQL
|
108
|
+
SELECT oid, tgname AS name, tgrelid, tgtype, tgfoid
|
109
|
+
FROM pg_trigger
|
110
|
+
WHERE tgrelid IN (?)
|
111
|
+
AND tgconstrrelid = '0' -- Ignore foreign key triggers, which have unpredictable names.
|
112
|
+
SQL
|
113
|
+
|
114
|
+
@pg_attrdef = execute <<-SQL
|
115
|
+
SELECT oid, adrelid, pg_get_expr(adbin, adrelid) AS default
|
116
|
+
FROM pg_attrdef
|
117
|
+
SQL
|
118
|
+
|
119
|
+
@pg_proc = load_table @pg_namespace.map{|ns| ns['oid']}, <<-SQL
|
120
|
+
SELECT oid, proname AS name, pronamespace, proargtypes, prorettype, proargmodes, prolang, pg_get_functiondef(oid) AS definition
|
121
|
+
FROM pg_proc
|
122
|
+
WHERE pronamespace IN (?)
|
123
|
+
AND NOT proisagg -- prevent pg_get_functiondef() from throwing errors on aggregate functions.
|
124
|
+
SQL
|
125
|
+
|
126
|
+
@pg_extension = execute <<-SQL
|
127
|
+
SELECT extname AS name, extnamespace, extversion
|
128
|
+
FROM pg_extension
|
129
|
+
SQL
|
130
|
+
|
131
|
+
@pg_language = execute <<-SQL
|
132
|
+
SELECT oid, lanname AS name
|
133
|
+
FROM pg_language
|
134
|
+
SQL
|
135
|
+
end
|
136
|
+
|
137
|
+
def load_table(oids, sql)
|
138
|
+
if oids.any?
|
139
|
+
execute sql.gsub(/\?/, oids.map{|oid| "'#{oid}'"}.join(', '))
|
140
|
+
else
|
141
|
+
[]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/pg_examiner.rb
ADDED
data/pg_examiner.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pg_examiner/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'pg_examiner'
|
8
|
+
spec.version = PGExaminer::VERSION
|
9
|
+
spec.authors = ["Chris Hanks"]
|
10
|
+
spec.email = ["christopher.m.hanks@gmail.com"]
|
11
|
+
spec.summary = %q{Parse the schemas of Postgres databases in detail}
|
12
|
+
spec.description = %q{Examine and compare the tables, columns, constraints and other information that makes up the schema of a PG database}
|
13
|
+
spec.homepage = 'https://github.com/chanks/pg_examiner'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'pry'
|
24
|
+
end
|