pgdiff 1.0.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68b437ba9b29ffad626470871d071797eb01db9e0ef80e46036ac894cd389ee7
4
- data.tar.gz: ab86aaa142d6e7f5f8f883c249edf46054524e820c589c10c5b9066a207bbb9c
3
+ metadata.gz: 633c03ef3f939faad6c41e2714fa2ed0913a8dcae53dffb55464dd589d1819a9
4
+ data.tar.gz: ec25fe04644ce6e75de45b6445f83962c97f64f63c4eb0f5adf2821d583c1579
5
5
  SHA512:
6
- metadata.gz: dbf27f416b4aa28071de9754ad81d626ce3d3f1680b5db41bc4779042cf01fc1b51609b68be93dc0be29fc503e67d2a89b31fd96a0d26926595683795de222d5
7
- data.tar.gz: 2e6ab376273507606fa28a70e8116d9522353524129d63b4d98137aead8c9855d772a0b09dda218049db16f64aa4fa490a5ed6e0ded28292a01a96cbf8e2968f
6
+ metadata.gz: 703cd30da3b358aef33b409d83022a371f89b8810a0305dce5be657acdff03758ab4dedc5687c1b9350583ffea1440d5396c673ba2c45dc545f172e3b8d05233
7
+ data.tar.gz: 64147cd571163edd3a6ba14ca9bee9a05d1d37a44796838ba5f41700973c183dceb3c131f4753a4fe1328f78ab55a4de02cd0e092c15a05a8708605474aed051
data/CHANGELOG.rdoc CHANGED
@@ -1,7 +1,12 @@
1
1
  = Changelog
2
2
 
3
- === 1.0.1 (May 16, 2018)
4
- * Stop using deprecated PGConn class
3
+ === 2.0.1 (June 2, 2023)
4
+ * Fix incorrect quoting of system schemas
5
+
6
+ === 2.0.0 (June 1, 2023)
7
+ * Add new command line arguments
8
+ * Significantly improve performance
9
+ * Rewrite to imporove modularity
5
10
 
6
11
  === 1.0.0 (January 31, 2015)
7
12
  * Improve constraint checking
data/README.rdoc CHANGED
@@ -2,15 +2,14 @@
2
2
 
3
3
  == Overview
4
4
 
5
- This ruby gem provides a pgdiff script that compares two PostgreSQL databases and generates SQL statements to make
6
- their structures the same. The original version was posted at http://www.dzone.com/snippets/pgdiff-compare-two-postgresql.
5
+ This pgdiff gem compares two PostgreSQL databases and generates SQL statements to update the source database to match the target datbase.
7
6
 
8
7
  The script detects differences in:
9
8
 
10
- * Domains
11
9
  * Schemas
10
+ * Extensions
11
+ * Domains
12
12
  * Tables
13
- * Table field order
14
13
  * Sequences
15
14
  * Views
16
15
  * Constraints
@@ -21,29 +20,45 @@ The script detects differences in:
21
20
 
22
21
  Two objects with the same name are considered equal if they have the same definitions.
23
22
 
24
- pgdiff does not currently compare ownership, user rights, object dependencies, table inheritance, type casts, aggregates
25
- or operators. Patches are welcome to add this functionality.
23
+ pgdiff does not currently compare ownership, user rights, object dependencies, table inheritance, type casts, aggregates or operators. Patches are welcome to add this functionality.
26
24
 
27
25
  == Installation
28
26
 
29
- Install pgdiff using Ruby Gems:
27
+ Install pgdiff:
30
28
 
31
- gem install "pgdiff"
29
+ gem install pgdiff
32
30
 
33
31
  == Usage
34
32
 
35
33
  To use pgdiff open a command prompt and runn the following command:
36
34
 
37
- pg_diff "source_connection_string" "destination_connection_string"
35
+ pg_diff --source "source_connection_string" --target "destination_connection_string"
36
+
37
+ The format of the connection strings are documented in the Ruby PG gem at https://www.rubydoc.info/gems/pg/PG/Connection.new.
38
+
39
+ For eample:
40
+ "host=localhost dbname=a_database user=a_user password=a_password"
41
+
42
+ == Ignore Schemas
43
+ By default, pgdiff will not process the following system schemas:
38
44
 
39
- The format of the two connection strings is documented in the Ruby pg gem. For more information
40
- see http://www.rubydoc.info/gems/pg/PG/Connection:initialize
45
+ * pg_catalog
46
+ * pg_toast
47
+ * information_schema
48
+
49
+ You may specify additions schemas to ingore using the `--ingore-schemas` flag:
50
+
51
+ pgdiff --ignore_schemas=schema1,schema2
41
52
 
42
53
  == Output
54
+ By default, pgdiff will output results to stdout. You may also specify a file path via the `--output` command line parameter:
55
+
56
+ pgdiff --ouput=/tmp/diff.sql
57
+
58
+ You can then run this sql script against your database.
43
59
 
