<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: postgresql code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sun, 12 Oct 2008 19:15:09 GMT</pubDate>
    <description>DZone Snippets: postgresql code</description>
    <item>
      <title>PostgreSQL: generate DDL to alter views with dependencies</title>
      <link>http://snippets.dzone.com/posts/show/4038</link>
      <description>Determine dependencies on given table/view or their columns, types (base/domain/composite), functions, rules and show DROP/ALTER/CREATE series to update their definitions.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/usr/bin/ruby -W0&lt;br /&gt;#&lt;br /&gt;# This script tries to determine all dependencies on given table/view or their columns, &lt;br /&gt;# types (base/domain/composite), functions, rules.&lt;br /&gt;#&lt;br /&gt;# Usage:&lt;br /&gt;#       ruby show_obj_deps conn_string object_id alter_stmt &lt;br /&gt;# e.g:&lt;br /&gt;#       ruby show_obj_deps dbname=db1 "VIEW public.base_stats" "DROP VIEW public.base_stats; CREATE VIEW public.base_stats AS SELECT ..."&lt;br /&gt;#&lt;br /&gt;# Result will be series of DDL DROP statements for dependent objects, then alter_stmt, and&lt;br /&gt;# then series of DDL CREATE statements for dropped objects.&lt;br /&gt;#&lt;br /&gt;# Format of object_id:&lt;br /&gt;#   TABLE schema_name.table_name&lt;br /&gt;#   VIEW schema_name.view_name&lt;br /&gt;#   TABLE schema_name.table_name COLUMN column_name&lt;br /&gt;#   VIEW schema_name.view_name COLUMN column_name&lt;br /&gt;#   FUNCTION schema_name.func_name(type_1, type2, ...)&lt;br /&gt;#   RULE rule_name ON schema_name.obj_name&lt;br /&gt;#&lt;br /&gt;# This script is somewhat rewritten version of http://snippets.dzone.com/posts/show/2105&lt;br /&gt;#&lt;br /&gt;# Developed using PostgreSQL v8.0.3, v8.1 with ruby-postgres libpq binding&lt;br /&gt;# &lt;br /&gt;&lt;br /&gt;require 'postgres'&lt;br /&gt;require 'tsort'&lt;br /&gt;require 'pp'&lt;br /&gt;PGconn.translate_results = true&lt;br /&gt;&lt;br /&gt;$PG_CLASSES = {}&lt;br /&gt;class PgDependencyGraph            &lt;br /&gt;class DBObject&lt;br /&gt;  attr_accessor :row, :o_type, :nsp&lt;br /&gt;  def initialize(conn, class_id, obj_id, sub_id)&lt;br /&gt;    @o_type   = $PG_CLASSES[class_id]&lt;br /&gt;    @row   = conn.query(sql_for(@o_type, obj_id, sub_id)).first&lt;br /&gt;    @nsp = row['nsp']&lt;br /&gt;    if @o_type == 'pg_proc'&lt;br /&gt;      arg_types = row.last.split(" ")&lt;br /&gt;      unless arg_types.empty?&lt;br /&gt;        arg_type_names = arg_types.map {|oid| "format_type(#{oid}, -1)"}.join(", ")&lt;br /&gt;        row[-1] = "("+conn.query("SELECT #{arg_type_names}").first.join(", ") +")"&lt;br /&gt;      else &lt;br /&gt;	     row[-1] = "()"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  def sql_for(pg_class, obj_id, sub_id)&lt;br /&gt;  base_sql = case pg_class &lt;br /&gt;    when 'pg_type' &lt;br /&gt;      "select       (case &lt;br /&gt;      	when typtype = 'b' then 'BASE '&lt;br /&gt;	when typtype = 'c' then 'COMPOSITE '&lt;br /&gt;	when typtype = 'd' then 'DOMAIN '&lt;br /&gt;	when typtype = 'p' then 'PSEUDO '&lt;br /&gt;      end) || coalesce( &lt;br /&gt;      (select (CASE WHEN relkind = 'r' THEN 'TABLE'&lt;br /&gt;            WHEN relkind = 'v' THEN 'VIEW'&lt;br /&gt;            WHEN relkind = 'i' THEN 'INDEX'&lt;br /&gt;            WHEN relkind = 'S' THEN 'SEQUENCE'&lt;br /&gt;            WHEN relkind = 's' THEN 'SPECIAL'&lt;br /&gt;            WHEN relkind = 't' THEN 'TOAST'&lt;br /&gt;	    WHEN relkind = 'c' THEN ' '&lt;br /&gt;        END) from pg_class c where c.oid = typrelid), ' '), &lt;br /&gt;(select nspname from pg_namespace n where n.oid = typnamespace) as nsp,       typname as obj_name,&lt;br /&gt;      #{sub_id} from pg_type  "&lt;br /&gt;    when 'pg_proc' &lt;br /&gt;      "select (select nspname from pg_namespace n where n.oid = pronamespace) as nsp, proname as obj_name, proargtypes from pg_proc "&lt;br /&gt;    when 'pg_class'&lt;br /&gt;      "select &lt;br /&gt;  (CASE WHEN relkind = 'r' THEN 'TABLE'&lt;br /&gt;            WHEN relkind = 'v' THEN 'VIEW'&lt;br /&gt;            WHEN relkind = 'i' THEN 'INDEX'&lt;br /&gt;            WHEN relkind = 'S' THEN 'SEQUENCE'&lt;br /&gt;            WHEN relkind = 's' THEN 'SPECIAL'&lt;br /&gt;            WHEN relkind = 't' THEN 'TOAST'&lt;br /&gt;        END) , (select nspname from pg_namespace n where n.oid = relnamespace) as nsp, &lt;br /&gt;       relname as obj_name,  &lt;br /&gt;      (select attname from pg_attribute where attrelid = #{obj_id} and attnum = #{sub_id}) from pg_class"&lt;br /&gt;    when 'pg_rewrite'&lt;br /&gt;      "select (select nspname from pg_namespace n where n.oid = (select relnamespace from pg_class c where c.oid = ev_class) ) as nsp, rulename, (select relname from pg_class c where c.oid = ev_class)  from pg_rewrite"&lt;br /&gt;    else&lt;br /&gt;        puts "IGNORE: #{pg_class}, #{obj_id}, #{sub_id}"&lt;br /&gt;        nil&lt;br /&gt;  end&lt;br /&gt;  base_sql = base_sql ? base_sql &lt;&lt; " WHERE oid = #{obj_id} ": nil&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt; &lt;br /&gt;  def to_s&lt;br /&gt;    case @o_type&lt;br /&gt;    	when 'pg_proc' : "FUNCTION #{@row[0]}.#{@row[1]}#{@row[2]}"&lt;br /&gt;      when 'pg_type': "TYPE: #{@row[0]} #{@row[1]}.#{@row[2]}"&lt;br /&gt;      when 'pg_class' : "#{@row[0]} #{@row[1]}.#{@row[2]}" + (row[3] ? " COLUMN #{@row[3]}" : "")&lt;br /&gt;      when 'pg_rewrite' :  @row[1] == '_RETURN' ? "VIEW #{@row[0]}.#{@row[2]}" : "RULE #{@row[1]} ON #{@row[0]}.#{@row[2]}"&lt;br /&gt;    end &lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;  attr_accessor :depend_graph&lt;br /&gt;    ACCEPTED = ['pg_type', 'pg_proc', 'pg_class', 'pg_rewrite']&lt;br /&gt;  def initialize(conn)&lt;br /&gt;    conn.query("select distinct classid, relname from pg_class c join pg_depend d on (c.oid = d.classid)").each do |row|&lt;br /&gt;      $PG_CLASSES[row['classid']] = row['relname']&lt;br /&gt;    end&lt;br /&gt;    conn.query("select distinct refclassid, relname from pg_class c join pg_depend d on (c.oid = d.refclassid)").each do |row|&lt;br /&gt;      $PG_CLASSES[row['refclassid']] = row['relname']&lt;br /&gt;    end&lt;br /&gt;    dep_graphs = {}&lt;br /&gt;    conn.query("SELECT * FROM pg_catalog.pg_depend   where  true or ( deptype&lt;&gt; 'i' and deptype &lt;&gt; 'p')").each do |row|&lt;br /&gt;    if ACCEPTED.include?($PG_CLASSES[row['classid']]) &amp;&amp; ACCEPTED.include?($PG_CLASSES[row['refclassid']])&lt;br /&gt;      this_obj = DBObject.new(conn, row['classid'], row['objid'], row['objsubid'])&lt;br /&gt;      that_obj = DBObject.new(conn, row['refclassid'], row['refobjid'], row['refobjsubid'])&lt;br /&gt;      #dep_string = this_obj.nsp !~ /^pg_toast$/ ? "\"#{this_obj.to_s}\" -&gt; \"#{that_obj.to_s}\"; \n" : nil&lt;br /&gt;      dep_string = this_obj.nsp !~ /^(information_schema|pg_catalog|pg_toast)$/  ? this_obj.to_s : nil&lt;br /&gt;    &lt;br /&gt;      if dep_string &amp;&amp; this_obj.to_s !~ /^INDEX /&lt;br /&gt;        dep_graphs[that_obj.to_s] ||=[]&lt;br /&gt;        dep_graphs[that_obj.to_s] &lt;&lt; dep_string&lt;br /&gt;        if that_obj.to_s =~ /^(VIEW|TABLE) (.+?) COLUMN /m&lt;br /&gt;          ds2 = that_obj.to_s.sub(/ COLUMN.*/m, '')&lt;br /&gt;          dep_graphs[ds2] ||=[]&lt;br /&gt;          dep_graphs[ds2] &lt;&lt; that_obj.to_s unless that_obj.to_s == ds2&lt;br /&gt;        elsif that_obj.to_s =~ /^TYPE: COMPOSITE (TABLE|VIEW) /&lt;br /&gt;          ds2 = that_obj.to_s.sub(/^TYPE: COMPOSITE (TABLE|VIEW) /, '\1 ')&lt;br /&gt;          dep_graphs[ds2] ||=[]&lt;br /&gt;          dep_graphs[ds2] &lt;&lt; that_obj.to_s unless that_obj.to_s == ds2&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  dep_graphs.each do |that, values|&lt;br /&gt;    values.uniq!&lt;br /&gt;    values.reject! {|item| item == that}&lt;br /&gt;  end&lt;br /&gt;  @depend_graph = dep_graphs&lt;br /&gt; end&lt;br /&gt; def list_dependencies(obj)&lt;br /&gt;    dep_list = []&lt;br /&gt;    if @depend_graph[obj]&lt;br /&gt;      @depend_graph[obj].each do |v|&lt;br /&gt;        k = list_dependencies(v)&lt;br /&gt;        k.empty? ? dep_list &lt;&lt; v : dep_list &lt;&lt; [v, k]&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    dep_list&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class DG&lt;br /&gt; include TSort&lt;br /&gt; def initialize(dep_graph, node_list)&lt;br /&gt;   @nodes = node_list&lt;br /&gt;   @dg = dep_graph&lt;br /&gt; end&lt;br /&gt; def tsort_each_node(&amp;block)&lt;br /&gt;   @nodes.each {|x| yield x}&lt;br /&gt; end&lt;br /&gt; def tsort_each_child(node, &amp;block)&lt;br /&gt;   (@dg[node]||[]).each(&amp;block)&lt;br /&gt; end&lt;br /&gt;end    &lt;br /&gt;&lt;br /&gt;class Function&lt;br /&gt;  attr_reader :typed_head&lt;br /&gt;  def initialize(conn, tuple)&lt;br /&gt;    @name = tuple['namespace'] + "." + tuple['function_name']&lt;br /&gt;    @language = tuple['language_name']&lt;br /&gt;    @src = tuple['source_code']&lt;br /&gt;    @returns_set = tuple['returns_set']&lt;br /&gt;    @return_type = format_type(conn, tuple['return_type'])&lt;br /&gt;    @tipes = tuple['function_args'].split(" ")&lt;br /&gt;    if tuple['function_arg_names'] &amp;&amp; tuple['function_arg_names'] =~ /^\{(.*)\}$/&lt;br /&gt;      @arnames = $1.split(',')&lt;br /&gt;    elsif tuple['function_arg_names'].is_a? Array&lt;br /&gt;      @arnames = tuple['function_arg_names']&lt;br /&gt;    else&lt;br /&gt;      @arnames = [""] * @tipes.length&lt;br /&gt;    end&lt;br /&gt;    alist = []&lt;br /&gt;    atypelist = [] &lt;br /&gt;    @tipes.each_with_index do |typ,idx|&lt;br /&gt;      ft = format_type(conn, typ)&lt;br /&gt;      alist &lt;&lt; (@arnames[idx] +" " + ft)&lt;br /&gt;      atypelist &lt;&lt; ft&lt;br /&gt;    end&lt;br /&gt;    @arglist = alist.join(" , ")&lt;br /&gt;    @strict = tuple['proisstrict'] ? ' STRICT' : ''&lt;br /&gt;    @secdef = tuple['prosecdef'] ? ' SECURITY DEFINER' : ''&lt;br /&gt;    @volatile = case tuple['provolatile']&lt;br /&gt;      when 'i' then ' IMMUTABLE'&lt;br /&gt;      when 's' then ' STABLE'&lt;br /&gt;      else ''&lt;br /&gt;    end&lt;br /&gt;    @typed_head = @name+"("+atypelist.join(", ")+")"&lt;br /&gt;  end&lt;br /&gt;  def signature&lt;br /&gt;    "#{@name}(#{@arglist})"&lt;br /&gt;  end&lt;br /&gt;  def definition&lt;br /&gt;    &lt;&lt;-EOT&lt;br /&gt;CREATE OR REPLACE FUNCTION #{@name} (#{@arglist}) RETURNS #{@returns_set ?  'SETOF' : ''} #{@return_type} AS $_$#{@src}$_$ LANGUAGE '#{@language}' #{@volatile}#{@strict}#{@secdef};&lt;br /&gt;EOT&lt;br /&gt;  end&lt;br /&gt;  def == (other)&lt;br /&gt;    definition == other.definition&lt;br /&gt;  end&lt;br /&gt;  def format_type(conn, oid)&lt;br /&gt;    t_query = &lt;&lt;-EOT&lt;br /&gt;    SELECT pg_catalog.format_type(pg_type.oid, typtypmod) AS type_name&lt;br /&gt;     FROM pg_catalog.pg_type&lt;br /&gt;     JOIN pg_catalog.pg_namespace ON (pg_namespace.oid = typnamespace)&lt;br /&gt;     WHERE pg_type.oid = &lt;br /&gt;    EOT&lt;br /&gt;    return conn.query(t_query + oid.to_s)[0][0]&lt;br /&gt;  end&lt;br /&gt;  def self.find(conn, schema, name, args)&lt;br /&gt;    func_query = &lt;&lt;-EOT&lt;br /&gt;     SELECT proname AS function_name&lt;br /&gt;     , nspname AS namespace&lt;br /&gt;     , lanname AS language_name&lt;br /&gt;     , pg_catalog.obj_description(pg_proc.oid, 'pg_proc') AS comment&lt;br /&gt;     , proargtypes AS function_args&lt;br /&gt;     , proargnames AS function_arg_names&lt;br /&gt;     , prosrc AS source_code&lt;br /&gt;     , proretset AS returns_set&lt;br /&gt;     , prorettype AS return_type,&lt;br /&gt;     provolatile, proisstrict, prosecdef&lt;br /&gt;     FROM pg_catalog.pg_proc&lt;br /&gt;     JOIN pg_catalog.pg_language ON (pg_language.oid = prolang)&lt;br /&gt;     JOIN pg_catalog.pg_namespace ON (pronamespace = pg_namespace.oid)&lt;br /&gt;     JOIN pg_catalog.pg_type ON (prorettype = pg_type.oid)&lt;br /&gt;     WHERE pg_namespace.nspname !~ 'pg_catalog|information_schema|pg_temp_'&lt;br /&gt;     AND nspname = $1&lt;br /&gt;     AND proname = $2&lt;br /&gt;     AND oidvectortypes(proargtypes) = $3&lt;br /&gt;    EOT&lt;br /&gt;&lt;br /&gt;    Function.new(conn, conn.query(func_query, schema, name, args).first)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;conn = PGconn.new(ARGV[0])&lt;br /&gt;graph = PgDependencyGraph.new(conn)&lt;br /&gt;#pp graph.list_dependencies(ARGV[1])&lt;br /&gt;dep_list = []&lt;br /&gt;ARGV[1].split(/\|/).each do |obj_id|&lt;br /&gt;  dep_list += graph.list_dependencies(obj_id).flatten.uniq&lt;br /&gt;end&lt;br /&gt;dep_list.uniq!&lt;br /&gt;&lt;br /&gt;top_sorted = DG.new(graph.depend_graph, dep_list).tsort&lt;br /&gt;top_sorted.each do |line|&lt;br /&gt;  case line&lt;br /&gt;        when /^TYPE:/, /^VIEW (\S+) COLUMN/; &lt;br /&gt;        when /^VIEW (.+)/ then puts "DROP VIEW #$1;"&lt;br /&gt;        when /^FUNCTION (.+)/ then puts "DROP FUNCTION #$1;"&lt;br /&gt;        when /^RULE / then puts "DROP "+line+";"&lt;br /&gt;        else &lt;br /&gt;        puts "-- SKIP #{line}"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;puts "", "--- ", "--- ALTER: ", "---"&lt;br /&gt;puts ARGV[2]&lt;br /&gt;puts "---", "---", ""&lt;br /&gt;def view_def(conn, name)&lt;br /&gt;  conn.select_value("SELECT pg_catalog.pg_get_viewdef('#{name}'::regclass, true)")&lt;br /&gt;end&lt;br /&gt;def rule_def(conn, tablename, rule_name)&lt;br /&gt; conn.select_value("select definition from pg_rules  where schemaname || '.' ||  tablename = $1  and rulename = $2", tablename, rule_name)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def func_def(conn, *args)&lt;br /&gt; Function.find(conn, *args).definition&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;top_sorted.reverse.each do |line|&lt;br /&gt;  case line&lt;br /&gt;        when /^TYPE:/, /^VIEW (\S+) COLUMN/ then puts "-- SKIP #{line}"&lt;br /&gt;        when /^VIEW (.+)/ then puts "CREATE VIEW #$1 AS "+view_def(conn, $1)&lt;br /&gt;        when /^FUNCTION (\w+)\.(\w+)\((.*)\)/ then puts func_def(conn, $1, $2, $3)&lt;br /&gt;        when /^RULE (.+) ON (.+)/ then puts "CREATE "+rule_def(conn, $2, $1)&lt;br /&gt;        else&lt;br /&gt;          puts "-- SKIP #{line}"&lt;br /&gt;  end&lt;br /&gt;  puts &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 17 May 2007 14:19:15 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4038</guid>
      <author>dseverin ()</author>
    </item>
    <item>
      <title>pg_show_deps: outputs dependencies graph for tables, types, proc, views in PostgreSQL database schema.</title>
      <link>http://snippets.dzone.com/posts/show/2105</link>
      <description>pg_show_deps: outputs dependencies graph for tables, types, proc, views in PostgreSQL database schema.&lt;br /&gt; output is produced in dot format and can be processed using graphviz (http://www.graphviz.org/) toolset&lt;br /&gt;# to get a picture of dependencies.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/bin/env ruby&lt;br /&gt;#&lt;br /&gt;# pg_show_deps: outputs dependencies graph for objects (tables, types, proc, views) in PostgreSQL database schema.&lt;br /&gt;# output is produced in dot format and can be processed using graphviz (http://www.graphviz.org/) toolset&lt;br /&gt;# to get a picture of dependencies.&lt;br /&gt;#&lt;br /&gt;# Sample usage:&lt;br /&gt;#    pg_show_deps 'dbname=template1 port=5432 user=looser' &gt;template1.dot&lt;br /&gt;#    ccomps -otemplate_split.dot -x template1.dot&lt;br /&gt;#    dot -Tps -o template_2.ps template_split_2.dot&lt;br /&gt;#&lt;br /&gt;# Developed using PostgreSQL 8.1, &lt;br /&gt;# requires ruby-postgres driver (http://rubyforge.org/projects/ruby-postgres)&lt;br /&gt;&lt;br /&gt;require 'postgres'&lt;br /&gt;$PG_CLASSES = {}&lt;br /&gt;            &lt;br /&gt;class DBObject&lt;br /&gt;  attr_accessor :row, :o_type, :nsp&lt;br /&gt;  def initialize(conn, class_id, obj_id, sub_id)&lt;br /&gt;    @o_type   = $PG_CLASSES[class_id]&lt;br /&gt;    @row   = conn.query(sql_for(@o_type, obj_id, sub_id)).first&lt;br /&gt;    @nsp = row['nsp']&lt;br /&gt;    if @o_type == 'pg_proc'&lt;br /&gt;      arg_types = row.last.split(" ")&lt;br /&gt;      unless arg_types.empty?&lt;br /&gt;        arg_type_names = arg_types.map {|oid| "format_type(#{oid}, -1)"}.join(", ")&lt;br /&gt;        row[-1] = "("+conn.query("SELECT #{arg_type_names}").first.join(", ") +")"&lt;br /&gt;      else &lt;br /&gt;	     row[-1] = "()"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  def sql_for(pg_class, obj_id, sub_id)&lt;br /&gt;  base_sql = case pg_class &lt;br /&gt;    when 'pg_type' &lt;br /&gt;      "select       (case &lt;br /&gt;      	when typtype = 'b' then 'BASE '&lt;br /&gt;	when typtype = 'c' then 'COMPOSITE '&lt;br /&gt;	when typtype = 'd' then 'DOMAIN '&lt;br /&gt;	when typtype = 'p' then 'PSEUDO '&lt;br /&gt;      end) || coalesce( &lt;br /&gt;      (select (CASE WHEN relkind = 'r' THEN 'TABLE'&lt;br /&gt;            WHEN relkind = 'v' THEN 'VIEW'&lt;br /&gt;            WHEN relkind = 'i' THEN 'INDEX'&lt;br /&gt;            WHEN relkind = 'S' THEN 'SEQUENCE'&lt;br /&gt;            WHEN relkind = 's' THEN 'SPECIAL'&lt;br /&gt;            WHEN relkind = 't' THEN 'TOAST'&lt;br /&gt;	    WHEN relkind = 'c' THEN ' '&lt;br /&gt;        END) from pg_class c where c.oid = typrelid), ' '), &lt;br /&gt;(select nspname from pg_namespace n where n.oid = typnamespace) as nsp,       typname as obj_name,&lt;br /&gt;      #{sub_id} from pg_type  "&lt;br /&gt;    when 'pg_proc' &lt;br /&gt;      "select (select nspname from pg_namespace n where n.oid = pronamespace) as nsp, proname as obj_name, proargtypes from pg_proc "&lt;br /&gt;    when 'pg_class'&lt;br /&gt;      "select &lt;br /&gt;  (CASE WHEN relkind = 'r' THEN 'TABLE'&lt;br /&gt;            WHEN relkind = 'v' THEN 'VIEW'&lt;br /&gt;            WHEN relkind = 'i' THEN 'INDEX'&lt;br /&gt;            WHEN relkind = 'S' THEN 'SEQUENCE'&lt;br /&gt;            WHEN relkind = 's' THEN 'SPECIAL'&lt;br /&gt;            WHEN relkind = 't' THEN 'TOAST'&lt;br /&gt;        END) , (select nspname from pg_namespace n where n.oid = relnamespace) as nsp, &lt;br /&gt;       relname as obj_name,  &lt;br /&gt;      (select attname from pg_attribute where attrelid = #{obj_id} and attnum = #{sub_id}) from pg_class"&lt;br /&gt;    when 'pg_rewrite'&lt;br /&gt;      "select (select nspname from pg_namespace n where n.oid = (select relnamespace from pg_class c where c.oid = ev_class) ) as nsp, rulename, (select relname from pg_class c where c.oid = ev_class)  from pg_rewrite"&lt;br /&gt;    else nil&lt;br /&gt;  end&lt;br /&gt;  base_sql = base_sql ? base_sql &lt;&lt; " WHERE oid = #{obj_id} ": nil&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt; &lt;br /&gt;  def to_s&lt;br /&gt;    case @o_type&lt;br /&gt;    	when 'pg_proc' : "FUNCTION\\n#{@row[0]}.#{@row[1]}#{@row[2]}"&lt;br /&gt;	when 'pg_type': "TYPE: #{@row[0]}\\n#{@row[1]}.#{@row[2]}"&lt;br /&gt;	when 'pg_class' : "#{@row[0]}\\n#{@row[1]}.#{@row[2]}" + (row[3] ? "\\nCOLUMN #{@row[3]}" : "")&lt;br /&gt;	when 'pg_rewrite' :  @row[1] == '_RETURN' ? "VIEW\\n#{@row[0]}.#{@row[2]}" : "RULE #{@row[1]} ON \\n#{@row[0]}.#{@row[2]}"&lt;br /&gt;    end &lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# connection to database&lt;br /&gt;conn=PGconn.new(ARGV[0])&lt;br /&gt;&lt;br /&gt;conn.query("select distinct classid, relname from pg_class c join pg_depend d on (c.oid = d.classid)").each do |row|&lt;br /&gt;  $PG_CLASSES[row['classid']] = row['relname']&lt;br /&gt;end&lt;br /&gt;conn.query("select distinct refclassid, relname from pg_class c join pg_depend d on (c.oid = d.refclassid)").each do |row|&lt;br /&gt;  $PG_CLASSES[row['refclassid']] = row['relname']&lt;br /&gt;end&lt;br /&gt;ACCEPTED = ['pg_type', 'pg_proc', 'pg_class', 'pg_rewrite']&lt;br /&gt;dep_graphs = {}&lt;br /&gt;conn.query("SELECT * FROM pg_catalog.pg_depend   where   deptype&lt;&gt; 'i' and deptype &lt;&gt; 'p'").each do |row|&lt;br /&gt;  if ACCEPTED.include?($PG_CLASSES[row['classid']]) &amp;&amp; ACCEPTED.include?($PG_CLASSES[row['refclassid']])&lt;br /&gt;    this_obj = DBObject.new(conn, row['classid'], row['objid'], row['objsubid'])&lt;br /&gt;    that_obj = DBObject.new(conn, row['refclassid'], row['refobjid'], row['refobjsubid'])&lt;br /&gt;    #dep_string = this_obj.nsp !~ /^pg_toast$/ ? "\"#{this_obj.to_s}\" -&gt; \"#{that_obj.to_s}\"; \n" : nil&lt;br /&gt;    dep_string = this_obj.nsp !~ /^(information_schema|pg_catalog|pg_toast)$/  ? "\"#{this_obj.to_s}\" -&gt; \"#{that_obj.to_s}\"; \n" : nil&lt;br /&gt;    &lt;br /&gt;    if dep_string &amp;&amp; this_obj.to_s !~ /^INDEX\\n/&lt;br /&gt;      dep_graphs[that_obj.to_s] ||=[]&lt;br /&gt;      dep_graphs[that_obj.to_s] &lt;&lt; dep_string&lt;br /&gt;      if that_obj.to_s =~ /^(VIEW|TABLE)\\n(.+?)\\nCOLUMN /m&lt;br /&gt;        ds2 = that_obj.to_s.sub(/\\nCOLUMN.*/m, '')&lt;br /&gt;        dep_graphs[ds2] ||=[]&lt;br /&gt;        dep_graphs[ds2] &lt;&lt; "\"#{that_obj.to_s}\" -&gt; \"#{ds2}\"; \n"&lt;br /&gt;      end&lt;br /&gt;      if that_obj.to_s =~ /^TYPE: COMPOSITE (TABLE|VIEW)\\n/&lt;br /&gt;        ds2 = that_obj.to_s.sub(/^TYPE: COMPOSITE (TABLE|VIEW)\\n/, '\1\n')&lt;br /&gt;        dep_graphs[ds2] ||=[]&lt;br /&gt;        dep_graphs[ds2] &lt;&lt; "\"#{that_obj.to_s}\" -&gt; \"#{ds2}\"; \n"&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;print &lt;&lt;-EOT&lt;br /&gt;digraph g {&lt;br /&gt;  graph [rankdir = "RL",concentrate = true,ratio = auto, overlap=false];&lt;br /&gt;  node [fontsize = "10"];&lt;br /&gt;  edge [];&lt;br /&gt;EOT&lt;br /&gt;dep_graphs.each do |that, values|&lt;br /&gt; puts "subgraph \"#{that}\" {"&lt;br /&gt;    values.uniq.each do |v|&lt;br /&gt;	    a = v.split( / -&gt; /)&lt;br /&gt;	    puts v unless a[0] == "\"#{that}\""&lt;br /&gt;    end&lt;br /&gt; puts "}"&lt;br /&gt;end&lt;br /&gt;print "}"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Simple helper script to quickly create images for dependencies subgraphs.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;# mk_dep_doc.sh conninfo splitprefix output_type&lt;br /&gt;# e.g.:&lt;br /&gt;# mk_dep_doc.sh "dbname=template1" template_split png&lt;br /&gt;./pg_show_deps "$1" | ccomps -o "$2.dot" -x&lt;br /&gt;find -name "$2*dot" -exec dot -T$3 -o{}.$3 {} \;&lt;br /&gt;find -name "$2*dot" -exec echo "&lt;a href={}&gt;{}&lt;/a&gt;   &lt;a href={}.$3&gt;Image&lt;/a&gt;&lt;br/&gt;" \; &gt;index.html&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Fri, 26 May 2006 12:28:54 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2105</guid>
      <author>dseverin ()</author>
    </item>
    <item>
      <title>PgProc - call PostgreSQL functions from Rails app</title>
      <link>http://snippets.dzone.com/posts/show/1008</link>
      <description>&lt;code&gt;&lt;br /&gt;# Just for fun and horror: make database functions a part of your Rails model! :)&lt;br /&gt;#&lt;br /&gt;# Class to access to PostgreSQL functions. Returned value depends on params and query result, see below.&lt;br /&gt;#&lt;br /&gt;# Currently supported options are:&lt;br /&gt;#     :order =&gt; '1 desc' # to add order clause&lt;br /&gt;#     :use_from =&gt; true  # to add "* from" for non-model function queries, which return records&lt;br /&gt;#     :all =&gt; true       # to return not first but all found models&lt;br /&gt;#     :cast =&gt; string    # to cast result (useful for functions, returning +setof record+)&lt;br /&gt;#&lt;br /&gt;# Call-patterns:&lt;br /&gt;#&lt;br /&gt;# A) Model loading from functions that return setof system known rowtype&lt;br /&gt;#     PgProc.function(ModelClass[, options])&lt;br /&gt;#         PgProc.get_descendants(ContentNode, 123)&lt;br /&gt;#&lt;br /&gt;#     PgProc.function(ModelClass, value[, options ])&lt;br /&gt;#         PgProc.get_children(ContentNode, 123, :order =&gt; 'position', :all =&gt; true)&lt;br /&gt;#&lt;br /&gt;# returns either:&lt;br /&gt;# * empty array if nothing found&lt;br /&gt;# * first found model object, if found only one and +:all+ option is not set&lt;br /&gt;# * array of model objects&lt;br /&gt;#&lt;br /&gt;# B) Values from functions&lt;br /&gt;#&lt;br /&gt;# PgProc.function(:type_symbol, value, [type_symbol2, value2, ...[, options]]) - for explicit parameter typecast&lt;br /&gt;#     PgProc.array_append(:"int[]", '{1,2,3,4}', :int, 5) # =&gt; {1,2,3,4,5}&lt;br /&gt;#&lt;br /&gt;# PgProc.function(*args [, options])&lt;br /&gt;#     PgProc.generate_series(1,10,2, :order =&gt; '1 desc') # =&gt; [9,5,7,3,2,1]&lt;br /&gt;#&lt;br /&gt;# PgProc.function() - for functions w/o params&lt;br /&gt;#     PgProc.now()&lt;br /&gt;#&lt;br /&gt;# returns either:&lt;br /&gt;# * empty string for +void+ functions&lt;br /&gt;# * single value, if resultset has 1x1 dimension&lt;br /&gt;# * array of values if resultset has Nx1 dimension (N&gt;1)&lt;br /&gt;# * array of rows otherwise&lt;br /&gt;#&lt;br /&gt;# Throws PGError, if function doesn't exist or wrong params supplied&lt;br /&gt;&lt;br /&gt;class PgProc &lt; ActiveRecord::Base&lt;br /&gt;    set_table_name 'pg_catalog.pg_proc'&lt;br /&gt;    set_primary_key 'oid'&lt;br /&gt;    def readonly?&lt;br /&gt;      true&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;private&lt;br /&gt;    def self.method_missing(meth_sym, *args)&lt;br /&gt;      func_name = meth_sym.id2name&lt;br /&gt;      super unless find(:first, :conditions =&gt; ['proname = ?', func_name])&lt;br /&gt;      if ! args.empty? &amp;&amp; args.last.is_a?(Hash)&lt;br /&gt;        options = args.pop&lt;br /&gt;        order_str = " ORDER BY #{options[:order]}" if options[:order]&lt;br /&gt;      else&lt;br /&gt;        options = {}&lt;br /&gt;        order_str = nil&lt;br /&gt;      end&lt;br /&gt;      from_str = " * FROM " if options[:use_from]&lt;br /&gt;      if args.empty?&lt;br /&gt;        temp = connection.query("select #{from_str} #{func_name}() #{options[:cast]} #{order_str}")&lt;br /&gt;      elsif args.first.is_a?(Class)&lt;br /&gt;        model_klass = args.shift&lt;br /&gt;        if args.length == 0&lt;br /&gt;          temp = model_klass.find_by_sql("select * from  #{func_name}()  #{options[:cast]} #{order_str}")&lt;br /&gt;        else&lt;br /&gt;          temp = model_klass.find_by_sql("select * from  #{func_name}(#{quote_bound_value(args)})  #{options[:cast]} #{order_str}")&lt;br /&gt;        end&lt;br /&gt;        return temp if options[:all]&lt;br /&gt;        return temp.length == 1 ? temp.first : temp&lt;br /&gt;      else&lt;br /&gt;          if args.length % 2 == 0 &amp;&amp; args.first.is_a?(Symbol)&lt;br /&gt;            temp = connection.query("select #{from_str} #{func_name}(#{quote_bound_value_types(args)}) #{options[:cast]} #{order_str}")&lt;br /&gt;          else&lt;br /&gt;            temp = connection.query("select #{from_str} #{func_name}(#{quote_bound_value(args)})  #{options[:cast]} #{order_str}")&lt;br /&gt;          end&lt;br /&gt;      end&lt;br /&gt;      return temp.first.first if temp.length == 1 &amp;&amp; temp.first.length == 1&lt;br /&gt;      return temp.flatten if temp.length &gt; 1 &amp;&amp; temp.first.length == 1&lt;br /&gt;      return temp&lt;br /&gt;    end&lt;br /&gt;    def self.quote_bound_value_types(value)&lt;br /&gt;      i = true&lt;br /&gt;      value.partition {|v| i = !i }.transpose.map{|v| "#{connection.quote(v[0])}::#{v[1]}"}.join(',')&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 22 Dec 2005 18:52:12 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/1008</guid>
      <author>dseverin ()</author>
    </item>
    <item>
      <title>pg_diff - compare two PostgreSQL database schemas</title>
      <link>http://snippets.dzone.com/posts/show/949</link>
      <description>&lt;code&gt;&lt;br /&gt;#!/bin/env ruby&lt;br /&gt;# pg_diff - compare two PostgreSQL database schemas&lt;br /&gt;#&lt;br /&gt;# URL: http://snippets.dzone.com/posts/show/949&lt;br /&gt;#&lt;br /&gt;# This is a simple approach to track database schema changes in PostgreSQL.&lt;br /&gt;# In some way it is similar to diff program, finding out structure changes&lt;br /&gt;# and results in  SQL script to upgrade to new schema.&lt;br /&gt;# &lt;br /&gt;# Differences are tracked on schemas, domains, sequences, views, tables, indices, constraints, rules, functions, triggers.&lt;br /&gt;# Two objects with the same name are considered equal if they have the same definitions. &lt;br /&gt;#&lt;br /&gt;# Missing features: tracking of ownership,  user rights, object dependencies, table inheritance, type casts, aggregates, operators.&lt;br /&gt;# &lt;br /&gt;# Usage:&lt;br /&gt;#    ./pg_diff dbname=db_v03_dev dbname=db_v04_dev&lt;br /&gt;# &lt;br /&gt;# Developed using PostgreSQL v8.0.3, v8.1 with ruby-postgres libpq binding (20051127 snapshot).&lt;br /&gt;# &lt;br /&gt;# This software is released under MIT License&lt;br /&gt;# &lt;br /&gt;# Copyright (c) 2005 Dmitry Severin &lt;dmitry.severin (at) gmail.com&gt;&lt;br /&gt;#&lt;br /&gt;# Permission is hereby granted, free of charge, to any person obtaining a copy&lt;br /&gt;# of this software and associated documentation files (the "Software"), to deal&lt;br /&gt;# in the Software without restriction, including without limitation the rights&lt;br /&gt;# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&lt;br /&gt;# copies of the Software, and to permit persons to whom the Software is&lt;br /&gt;# furnished to do so, subject to the following conditions:&lt;br /&gt;# &lt;br /&gt;# The above copyright notice and this permission notice shall be included in&lt;br /&gt;# all copies or substantial portions of the Software.&lt;br /&gt;# &lt;br /&gt;# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;br /&gt;# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;br /&gt;# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;br /&gt;# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&lt;br /&gt;# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&lt;br /&gt;# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&lt;br /&gt;# THE SOFTWARE.&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;require 'postgres'&lt;br /&gt;module PostgreSqlSchema&lt;br /&gt;&lt;br /&gt;class Attribute&lt;br /&gt;  attr_accessor :name, :type_def, :notnull, :default&lt;br /&gt;  def initialize(name, typedef, notnull, default)&lt;br /&gt;    @name = name&lt;br /&gt;    @type_def = typedef&lt;br /&gt;    @notnull = notnull&lt;br /&gt;    @default = default&lt;br /&gt;  end&lt;br /&gt;  def definition&lt;br /&gt;    out = ['    ', @name,  @type_def]&lt;br /&gt;    out &lt;&lt; 'NOT NULL' if @notnull&lt;br /&gt;    out &lt;&lt; 'DEFAULT ' + @default if @default&lt;br /&gt;    out.join(" ")&lt;br /&gt;  end&lt;br /&gt;  def == (other)&lt;br /&gt;    definition == other.definition&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Table&lt;br /&gt;  attr_accessor :table_name, :schema, :attributes, :constraints, :indexes&lt;br /&gt;  &lt;br /&gt;  def initialize(conn, schema, table_name)&lt;br /&gt;    @schema = schema&lt;br /&gt;    @table_name = table_name&lt;br /&gt;    @attributes = {}&lt;br /&gt;    @constraints = {}&lt;br /&gt;    @indexes = {}&lt;br /&gt;    @atlist = []&lt;br /&gt;    &lt;br /&gt;    att_query = &lt;&lt;-EOT&lt;br /&gt;      select attname, format_type(atttypid, atttypmod) as a_type, attnotnull,  pg_get_expr(adbin, attrelid) as a_default &lt;br /&gt;      from pg_attribute left join pg_attrdef  on (adrelid = attrelid and adnum = attnum) &lt;br /&gt;      where attrelid = '#{schema}.#{table_name}'::regclass and not attisdropped and attnum &gt; 0 &lt;br /&gt;      order by attnum&lt;br /&gt;    EOT&lt;br /&gt;    conn.query(att_query).each do |row|&lt;br /&gt;      attname = row[0]&lt;br /&gt;      @attributes[attname] = Attribute.new(attname, row[1], row[2], row[3])&lt;br /&gt;      @atlist &lt;&lt; attname&lt;br /&gt;    end&lt;br /&gt;  &lt;br /&gt;    ind_query = &lt;&lt;-EOT&lt;br /&gt;      select indexrelid::regclass as indname, pg_get_indexdef(indexrelid) as def &lt;br /&gt;      from pg_index where indrelid = '#{schema}.#{table_name}'::regclass and not indisprimary&lt;br /&gt;    EOT&lt;br /&gt;    conn.query(ind_query).each do |row|&lt;br /&gt;      @indexes[row[0]] = row[1]&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    cons_query = &lt;&lt;-EOT&lt;br /&gt;      select conname, pg_get_constraintdef(oid) from pg_constraint where conrelid = '#{schema}.#{table_name}'::regclass&lt;br /&gt;    EOT&lt;br /&gt;    conn.query(cons_query).each do |row|&lt;br /&gt;      @constraints[row[0]] = row[1]&lt;br /&gt;    end&lt;br /&gt;    @constraints.keys.each do |cname|&lt;br /&gt;      @indexes.delete("#{schema}.#{cname}") if has_index?(cname)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def has_attribute?(name)&lt;br /&gt;    @attributes.has_key?(name)&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def has_index?(name)&lt;br /&gt;    @indexes.has_key?(name) || @indexes.has_key?("#{schema}.#{name}")&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def has_constraint?(name)&lt;br /&gt;    @constraints.has_key?(name)&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def table_creation&lt;br /&gt;    out = ["CREATE TABLE #{name} ("]&lt;br /&gt;    stmt = []&lt;br /&gt;    @atlist.each do |attname|&lt;br /&gt;      stmt &lt;&lt; @attributes[attname].definition&lt;br /&gt;    end&lt;br /&gt;    out &lt;&lt; stmt.join(",\n")&lt;br /&gt;    out &lt;&lt; ");"&lt;br /&gt;    out.join("\n")&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def name&lt;br /&gt;    "#{schema}.#{table_name}"&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def constr_creation&lt;br /&gt;    out = []&lt;br /&gt;    @constraints.each do |n, c|&lt;br /&gt;      out &lt;&lt; "ALTER TABLE #{name} ADD CONSTRAINT #{n} #{c};" &lt;br /&gt;    end&lt;br /&gt;    out.join("\n")&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def index_creation&lt;br /&gt;    out = []&lt;br /&gt;    @indexes.values.each do |c|&lt;br /&gt;      out &lt;&lt; (c+";")&lt;br /&gt;    end&lt;br /&gt;    out.join("\n")&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Sequence&lt;br /&gt;&lt;br /&gt;  def initialize(conn, sch, relname)&lt;br /&gt;     @name = "#{sch}.#{relname}"&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def definition&lt;br /&gt;    "CREATE SEQUENCE #{@name} ;"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class View&lt;br /&gt;  attr_reader :def, :name &lt;br /&gt;  &lt;br /&gt;  def initialize(conn, sch, relname)&lt;br /&gt;    @name = "#{sch}.#{relname}"&lt;br /&gt;    view_qery = &lt;&lt;-EOT&lt;br /&gt;      SELECT pg_catalog.pg_get_viewdef('#{@name}'::regclass, true)&lt;br /&gt;    EOT&lt;br /&gt;    @def = conn.query(view_qery)[0][0]&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def definition&lt;br /&gt;    "CREATE VIEW #{@name} AS #{@def}"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Database&lt;br /&gt;  attr_accessor :tables, :views, :sequences, :schemas, :domains, :rules, :functions, :triggers&lt;br /&gt;  def initialize(conn)&lt;br /&gt;     cls_query = &lt;&lt;-EOT&lt;br /&gt;      SELECT n.nspname, c.relname, c.relkind&lt;br /&gt;      FROM pg_catalog.pg_class c&lt;br /&gt;      LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner&lt;br /&gt;      LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace&lt;br /&gt;      WHERE c.relkind IN ('r','S','v')&lt;br /&gt;      AND n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')&lt;br /&gt;      ORDER BY 1,2;&lt;br /&gt;    EOT&lt;br /&gt;    @views = {}&lt;br /&gt;    @tables = {}&lt;br /&gt;    @sequences = {}&lt;br /&gt;    @schemas = {}&lt;br /&gt;    @domains = {}&lt;br /&gt;    @functions = {}&lt;br /&gt;    @rules = {}&lt;br /&gt;    @triggers = {}&lt;br /&gt;    &lt;br /&gt;    conn.query(cls_query).each do |row|&lt;br /&gt;      schema, relname, relkind = row&lt;br /&gt;      case relkind&lt;br /&gt;        when 'r' then @tables["#{schema}.#{relname}"] = Table.new(conn, schema, relname)&lt;br /&gt;        when 'v' then @views ["#{schema}.#{relname}"] = View.new(conn, schema, relname)&lt;br /&gt;        when 'S' then @sequences["#{schema}.#{relname}"] = Sequence.new(conn, schema, relname)&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  domain_qry = &lt;&lt;-EOT&lt;br /&gt;    SELECT n.nspname, t.typname,  pg_catalog.format_type(t.typbasetype, t.typtypmod) || ' ' ||&lt;br /&gt;       CASE WHEN t.typnotnull AND t.typdefault IS NOT NULL THEN 'not null default '||t.typdefault&lt;br /&gt;            WHEN t.typnotnull AND t.typdefault IS NULL THEN 'not null'&lt;br /&gt;            WHEN NOT t.typnotnull AND t.typdefault IS NOT NULL THEN 'default '||t.typdefault&lt;br /&gt;            ELSE ''&lt;br /&gt;       END &lt;br /&gt;    FROM pg_catalog.pg_type t&lt;br /&gt;       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace&lt;br /&gt;    WHERE t.typtype = 'd'&lt;br /&gt;    ORDER BY 1, 2&lt;br /&gt;  EOT&lt;br /&gt;    conn.query(domain_qry).each do |row|&lt;br /&gt;      @domains["#{row[0]}.#{row[1]}"] = row[2]&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    schema_qry = &lt;&lt;-EOT&lt;br /&gt;      select nspname from pg_namespace&lt;br /&gt;    EOT&lt;br /&gt;    conn.query(schema_qry).each do |row|&lt;br /&gt;      @schemas[row[0]]=row[0]&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    func_query = &lt;&lt;-EOT&lt;br /&gt;     SELECT proname AS function_name&lt;br /&gt;     , nspname AS namespace&lt;br /&gt;     , lanname AS language_name&lt;br /&gt;     , pg_catalog.obj_description(pg_proc.oid, 'pg_proc') AS comment&lt;br /&gt;     , proargtypes AS function_args&lt;br /&gt;     , proargnames AS function_arg_names&lt;br /&gt;     , prosrc AS source_code&lt;br /&gt;     , proretset AS returns_set&lt;br /&gt;     , prorettype AS return_type,&lt;br /&gt;     provolatile, proisstrict, prosecdef&lt;br /&gt;     FROM pg_catalog.pg_proc&lt;br /&gt;     JOIN pg_catalog.pg_language ON (pg_language.oid = prolang)&lt;br /&gt;     JOIN pg_catalog.pg_namespace ON (pronamespace = pg_namespace.oid)&lt;br /&gt;     JOIN pg_catalog.pg_type ON (prorettype = pg_type.oid)&lt;br /&gt;     WHERE pg_namespace.nspname !~ 'pg_catalog|information_schema'&lt;br /&gt;     AND proname != 'plpgsql_call_handler'&lt;br /&gt;     AND proname != 'plpgsql_validator'&lt;br /&gt;    EOT&lt;br /&gt;    conn.exec(func_query).result.each do |tuple|&lt;br /&gt;      func = Function.new(conn, tuple)&lt;br /&gt;      @functions[func.signature] = func &lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    rule_query = &lt;&lt;-EOT&lt;br /&gt;    select  schemaname || '.' ||  tablename || '.' || rulename as rule_name, &lt;br /&gt;            schemaname || '.' ||  tablename as tab_name,&lt;br /&gt;      rulename, definition&lt;br /&gt;    from pg_rules&lt;br /&gt;    where schemaname !~ 'pg_catalog|information_schema'&lt;br /&gt;    EOT&lt;br /&gt;    conn.exec(rule_query).result.each do |tuple|&lt;br /&gt;      @rules[tuple['rule_name']] = Rule.new(tuple['tab_name'], tuple['rulename'], tuple['definition'])&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    trigger_query =  &lt;&lt;-EOT&lt;br /&gt;    select nspname || '.' || relname as tgtable, tgname, pg_get_triggerdef(t.oid) as tg_def&lt;br /&gt;    from pg_trigger t join pg_class c ON (tgrelid = c.oid ) JOIN pg_namespace n ON (c.relnamespace = n.oid)&lt;br /&gt;    where not tgisconstraint&lt;br /&gt;    and nspname !~ 'pg_catalog|information_schema'&lt;br /&gt;    EOT&lt;br /&gt;    conn.exec(trigger_query).result.each do |tuple|&lt;br /&gt;      @triggers[tuple['tgtable'] + "." + tuple['tgname']] = Trigger.new(tuple['tgtable'], tuple['tgname'], tuple['tg_def'])&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Rule&lt;br /&gt;  attr_reader :table_name, :name, :definition&lt;br /&gt;  def initialize(table_name, name, df)&lt;br /&gt;    @table_name = table_name&lt;br /&gt;    @name = name&lt;br /&gt;    @definition = df&lt;br /&gt;  end&lt;br /&gt;  def == (other)&lt;br /&gt;    other.definition == definition&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Trigger&lt;br /&gt;  attr_reader :table_name, :name, :definition&lt;br /&gt;  def initialize(table_name, name, df)&lt;br /&gt;    @table_name = table_name&lt;br /&gt;    @name = name&lt;br /&gt;    @definition = df + ";"&lt;br /&gt;  end&lt;br /&gt;  def == (other)&lt;br /&gt;    other.definition == definition&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Function&lt;br /&gt;  def initialize(conn, tuple)&lt;br /&gt;    @name = tuple['namespace'] + "." + tuple['function_name']&lt;br /&gt;    @language = tuple['language_name']&lt;br /&gt;    @src = tuple['source_code']&lt;br /&gt;    @returns_set = tuple['returns_set']&lt;br /&gt;    @return_type = format_type(conn, tuple['return_type'])&lt;br /&gt;    @tipes = tuple['function_args'].split(" ")&lt;br /&gt;    if tuple['function_arg_names'] &amp;&amp; tuple['function_arg_names'] =~ /^\{(.*)\}$/&lt;br /&gt;      @arnames = $1.split(',')&lt;br /&gt;    elsif tuple['function_arg_names'].is_a? Array # my version of ruby-postgres&lt;br /&gt;      @arnames = tuple['function_arg_names']&lt;br /&gt;    else&lt;br /&gt;      @arnames = [""] * @tipes.length&lt;br /&gt;    end&lt;br /&gt;    alist = []&lt;br /&gt;    @tipes.each_with_index do |typ,idx|&lt;br /&gt;      alist &lt;&lt; (@arnames[idx] +" " + format_type(conn, typ))&lt;br /&gt;    end&lt;br /&gt;    @arglist = alist.join(" , ")&lt;br /&gt;    @strict = tuple['proisstrict'] ? ' STRICT' : ''&lt;br /&gt;    @secdef = tuple['prosecdef'] ? ' SECURITY DEFINER' : ''&lt;br /&gt;    @volatile = case tuple['provolatile']&lt;br /&gt;      when 'i' then ' IMMUTABLE'&lt;br /&gt;      when 's' then ' STABLE'&lt;br /&gt;      else ''&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  def signature&lt;br /&gt;    "#{@name}(#{@arglist})"&lt;br /&gt;  end&lt;br /&gt;  def definition&lt;br /&gt;    &lt;&lt;-EOT&lt;br /&gt;CREATE OR REPLACE FUNCTION #{@name} (#{@arglist}) RETURNS #{@returns_set ? 'SETOF' : ''} #{@return_type} AS $_$#{@src}$_$ LANGUAGE '#{@language}' #{@volatile}#{@strict}#{@secdef};&lt;br /&gt;EOT&lt;br /&gt;  end&lt;br /&gt;  def == (other)&lt;br /&gt;    definition == other.definition&lt;br /&gt;  end&lt;br /&gt;  def format_type(conn, oid)&lt;br /&gt;    t_query = &lt;&lt;-EOT&lt;br /&gt;    SELECT pg_catalog.format_type(pg_type.oid, typtypmod) AS type_name&lt;br /&gt;     FROM pg_catalog.pg_type&lt;br /&gt;     JOIN pg_catalog.pg_namespace ON (pg_namespace.oid = typnamespace)&lt;br /&gt;     WHERE pg_type.oid = &lt;br /&gt;    EOT&lt;br /&gt;    return conn.query(t_query + oid.to_s)[0][0]&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class Diff&lt;br /&gt; &lt;br /&gt;  def initialize(old_db_spec, new_db_spec)&lt;br /&gt;    @old_conn = PGconn.new(old_db_spec)&lt;br /&gt;    @new_conn = PGconn.new(new_db_spec)&lt;br /&gt;    @sections = [&lt;br /&gt;      :triggers_drop,&lt;br /&gt;      :rules_drop,&lt;br /&gt;      :functions_drop,&lt;br /&gt;      :indices_drop ,&lt;br /&gt;      :constraints_drop,&lt;br /&gt;      :views_drop,&lt;br /&gt;      :sequences_drop ,&lt;br /&gt;      :tables_drop ,&lt;br /&gt;      :domains_drop ,&lt;br /&gt;      :schemas_drop , &lt;br /&gt;      :schemas_create,&lt;br /&gt;      :domains_create,&lt;br /&gt;      :sequences_create,&lt;br /&gt;      :tables_create ,&lt;br /&gt;      :table_changes ,&lt;br /&gt;      :views_create ,&lt;br /&gt;      :functions_create ,&lt;br /&gt;      :rules_create ,&lt;br /&gt;      :triggers_create ,&lt;br /&gt;      :indices_create,&lt;br /&gt;      :constraints_create&lt;br /&gt;    ]&lt;br /&gt;    @script = {}&lt;br /&gt;    @sections.each {|s| @script[s] = []}&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def run_compare&lt;br /&gt;    @old_database = Database.new(@old_conn)&lt;br /&gt;    @new_database = Database.new(@new_conn)&lt;br /&gt;    compare_schemas&lt;br /&gt;    compare_domains&lt;br /&gt;    compare_sequences&lt;br /&gt;    compare_triggers_drop&lt;br /&gt;    compare_rules_drop&lt;br /&gt;    compare_views_drop&lt;br /&gt;    compare_table_attrs&lt;br /&gt;    compare_views_create&lt;br /&gt;    compare_functions&lt;br /&gt;    compare_rules_create&lt;br /&gt;    compare_triggers_create&lt;br /&gt;    compare_table_constraints&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def add_script(section, statement)&lt;br /&gt;    @script[section] &lt;&lt; statement&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def compare_schemas&lt;br /&gt;    @old_database.schemas.keys.each do |name|&lt;br /&gt;      add_script(:schemas_drop ,  "DROP SCHEMA #{name};") unless @new_database.schemas.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;    @new_database.schemas.keys.each do |name|&lt;br /&gt;      add_script(:schemas_create ,  "CREATE SCHEMA #{name};") unless @old_database.schemas.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_domains&lt;br /&gt;    @old_database.domains.keys.each do |name|&lt;br /&gt;      add_script(:domains_drop ,  "DROP DOMAIN #{name} CASCADE;") unless @new_database.domains.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;    @new_database.domains.each do |name, df|&lt;br /&gt;      add_script(:domains_create ,  "CREATE DOMAIN #{name} AS #{df};") unless @old_database.domains.has_key?(name)&lt;br /&gt;      old_domain = @old_database.domains[name]&lt;br /&gt;      if old_domain &amp;&amp; old_domain != df&lt;br /&gt;         add_script(:domains_drop, "DROP DOMAIN #{name} CASCADE;")&lt;br /&gt;         add_script(:domains_create,  "-- [changed domain] :")&lt;br /&gt;         add_script(:domains_create,  "-- OLD: #{old_domain}")&lt;br /&gt;         add_script(:domains_create,  "CREATE DOMAIN #{name} AS #{df};") &lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_sequences&lt;br /&gt;    @old_database.sequences.keys.each do |name|&lt;br /&gt;      add_script(:sequences_drop ,  "DROP SEQUENCE #{name} CASCADE;") unless @new_database.sequences.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;    @new_database.sequences.keys.each do |name|&lt;br /&gt;      add_script(:sequences_create ,  "CREATE SEQUENCE #{name};") unless @old_database.sequences.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def compare_functions&lt;br /&gt;    @old_database.functions.keys.each do |name|&lt;br /&gt;      add_script(:functions_drop ,  "DROP FUNCTION #{name} CASCADE;") unless @new_database.functions.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;    @new_database.functions.each do |name, func|&lt;br /&gt;      add_script(:functions_create ,   func.definition) unless @old_database.functions.has_key?(name)&lt;br /&gt;      old_function = @old_database.functions[name]&lt;br /&gt;      if old_function &amp;&amp; old_function.definition != func.definition&lt;br /&gt;        add_script(:functions_create , '-- [changed function] :')&lt;br /&gt;        add_script(:functions_create , '-- OLD :')&lt;br /&gt;        add_script(:functions_create ,  old_function.definition.gsub(/^/, "--&gt;  ") )&lt;br /&gt;        add_script(:functions_create ,   func.definition) &lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_rules_drop&lt;br /&gt;    @old_database.rules.each do |name, rule|&lt;br /&gt;      add_script(:rules_drop ,  "DROP RULE #{rule.name} ON #{rule.table_name} CASCADE;") unless @new_database.rules.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def compare_rules_create&lt;br /&gt;    @new_database.rules.each do |name, rule|&lt;br /&gt;      add_script(:rules_create ,   rule.definition) unless @old_database.rules.has_key?(name)&lt;br /&gt;      old_rule = @old_database.rules[name]&lt;br /&gt;      if old_rule &amp;&amp; old_rule != rule&lt;br /&gt;        add_script(:rules_drop ,  "DROP RULE #{rule.name} ON #{rule.table_name} CASCADE;") &lt;br /&gt;        add_script(:rules_create ,  "-- [changed rule] :")&lt;br /&gt;        add_script(:rules_create ,  "-- OLD: #{old_rule.definition}")&lt;br /&gt;        add_script(:rules_create ,   rule.definition )&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_triggers_drop&lt;br /&gt;    @old_database.triggers.each do |name, trigger|&lt;br /&gt;      add_script(:triggers_drop ,  "DROP trigger #{trigger.name} ON #{trigger.table_name} CASCADE;") unless @new_database.triggers.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def compare_triggers_create&lt;br /&gt;    @new_database.triggers.each do |name, trigger|&lt;br /&gt;      add_script(:triggers_create ,   trigger.definition) unless @old_database.triggers.has_key?(name)&lt;br /&gt;      old_trigger = @old_database.triggers[name]&lt;br /&gt;      if old_trigger &amp;&amp; old_trigger != trigger&lt;br /&gt;        add_script(:triggers_drop ,  "DROP trigger #{trigger.name} ON #{trigger.table_name} CASCADE;") &lt;br /&gt;        add_script(:triggers_create ,  "-- [changed trigger] :")&lt;br /&gt;        add_script(:triggers_create ,  "-- OLD #{old_trigger.definition}")&lt;br /&gt;        add_script(:triggers_create ,   trigger.definition) &lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_views_drop&lt;br /&gt;    @old_database.views.keys.each do |name|&lt;br /&gt;      add_script(:views_drop ,  "DROP VIEW #{name};") unless @new_database.views.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_views_create&lt;br /&gt;    @new_database.views.each do |name, df|&lt;br /&gt;      add_script(:views_create ,   df.definition) unless @old_database.views.has_key?(name)&lt;br /&gt;      old_view = @old_database.views[name]&lt;br /&gt;      if old_view &amp;&amp; df.definition != old_view.definition&lt;br /&gt;        add_script(:views_drop ,  "DROP VIEW #{name};")&lt;br /&gt;        add_script(:views_create ,  "-- [changed view] :")&lt;br /&gt;        add_script(:views_create ,  "-- #{old_view.definition.gsub(/\n/, ' ')}")&lt;br /&gt;        add_script(:views_create ,  df.definition)&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def compare_table_attrs&lt;br /&gt;    @old_database.tables.each do |name, table|&lt;br /&gt;      add_script(:tables_drop, "DROP TABLE #{name} CASCADE;") unless @new_database.tables.has_key?(name)&lt;br /&gt;    end&lt;br /&gt;    @to_compare = []&lt;br /&gt;    @new_database.tables.each do |name, table|&lt;br /&gt;      unless @old_database.tables.has_key?(name)&lt;br /&gt;        add_script(:tables_create ,  table.table_creation)&lt;br /&gt;        add_script(:indices_create ,  table.index_creation) unless table.indexes.empty?&lt;br /&gt;        @to_compare &lt;&lt; name&lt;br /&gt;      else&lt;br /&gt;        diff_attributes(@old_database.tables[name], table)&lt;br /&gt;        diff_indexes(@old_database.tables[name], table)&lt;br /&gt;        @to_compare &lt;&lt; name&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def compare_table_constraints&lt;br /&gt;    @c_check = []&lt;br /&gt;    @c_primary = []&lt;br /&gt;    @c_unique = []&lt;br /&gt;    @c_foreign = []&lt;br /&gt;    @to_compare.each do |name|&lt;br /&gt;      if @old_database.tables[name] &lt;br /&gt;        diff_constraints(@old_database.tables[name], @new_database.tables[name])&lt;br /&gt;      else&lt;br /&gt;        @new_database.tables[name].constraints.each do |cname, cdef|&lt;br /&gt;          add_cnstr(name,  cname, cdef) &lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    @script[:constraints_create] += @c_check&lt;br /&gt;    @script[:constraints_create] += @c_primary&lt;br /&gt;    @script[:constraints_create] += @c_unique&lt;br /&gt;    @script[:constraints_create] += @c_foreign&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def output&lt;br /&gt;    out = []&lt;br /&gt;    @sections.each do |sect|&lt;br /&gt;      if @script[sect].empty?&lt;br /&gt;         out &lt;&lt; "-- [SKIP SECTION : #{sect.to_s.upcase}] : no changes\n"&lt;br /&gt;      else&lt;br /&gt;         out &lt;&lt; "-- [START SECTION : #{sect.to_s.upcase}]"&lt;br /&gt;         out += @script[sect]&lt;br /&gt;         out &lt;&lt; "-- [END SECTION : #{sect.to_s.upcase}]\n"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    out.join("\n")&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def diff_attributes(old_table, new_table)&lt;br /&gt;    dropped = []&lt;br /&gt;    added   = []&lt;br /&gt;    changed = []&lt;br /&gt;    old_table.attributes.keys.each do |attname| &lt;br /&gt;      if new_table.has_attribute?(attname)&lt;br /&gt;        changed &lt;&lt; attname if old_table.attributes[attname] != new_table.attributes[attname]&lt;br /&gt;      else&lt;br /&gt;        dropped &lt;&lt; attname&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    new_table.attributes.keys.each do |attname|&lt;br /&gt;      added &lt;&lt; attname unless old_table.has_attribute?(attname)&lt;br /&gt;    end&lt;br /&gt;    add_script(:table_changes ,  "--  [#{old_table.name}] dropped attributes") unless dropped.empty?&lt;br /&gt;    dropped.each do |attname|&lt;br /&gt;      add_script(:table_changes ,  "ALTER TABLE #{old_table.name} DROP COLUMN #{attname} CASCADE;")&lt;br /&gt;    end&lt;br /&gt;    add_script(:table_changes ,  "--  [#{old_table.name}] added attributes") unless added.empty?&lt;br /&gt;    added.each do |attname|&lt;br /&gt;      add_script(:table_changes ,  "ALTER TABLE #{old_table.name} ADD COLUMN #{new_table.attributes[attname].definition};")&lt;br /&gt;    end&lt;br /&gt;    add_script(:table_changes ,  "--  [#{old_table.name}] changed attributes") unless changed.empty?&lt;br /&gt;    changed.each do |attname|&lt;br /&gt;      old_att = old_table.attributes[attname]&lt;br /&gt;      new_att = new_table.attributes[attname]&lt;br /&gt;      add_script(:table_changes ,  "-- attribute: #{attname}")&lt;br /&gt;      add_script(:table_changes ,  "-- OLD : #{old_att.definition}")&lt;br /&gt;      add_script(:table_changes ,  "-- NEW : #{new_att.definition}")&lt;br /&gt;      if old_att.type_def != new_att.type_def&lt;br /&gt;        add_script(:table_changes ,  "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} TYPE #{new_att.type_def};")&lt;br /&gt;      end&lt;br /&gt;      if old_att.default != new_att.default&lt;br /&gt;        if new_att.default.nil?&lt;br /&gt;          add_script(:table_changes ,  "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} DROP DEFAULT;")&lt;br /&gt;        else&lt;br /&gt;          add_script(:table_changes ,  "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} SET DEFAULT #{new_att.default};")&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;      if old_att.notnull != new_att.notnull&lt;br /&gt;        add_script(:table_changes ,  "ALTER TABLE #{old_table.name} ALTER COLUMN #{attname} #{new_att.notnull ? 'SET' : 'DROP'} NOT NULL;")&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def diff_constraints(old_table, new_table)&lt;br /&gt;    dropped = []&lt;br /&gt;    added   = []&lt;br /&gt;    &lt;br /&gt;    old_table.constraints.keys.each do |conname| &lt;br /&gt;      if new_table.has_constraint?(conname)&lt;br /&gt;        if old_table.constraints[conname] != new_table.constraints[conname]&lt;br /&gt;          dropped &lt;&lt; conname&lt;br /&gt;          added &lt;&lt; conname&lt;br /&gt;        end&lt;br /&gt;      else&lt;br /&gt;        dropped &lt;&lt; conname&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    new_table.constraints.keys.each do |conname| &lt;br /&gt;      added &lt;&lt; conname unless old_table.has_constraint?(conname)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    dropped.each do |name|  &lt;br /&gt;      add_script(:constraints_drop ,  "ALTER TABLE #{old_table.name} DROP CONSTRAINT #{name};")&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    added.each do |name|  &lt;br /&gt;      add_cnstr(old_table.name,  name, new_table.constraints[name]) &lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def add_cnstr(tablename, cnstrname, cnstrdef)&lt;br /&gt;    c_string = "ALTER TABLE #{tablename} ADD CONSTRAINT #{cnstrname} #{cnstrdef} ;"&lt;br /&gt;    case cnstrdef&lt;br /&gt;      when /^CHECK /   then @c_check  &lt;&lt; c_string&lt;br /&gt;      when /^PRIMARY / then @c_primary &lt;&lt; c_string&lt;br /&gt;      when /^FOREIGN / then @c_foreign &lt;&lt; c_string&lt;br /&gt;      when /^UNIQUE /  then @c_unique  &lt;&lt; c_string&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def diff_indexes(old_table, new_table)&lt;br /&gt;    dropped = []&lt;br /&gt;    added   = []&lt;br /&gt;    &lt;br /&gt;    old_table.indexes.keys.each do |name| &lt;br /&gt;      if new_table.has_index?(name)&lt;br /&gt;        if old_table.indexes[name] != new_table.indexes[name]&lt;br /&gt;          dropped &lt;&lt; name&lt;br /&gt;          added &lt;&lt; name&lt;br /&gt;        end&lt;br /&gt;      else&lt;br /&gt;        dropped &lt;&lt; name&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    new_table.indexes.each do |name| &lt;br /&gt;      added &lt;&lt; name unless old_table.has_index?(name)&lt;br /&gt;    end&lt;br /&gt;   &lt;br /&gt;    dropped.each do |name|  &lt;br /&gt;      add_script(:indices_drop ,  "DROP INDEX #{name};")&lt;br /&gt;    end&lt;br /&gt;    added.each do |name|&lt;br /&gt;      add_script(:indices_create ,  (new_table.indexes[name] + ";")) if new_table.indexes[name]&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def parse_conn_params(str)&lt;br /&gt;  h = {}&lt;br /&gt;  str.split(/:/).each{|pair| key, value = pair.split('=', 2); h[key]=value}&lt;br /&gt;  h&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;  diff = PostgreSqlSchema::Diff.new(parse_conn_params(ARGV[0]), parse_conn_params(ARGV[1]) )&lt;br /&gt;  diff.run_compare&lt;br /&gt;  puts diff.output&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 08 Dec 2005 01:07:07 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/949</guid>
      <author>dseverin ()</author>
    </item>
  </channel>
</rss>