44
- pgdiff will output the necesssary sql statements to update the source database to have the same structure
45
- as the destination database. PLEASE VERIFY the accuracy of the generated sql statements before running them,
46
- it is always possible the script has bugs.
60
+ == Run The Script
61
+ The sql script generated by pgdiff will change your database. It is YOUR responsibility to verify the script before running it. pgdiff may have bugs, which could result in catastrophic, unrecoverable changes to your database. DO NOT RUN THIS ON A PRODUCTION DATABASE WITHOUT BACKING UP AND TESTING FIRST.
47
62
 
48
63
  == Support
49
64
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'rubygems/package_task'
5
+ require 'rake/testtask'
5
6
 
6
7
  GEM_NAME = 'pgdiff'
7
8
 
@@ -12,4 +13,13 @@ spec = Gem::Specification.load("#{GEM_NAME}.gemspec")
12
13
  Gem::PackageTask.new(spec) do |pkg|
13
14
  pkg.package_dir = 'pkg'
14
15
  pkg.need_tar = false
15
- end
16
+ end
17
+
18
+ Rake::TestTask.new do |task|
19
+ task.libs << "test"
20
+ task.test_files = FileList['test/test*.rb']
21
+ task.verbose = true
22
+ end
23
+
24
+ # Add task to create test database
25
+ load 'test/fixtures/database.rake'
data/bin/pgdiff CHANGED
@@ -13,16 +13,52 @@
13
13
  # ./pg_diff source_connection_string destination_connection_string
14
14
  #
15
15
  # The format of the connection strings is documented in the Ruby pg gem
16
- # at http://www.rubydoc.info/gems/pg/PG/Connection:initialize.
16
+ # at https://www.rubydoc.info/gems/pg/PG/Connection.new
17
+
18
+ lib_path = File.expand_path(File.join(__dir__, '..', 'lib'))
19
+ $LOAD_PATH.unshift(File.expand_path(lib_path))
17
20
 
18
21
  require 'pg'
19
22
  require 'pgdiff'
23
+ require 'optparse'
24
+ require 'ostruct'
25
+
26
+ options = OpenStruct.new
27
+ options.source = nil
28
+ options.target = nil
29
+ options.ignore_schemas = []
30
+
31
+ args = OptionParser.new do |parser|
32
+ parser.version = PgDiff::VERSION
33
+
34
+ parser.banner = "Usage: pgdiff --source --target"
35
+
36
+ parser.on("-s", "--source=connection_string", "Source connection string. See https://www.rubydoc.info/gems/pg/PG/Connection.new") do |source|
37
+ options.source = source
38
+ end
39
+
40
+ parser.on("-t", "--target=connection_string", "Target connection string. See see https://www.rubydoc.info/gems/pg/PG/Connection.new") do |target|
41
+ options.target = target
42
+ end
43
+
44
+ parser.on("--ignore_schemas=schema1,schema2", "Schemas to ignore. Comma separated string") do |ignore_schemas|
45
+ options.ignore_schemas = ignore_schemas.split(',').map {|schema_name| schema_name.strip}
46
+ end
47
+
48
+ parser.on("--output=file_path", "Save output to file. Otherwise printed to stdout") do |path|
49
+ options.path = path
50
+ end
20
51
 
21
- if ARGV.length != 2
22
- raise(ArgumentError, "You must specify two arguments - a source and target db connection string. For " +
23
- "more information about how to format connection strings see http://www.rubydoc.info/gems/pg/PG/Connection:initialize")
24
- end
52
+ parser.on("-h", "--help", "Prints this help") do
53
+ puts parser
54
+ exit
55
+ end
56
+ end.parse!
25
57
 
26
- diff = PgDiff::Diff.new(ARGV[0], ARGV[1])
58
+ output = if options.path.nil?
59
+ STDOUT
60
+ else
61
+ File.open(options.path)
62
+ end
63
+ diff = PgDiff::Diff.new(output, options.source, options.target, ignore_schemas: options.ignore_schemas)
27
64
  diff.run_compare
28
- puts diff.output
data/lib/pgdiff.rb CHANGED
@@ -1,9 +1,21 @@
1
- require 'database'
2
- require 'table'
3
- require 'attribute'
4
- require 'view'
5
- require 'sequence'
6
- require 'rule'
7
- require 'function'
8
- require 'trigger'
9
- require 'diff'
1
+ require 'set'
2
+ require 'pg'
3
+ require 'pgdiff/attribute'
4
+ require 'pgdiff/attributes'
5
+ require 'pgdiff/collation'
6
+ require 'pgdiff/constraint'
7
+ require 'pgdiff/constraints'
8
+ require 'pgdiff/database'
9
+ require 'pgdiff/diff'
10
+ require 'pgdiff/domain'
11
+ require 'pgdiff/extension'
12
+ require 'pgdiff/function'
13
+ require 'pgdiff/index'
14
+ require 'pgdiff/indexes'
15
+ require 'pgdiff/rule'
16
+ require 'pgdiff/schema'
17
+ require 'pgdiff/sequence'
18
+ require 'pgdiff/table'
19
+ require 'pgdiff/trigger'
20
+ require 'pgdiff/view'
21
+ require 'pgdiff/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgdiff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2023-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -16,19 +16,75 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.18.0
19
+ version: 0.17.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.18.0
26
+ version: 0.17.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: diff-lcs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yaml
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
27
83
  description: 'Compares two PostgreSQL databases and generates the SQL statements needed
28
84
  to make their structure the same.
29
85
 
30
- '
31
- email:
86
+ '
87
+ email:
32
88
  executables:
33
89
  - pgdiff
34
90
  extensions: []
@@ -38,21 +94,12 @@ files:
38
94
  - README.rdoc
39
95
  - Rakefile
40
96
  - bin/pgdiff
41
- - lib/attribute.rb
42
- - lib/database.rb
43
- - lib/diff.rb
44
- - lib/function.rb
45
97
  - lib/pgdiff.rb
46
- - lib/rule.rb
47
- - lib/sequence.rb
48
- - lib/table.rb
49
- - lib/trigger.rb
50
- - lib/view.rb
51
98
  homepage: https://github.com/cfis/pgdiff.git
52
99
  licenses:
53
100
  - MIT
54
101
  metadata: {}
55
- post_install_message:
102
+ post_install_message:
56
103
  rdoc_options: []
57
104
  require_paths:
58
105
  - lib
@@ -60,16 +107,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
107
  requirements:
61
108
  - - ">="
62
109
  - !ruby/object:Gem::Version
63
- version: 2.1.0
110
+ version: 1.9.3
64
111
  required_rubygems_version: !ruby/object:Gem::Requirement
65
112
  requirements:
66
113
  - - ">="
67
114
  - !ruby/object:Gem::Version
68
115
  version: '0'
69
116
  requirements: []
70
- rubyforge_project:
71
- rubygems_version: 2.7.6
72
- signing_key:
117
+ rubygems_version: 3.3.26
118
+ signing_key:
73
119
  specification_version: 4
74
120
  summary: Provides a ruby script that compares two PostgreSQL databases and generates
75
121
  the SQL statements needed to make their structure the same. The original version
data/lib/attribute.rb DELETED
@@ -1,23 +0,0 @@
1
- module PgDiff
2
- class Attribute
3
- attr_accessor :name, :type_def, :notnull, :default
4
-
5
- def initialize(name, typedef, notnull, default)
6
- @name = name
7
- @type_def = typedef
8
- @notnull = notnull
9
- @default = default
10
- end
11
-
12
- def definition
13
- out = [' ', @name, @type_def]
14
- out << 'NOT NULL' if @notnull
15
- out << 'DEFAULT ' + @default if @default
16
- out.join(" ")
17
- end
18
-
19
- def == (other)
20
- definition == other.definition
21
- end
22
- end
23
- end
data/lib/database.rb DELETED
@@ -1,112 +0,0 @@
1
- module PgDiff
2
- class Database
3
- attr_accessor :tables, :views, :sequences, :schemas, :domains, :rules, :functions, :triggers
4
-
5
- def initialize(conn)
6
- cls_query = <<-EOT
7
- SELECT n.nspname, c.relname, c.relkind
8
- FROM pg_catalog.pg_class c
9
- LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
10
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
11
- WHERE c.relkind IN ('r','S','v')
12
- AND n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
13
- ORDER BY 1,2;
14
- EOT
15
- @views = {}
16
- @tables = {}
17
- @sequences = {}
18
- @schemas = {}
19
- @domains = {}
20
- @functions = {}
21
- @rules = {}
22
- @triggers = {}
23
-
24
- conn.query(cls_query).each do |tuple|
25
- schema = tuple['nspname']
26
- relname = tuple['relname']
27
- relkind = tuple['relkind']
28
- case relkind
29
- when 'r'
30
- @tables["#{schema}.#{relname}"] = Table.new(conn, schema, relname)
31
- when 'v'
32
- @views["#{schema}.#{relname}"] = View.new(conn, schema, relname)
33
- when 'S'
34
- @sequences["#{schema}.#{relname}"] = Sequence.new(conn, schema, relname)
35
- end
36
- end
37
-
38
- domain_qry = <<-EOT
39
- SELECT n.nspname, t.typname, pg_catalog.format_type(t.typbasetype, t.typtypmod) || ' ' ||
40
- CASE WHEN t.typnotnull AND t.typdefault IS NOT NULL THEN 'not null default '||t.typdefault
41
- WHEN t.typnotnull AND t.typdefault IS NULL THEN 'not null'
42
- WHEN NOT t.typnotnull AND t.typdefault IS NOT NULL THEN 'default '|| t.typdefault
43
- ELSE ''
44
- END AS def
45
- FROM pg_catalog.pg_type t
46
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
47
- WHERE t.typtype = 'd'
48
- ORDER BY 1, 2
49
- EOT
50
- conn.query(domain_qry).each do |tuple|
51
- schema = tuple['nspname']
52
- typename = tuple['typname']
53
- value = tuple['def']
54
- @domains["#{schema}.#{typename}"] = value
55
- end
56
-
57
- schema_qry = <<-EOT
58
- select nspname from pg_namespace
59
- EOT
60
- conn.query(schema_qry).each do |tuple|
61
- schema = tuple['nspname']
62
- @schemas[schema] = schema
63
- end
64
-
65
- func_query = <<-EOT
66
- SELECT proname AS function_name
67
- , nspname AS namespace
68
- , lanname AS language_name
69
- , pg_catalog.obj_description(pg_proc.oid, 'pg_proc') AS comment
70
- , proargtypes AS function_args
71
- , proargnames AS function_arg_names
72
- , prosrc AS source_code
73
- , proretset AS returns_set
74
- , prorettype AS return_type,
75
- provolatile, proisstrict, prosecdef
76
- FROM pg_catalog.pg_proc
77
- JOIN pg_catalog.pg_language ON (pg_language.oid = prolang)
78
- JOIN pg_catalog.pg_namespace ON (pronamespace = pg_namespace.oid)
79
- JOIN pg_catalog.pg_type ON (prorettype = pg_type.oid)
80
- WHERE pg_namespace.nspname !~ 'pg_catalog|information_schema'
81
- AND proname != 'plpgsql_call_handler'
82
- AND proname != 'plpgsql_validator'
83
- EOT
84
-
85
- conn.exec(func_query).each_with_index do |tuple, i|
86
- func = Function.new(conn, tuple)
87
- @functions[func.signature] = func
88
- end
89
-
90
- rule_query = <<-EOT
91
- select schemaname || '.' || tablename || '.' || rulename as rule_name,
92
- schemaname || '.' || tablename as tab_name,
93
- rulename, definition
94
- from pg_rules
95
- where schemaname !~ 'pg_catalog|information_schema'
96
- EOT
97
- conn.exec(rule_query).each do |tuple|
98
- @rules[tuple['rule_name']] = Rule.new(tuple['tab_name'], tuple['rulename'], tuple['definition'])
99
- end
100
-
101
- trigger_query = <<-EOT
102
- select nspname || '.' || relname as tgtable, tgname, pg_get_triggerdef(t.oid) as tg_def
103
- from pg_trigger t join pg_class c ON (tgrelid = c.oid ) JOIN pg_namespace n ON (c.relnamespace = n.oid)
104
- where not tgisinternal
105
- and nspname !~ 'pg_catalog|information_schema'
106
- EOT
107
- conn.exec(trigger_query).each do |tuple|
108
- @triggers[tuple['tgtable'] + "." + tuple['tgname']] = Trigger.new(tuple['tgtable'], tuple['tgname'], tuple['tg_def'])
109
- end
110
- end
111
- end
112
- end
data/lib/diff.rb DELETED
@@ -1,345 +0,0 @@
1
- module PgDiff
2
- class Diff
3
- def initialize(old_db_spec, new_db_spec)
4
- @old_conn = PG::Connection.new(old_db_spec)
5
- @new_conn = PG::Connection.new(new_db_spec)
6
- @sections = [
7
- :domains_drop,
8
- :domains_create,
9
- :schemas_drop,
10
- :schemas_create,
11
- :tables_drop,
12
- :tables_change,
13
- :tables_create,
14
- :sequences_drop,
15
- :sequences_create,
16
- :views_drop,
17
- :views_create,
18
- :constraints_drop,
19
- :constraints_change,
20
- :constraints_create,
21
- :indices_drop,
22
- :indices_create,
23
- :functions_drop,
24
- :functions_create ,
25
- :triggers_drop,
26
- :triggers_create ,
27
- :rules_drop,
28
- :rules_create
29
- ]
30
- @script = {}
31
- @sections.each {|s| @script[s] = []}
32
- end
33
-
34
- def run_compare
35
- @old_database = Database.new(@old_conn)
36
- @new_database = Database.new(@new_conn)
37
- compare_schemas
38
- compare_domains
39
- compare_sequences
40
- compare_triggers_drop
41
- compare_rules_drop
42
- compare_views_drop
43
- compare_table_attrs
44
- compare_views_create
45
- compare_functions
46
- compare_rules_create
47
- compare_triggers_create
48
- compare_table_constraints
49
- end
50
-
51
- def add_script(section, statement)
52
- @script[section] << statement
53
- end
54
-
55
- def compare_schemas
56
- @old_database.schemas.keys.each do |name|
57
- add_script(:schemas_drop , "DROP SCHEMA #{name};") unless @new_database.schemas.has_key?(name)
58
- end
59
- @new_database.schemas.keys.each do |name|
60
- add_script(:schemas_create , "CREATE SCHEMA #{name};") unless @old_database.schemas.has_key?(name)
61
- end
62
- end
63
-
64
- def compare_domains
65
- @old_database.domains.keys.each do |name|
66
- add_script(:domains_drop , "DROP DOMAIN #{name} CASCADE;") unless @new_database.domains.has_key?(name)
67
- end
68
- @new_database.domains.each do |name, df|
69
- add_script(:domains_create , "CREATE DOMAIN #{name} AS #{df};") unless @old_database.domains.has_key?(name)
70
- old_domain = @old_database.domains[name]
71
- if old_domain && old_domain != df
72
- add_script(:domains_drop, "DROP DOMAIN #{name} CASCADE;")
73
- add_script(:domains_create, "-- [changed domain] :")
74
- add_script(:domains_create, "-- OLD: #{old_domain}")
75
- add_script(:domains_create, "CREATE DOMAIN #{name} AS #{df};")
76
- end
77
- end
78
- end
79
-
80
- def compare_sequences
81
- @old_database.sequences.keys.each do |name|
82
- add_script(:sequences_drop , "DROP SEQUENCE #{name} CASCADE;") unless @new_database.sequences.has_key?(name)
83
- end
84
- @new_database.sequences.keys.each do |name|
85
- add_script(:sequences_create , "CREATE SEQUENCE #{name};") unless @old_database.sequences.has_key?(name)
86
- end
87
- end
88
-
89
- def compare_functions
90
- @old_database.functions.keys.each do |name|
91
- add_script(:functions_drop , "DROP FUNCTION #{name} CASCADE;") unless @new_database.functions.has_key?(name)
92
- end
93
- @new_database.functions.each do |name, func|
94
- add_script(:functions_create , func.definition) unless @old_database.functions.has_key?(name)
95
- old_function = @old_database.functions[name]
96
- if old_function && old_function.definition != func.definition
97
- add_script(:functions_create , '-- [changed function] :')
98
- add_script(:functions_create , '-- OLD :')
99
- add_script(:functions_create , old_function.definition.gsub(/^/, "--> ") )
100
- add_script(:functions_create , func.definition)
101
- end
102
- end
103
- end
104
-
105
- def compare_rules_drop
106
- @old_database.rules.each do |name, rule|
107
- add_script(:rules_drop , "DROP RULE #{rule.name} ON #{rule.table_name} CASCADE;") unless @new_database.rules.has_key?(name)
108
- end
109
- end
110
-
111
- def compare_rules_create
112
- @new_database.rules.each do |name, rule|
113
- add_script(:rules_create , rule.definition) unless @old_database.rules.has_key?(name)
114
- old_rule = @old_database.rules[name]
115
- if old_rule && old_rule != rule
116
- add_script(:rules_drop , "DROP RULE #{rule.name} ON #{rule.table_name} CASCADE;")
117
- add_script(:rules_create , "-- [changed rule] :")
118
- add_script(:rules_create , "-- OLD: #{old_rule.definition}")
119
- add_script(:rules_create , rule.definition )
120
- end
121
- end
122
- end
123
-
124
- def compare_triggers_drop
125
- @old_database.triggers.each do |name, trigger|
126
- add_script(:triggers_drop , "DROP trigger #{trigger.name} ON #{trigger.table_name} CASCADE;") unless @new_database.triggers.has_key?(name)
127
- end
128
- end
129
-
130
- def compare_triggers_create
131
- @new_database.triggers.each do |name, trigger|
132
- add_script(:triggers_create , trigger.definition) unless @old_database.triggers.has_key?(name)
133
- old_trigger = @old_database.triggers[name]
134
- if old_trigger && old_trigger != trigger
135
- add_script(:triggers_drop , "DROP trigger #{trigger.name} ON #{trigger.table_name} CASCADE;")
136
- add_script(:triggers_create , "-- [changed trigger] :")
137
- add_script(:triggers_create , "-- OLD #{old_trigger.definition}")
138
- add_script(:triggers_create , trigger.definition)
139
- end
140
- end
141
- end
142
-
143
- def compare_views_drop
144
- @old_database.views.keys.each do |name|
145
- add_script(:views_drop , "DROP VIEW #{name};") unless @new_database.views.has_key?(name)
146
- end
147
- end
148
-
149
- def compare_views_create
150
- @new_database.views.each do |name, df|
151
- add_script(:views_create , df.definition) unless @old_database.views.has_key?(name)
152
- old_view = @old_database.views[name]
153
- if old_view && df.definition != old_view.definition
154
- add_script(:views_drop , "DROP VIEW #{name};")
155
- add_script(:views_create , "-- [changed view] :")
156
- add_script(:views_create , "-- #{old_view.definition.gsub(/\n/, ' ')}")
157
- add_script(:views_create , df.definition)
158
- end
159
- end
160
- end
161
-
162
- def compare_table_attrs
163
- @old_database.tables.each do |name, table|
164
- add_script(:tables_drop, "DROP TABLE #{name} CASCADE;") unless @new_database.tables.has_key?(name)
165
- end
166
- @to_compare = []
167
- @new_database.tables.each do |name, table|
168
- unless @old_database.tables.has_key?(name)
169
- add_script(:tables_create , table.table_creation)
170
- add_script(:indices_create , table.index_creation) unless table.indexes.empty?
171
- @to_compare << name
172
- else
173
- diff_attributes(@old_database.tables[name], table)
174
- diff_indexes(@old_database.tables[name], table)
175
- @to_compare << name
176
- end
177
- end
178
- end
179
-
180
- def compare_table_constraints
181
- @c_check = []
182
- @c_primary = []
183
- @c_unique = []
184
- @c_foreign = []
185
- @to_compare.each do |name|
186
- if @old_database.tables[name]
187
- diff_constraints(@old_database.tables[name], @new_database.tables[name])
188
- else
189
- @new_database.tables[name].constraints.each do |cname, cdef|
190
- add_cnstr(name, cname, cdef)
191
- end
192
- end
193
- end
194
- @script[:constraints_create] += @c_check
195
- @script[:constraints_create] += @c_primary
196
- @script[:constraints_create] += @c_unique
197
- @script[:constraints_create] += @c_foreign
198
- end
199
-
200
- def output
201
- out = []
202
- @sections.each do |sect|
203
- if @script[sect].empty?
204
- out << "-- [SKIP SECTION : #{sect.to_s.upcase}] : no changes\n"
205
- else
206
- out << "-- [START SECTION : #{sect.to_s.upcase}]"
207
- out += @script[sect]
208
- out << "-- [END SECTION : #{sect.to_s.upcase}]\n"
209
- end
210
- end
211
- out.join("\n")
212
- end
213
-
214
- def diff_attributes(old_table, new_table)
215
- dropped = []
216
- added = []
217
- changed = []
218
-
219
- order = []
220
- old_table.attributes.keys.each do |attname|
221
- if new_table.has_attribute?(attname)
222
- changed << attname if old_table.attributes[attname] != new_table.attributes[attname]
223
- else
224
- dropped << attname
225
- end
226
- end
227
-
228
- old_table.attributes.keys.each do |attname|
229
- if new_table.has_attribute?(attname)
230
- old_index = old_table.attribute_index(attname)
231
- new_index = new_table.attribute_index(attname)
232
- order << attname if old_index != new_index
233
- end
234
- end
235
- new_table.attributes.keys.each do |attname|
236
- added << attname unless old_table.has_attribute?(attname)
237
- end
238
- add_script(:tables_change , "-- [#{old_table.name}] dropped attributes") unless dropped.empty?
239
- dropped.each do |attname|
240
- add_script(:tables_change , "ALTER TABLE #{old_table.name} DROP COLUMN #{attname} CASCADE;")
241
- end
242
- add_script(:tables_change , "-- [#{old_table.name}] added attributes") unless added.empty?
243
- added.each do |attname|
244
- add_script(:tables_change , "ALTER TABLE #{old_table.name} ADD COLUMN #{new_table.attributes[attname].definition};")
245
- end
246
- add_script(:tables_change , "-- [#{old_table.name}] changed attributes") unless changed.empty?
247
- changed.each do |attname|
248
- old_att = old_table.attributes[attname]
249
- new_att = new_table.attributes[attname]
250
- add_script(:tables_change , "-- attribute: #{attname}")
251
- add_script(:tables_change , "-- OLD : #{old_att.definition}")
252
- add_script(:tables_change , "-- NEW : #{new_att.definition}")
253
- if old_att.type_def != new_att.type_def
254
- add_script(:tables_change , "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} TYPE #{new_att.type_def};")
255
- end
256
- if old_att.default != new_att.default
257
- if new_att.default.nil?
258
- add_script(:tables_change , "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} DROP DEFAULT;")
259
- else
260
- add_script(:tables_change , "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} SET DEFAULT #{new_att.default};")
261
- end
262
- end
263
- if old_att.notnull != new_att.notnull
264
- add_script(:tables_change , "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} #{new_att.notnull ? 'SET' : 'DROP'} NOT NULL;")
265
- end
266
- end
267
-
268
- add_script(:tables_change , "-- [#{old_table.name}] attribute order changed") unless order.empty?
269
- order.each do |attname|
270
- add_script(:tables_change , " #{attname}. Old index: #{old_table.attribute_index(attname)}, New index: #{new_table.attribute_index(attname)}")
271
- end
272
- end
273
-
274
- def diff_constraints(old_table, new_table)
275
- dropped = []
276
- added = []
277
- changed = []
278
-
279
- old_table.constraints.keys.each do |conname|
280
- if new_table.has_constraint?(conname)
281
- if old_table.constraints[conname] != new_table.constraints[conname]
282
- changed << conname
283
- end
284
- else
285
- dropped << conname
286
- end
287
- end
288
-
289
- new_table.constraints.keys.each do |conname|
290
- added << conname unless old_table.has_constraint?(conname)
291
- end
292
-
293
- dropped.each do |name|
294
- add_script(:constraints_drop , "ALTER TABLE #{old_table.name} DROP CONSTRAINT #{name};")
295
- end
296
-
297
- added.each do |name|
298
- add_cnstr(old_table.name, name, new_table.constraints[name])
299
- end
300
-
301
- changed.each do |name|
302
- add_script(:constraints_change,
303
- "-- Previous: #{old_table.constraints[name]}\n" +
304
- "ALTER TABLE #{old_table.name} DROP CONSTRAINT #{name};\n" +
305
- "ALTER TABLE #{new_table.name} ADD CONSTRAINT #{name} #{new_table.constraints[name]};\n")
306
- end
307
- end
308
-
309
- def add_cnstr(tablename, cnstrname, cnstrdef)
310
- c_string = "ALTER TABLE #{tablename} ADD CONSTRAINT #{cnstrname} #{cnstrdef} ;"
311
- case cnstrdef
312
- when /^CHECK / then @c_check << c_string
313
- when /^PRIMARY / then @c_primary << c_string
314
- when /^FOREIGN / then @c_foreign << c_string
315
- when /^UNIQUE / then @c_unique << c_string
316
- end
317
- end
318
-
319
- def diff_indexes(old_table, new_table)
320
- dropped = []
321
- added = []
322
-
323
- old_table.indexes.keys.each do |name|
324
- if new_table.has_index?(name)
325
- if old_table.indexes[name] != new_table.indexes[name]
326
- dropped << name
327
- added << name
328
- end
329
- else
330
- dropped << name
331
- end
332
- end
333
- new_table.indexes.keys.each do |name|
334
- added << name unless old_table.has_index?(name)
335
- end
336
-
337
- dropped.each do |name|
338
- add_script(:indices_drop , "DROP INDEX #{name};")
339
- end
340
- added.each do |name|
341
- add_script(:indices_create , (new_table.indexes[name] + ";")) if new_table.indexes[name]
342
- end
343
- end
344
- end
345
- end
data/lib/function.rb DELETED
@@ -1,56 +0,0 @@
1
- module PgDiff
2
- class Function
3
- def initialize(conn, tuple)
4
- @name = tuple['namespace'] + "." + tuple['function_name']
5
- @language = tuple['language_name']
6
- @src = tuple['source_code']
7
- @returns_set = tuple['returns_set']
8
- @return_type = format_type(conn, tuple['return_type'])
9
- @tipes = tuple['function_args'].split(" ")
10
- if tuple['function_arg_names'] && tuple['function_arg_names'] =~ /^\{(.*)\}$/
11
- @arnames = $1.split(',')
12
- elsif tuple['function_arg_names'].is_a? Array # my version of ruby-postgres
13
- @arnames = tuple['function_arg_names']
14
- else
15
- @arnames = [""] * @tipes.length
16
- end
17
- alist = []
18
- @tipes.each_with_index do |typ,idx|
19
- alist << (@arnames[idx] + " " + format_type(conn, typ))
20
- end
21
- @arglist = alist.join(" , ")
22
- @strict = tuple['proisstrict'] ? ' STRICT' : ''
23
- @secdef = tuple['prosecdef'] ? ' SECURITY DEFINER' : ''
24
- @volatile = case tuple['provolatile']
25
- when 'i' then ' IMMUTABLE'
26
- when 's' then ' STABLE'
27
- else ''
28
- end
29
- end
30
-
31
- def signature
32
- "#{@name}(#{@arglist})"
33
- end
34
-
35
- def definition
36
- <<-EOT
37
- CREATE OR REPLACE FUNCTION #{@name} (#{@arglist}) RETURNS #{@returns_set ? 'SETOF' : ''} #{@return_type} AS $_$#{@src}$_$ LANGUAGE '#{@language}' #{@volatile}#{@strict}#{@secdef};
38
- EOT
39
- end
40
-
41
- def == (other)
42
- definition == other.definition
43
- end
44
-
45
- def format_type(conn, oid)
46
- t_query = <<-EOT
47
- SELECT pg_catalog.format_type(pg_type.oid, typtypmod) AS type_name
48
- FROM pg_catalog.pg_type
49
- JOIN pg_catalog.pg_namespace ON (pg_namespace.oid = typnamespace)
50
- WHERE pg_type.oid =
51
- EOT
52
- tuple = conn.query(t_query + oid.to_s).first
53
- tuple['type_name']
54
- end
55
- end
56
- end
data/lib/rule.rb DELETED
@@ -1,15 +0,0 @@
1
- module PgDiff
2
- class Rule
3
- attr_reader :table_name, :name, :definition
4
-
5
- def initialize(table_name, name, df)
6
- @table_name = table_name
7
- @name = name
8
- @definition = df
9
- end
10
-
11
- def == (other)
12
- other.definition == definition
13
- end
14
- end
15
- end
data/lib/sequence.rb DELETED
@@ -1,11 +0,0 @@
1
- module PgDiff
2
- class Sequence
3
- def initialize(conn, sch, relname)
4
- @name = "#{sch}.#{relname}"
5
- end
6
-
7
- def definition
8
- "CREATE SEQUENCE #{@name} ;"
9
- end
10
- end
11
- end
data/lib/table.rb DELETED
@@ -1,90 +0,0 @@
1
- module PgDiff
2
- class Table
3
- attr_accessor :table_name, :schema, :attributes, :constraints, :indexes
4
-
5
- def initialize(conn, schema, table_name)
6
- @schema = schema
7
- @table_name = table_name
8
- @attributes = {}
9
- @constraints = {}
10
- @indexes = {}
11
- @atlist = []
12
-
13
- att_query = <<-EOT
14
- select attname, format_type(atttypid, atttypmod) as a_type, attnotnull, pg_get_expr(adbin, attrelid) as a_default
15
- from pg_attribute left join pg_attrdef on (adrelid = attrelid and adnum = attnum)
16
- where attrelid = '#{schema}.#{table_name}'::regclass and not attisdropped and attnum > 0
17
- order by attnum
18
- EOT
19
- conn.query(att_query).each do |tuple|
20
- attname = tuple['attname']
21
- typedef = tuple['a_type']
22
- notnull = tuple['attnotnull']
23
- default = tuple['a_default']
24
- @attributes[attname] = Attribute.new(attname, typedef, notnull, default)
25
- @atlist << attname
26
- end
27
-
28
- ind_query = <<-EOT
29
- select indexrelid::regclass as indname, pg_get_indexdef(indexrelid) as def
30
- from pg_index where indrelid = '#{schema}.#{table_name}'::regclass and not indisprimary
31
- EOT
32
- conn.query(ind_query).each do |tuple|
33
- name = tuple['indname']
34
- value = tuple['def']
35
- @indexes[name] = value
36
- end
37
-
38
- cons_query = <<-EOT
39
- select conname, pg_get_constraintdef(oid) from pg_constraint where conrelid = '#{schema}.#{table_name}'::regclass
40
- EOT
41
- conn.query(cons_query).each do |tuple|
42
- name = tuple['conname']
43
- value = tuple['pg_get_constraintdef']
44
- @constraints[name] = value
45
- end
46
- @constraints.keys.each do |cname|
47
- @indexes.delete("#{schema}.#{cname}") if has_index?(cname)
48
- end
49
- end
50
-
51
- def has_attribute?(name)
52
- @attributes.has_key?(name)
53
- end
54
-
55
- def attribute_index(name)
56
- @atlist.index(name)
57
- end
58
-
59
- def has_index?(name)
60
- @indexes.has_key?(name) || @indexes.has_key?("#{schema}.#{name}")
61
- end
62
-
63
- def has_constraint?(name)
64
- @constraints.has_key?(name)
65
- end
66
-
67
- def table_creation
68
- out = ["CREATE TABLE #{name} ("]
69
- stmt = []
70
- @atlist.each do |attname|
71
- stmt << @attributes[attname].definition
72
- end
73
- out << stmt.join(",\n")
74
- out << ");"
75
- out.join("\n")
76
- end
77
-
78
- def name
79
- "#{schema}.#{table_name}"
80
- end
81
-
82
- def index_creation
83
- out = []
84
- @indexes.values.each do |c|
85
- out << (c+";")
86
- end
87
- out.join("\n")
88
- end
89
- end
90
- end
data/lib/trigger.rb DELETED
@@ -1,15 +0,0 @@
1
- module PgDiff
2
- class Trigger
3
- attr_reader :table_name, :name, :definition
4
-
5
- def initialize(table_name, name, df)
6
- @table_name = table_name
7
- @name = name
8
- @definition = df + ";"
9
- end
10
-
11
- def == (other)
12
- other.definition == definition
13
- end
14
- end
15
- end
data/lib/view.rb DELETED
@@ -1,18 +0,0 @@
1
- module PgDiff
2
- class View
3
- attr_reader :def, :name
4
-
5
- def initialize(conn, sch, relname)
6
- @name = "#{sch}.#{relname}"
7
- view_qery = <<-EOT
8
- SELECT pg_catalog.pg_get_viewdef('#{@name}'::regclass, true)
9
- EOT
10
- tuple = conn.query(view_qery).first
11
- @def = tuple['pg_get_viewdef']
12
- end
13
-
14
- def definition
15
- "CREATE VIEW #{@name} AS #{@def}"
16
- end
17
- end
18
- end