/* ********** *

[Install]
  psql シェルでコピー＆ペーストするなどしてください。
  copy & paste in psql shell.

[Usage]
  SELECT show_create_table('table_name', 'schema_name');
    or
  SELECT show_create_table('table_name', null);
    schema_name を null にすると、全スキーマが検索の対象になります。
    If schema_name = null, all of the schema will be searched.

[Note]
  このファンクションは、SQL文1つから構成されます。
  postgreSQL の設定変更なしに利用することができます。
  This function is written in SQL.
  You do not need to re-configure your postgreSQL settings.

[Available versions]
  v8.3.8
  v8.x (maybe...)

  上記以外のバージョンで動作した場合、ご報告頂けると有り難いです。
  If this function successfully runs on your postgreSQL, please tell me your version!
    http://sourceforge.jp/forum/forum.php?forum_id=23996

 * ********** */
CREATE OR REPLACE FUNCTION show_create_table(varchar /*table*/, varchar /*schema*/) returns text as $$
/*
 * show create table for PostgreSQL v0.1
 *   2010.8.16 Tadashi Murakami
 *
 * Bug reports are welcome:
 *   http://sourceforge.jp/projects/pgshowcreatetbl/
 *
 * Licensed under the Apache License, Version 2.0.
 * You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 */
SELECT 'CREATE TABLE ' || $1 || ' (' || chr(10) || array_to_string(ARRAY(
SELECT '  "'||"Column" ||'" '|| "Type" || "Modifiers" || "Index" FROM (
SELECT at.attnum, ns.nspname as schema, tb.relname as table, at.attname as "Column",
       /* Type */
       CASE WHEN at.attinhcount<>0 OR at.attisdropped THEN null
            ELSE
            CASE WHEN tp.typname = 'int2' THEN 'smallint'
                 WHEN tp.typname = 'int4' THEN 'integer'
                 WHEN tp.typname = 'int8' THEN 'bigint'
                 WHEN tp.typname = 'float4' THEN 'real'
                 WHEN tp.typname = 'float8' THEN 'double precision'
                 WHEN tp.typname = 'bpchar' THEN 'char'
                 ELSE tp.typname
             END ||
            CASE WHEN at.attlen >= 0        THEN ''
                 WHEN at.atttypmod < 4      THEN ''
                 WHEN tp.typname<>'numeric' THEN '('|| at.atttypmod-4 ||')'
                 WHEN (at.atttypmod & 65535) = 4 THEN '('|| (at.atttypmod >> 16) ||')'
                 ELSE '('|| (at.atttypmod >> 16) ||','|| (at.atttypmod & 65535)-4 ||')'
             END
        END as "Type",
       /* Modifiers */
       CASE WHEN at.attnotnull THEN ' not null' ELSE ''
        END ||
       CASE WHEN ad.adbin IS NULL THEN ''
            ELSE ' default ' || pg_get_expr(ad.adbin, tb.oid)
        END as "Modifiers",
       /* one-column Index */
       CASE WHEN ix.indexrelid IS NULL THEN ''
            ELSE
            CASE WHEN ix.indisprimary THEN ' PRIMARY KEY'
                 WHEN ix.indisunique  THEN ' UNIQUE'
                 ELSE ' /* index */'
             END ||
            CASE WHEN am.amname <> 'btree' THEN ' /*'||am.amname||'*/' ELSE ''
             END
            || ' /* '|| ic.relname ||' */'
        END as "Index"
--     , tb.*, at.*, tp.*
  FROM pg_class tb
--
-- for Type, Modifiers
--
 INNER JOIN pg_attribute at ON tb.oid = at.attrelid
 INNER JOIN pg_type      tp ON at.atttypid = tp.oid
  LEFT OUTER JOIN pg_attrdef ad ON ad.adrelid = tb.oid AND ad.adnum = at.attnum
 INNER JOIN pg_namespace ns ON tb.relnamespace = ns.oid
--
-- for one-column Index
--
  LEFT OUTER JOIN pg_index ix ON ix.indrelid = tb.oid AND ix.indnatts = 1 AND at.attnum = ix.indkey[0]
  LEFT OUTER JOIN pg_class ic ON ix.indexrelid = ic.oid
  LEFT OUTER JOIN pg_am    am ON ic.relam = am.oid
--
 WHERE at.attnum >= 1
   AND tb.relname = $1
   AND ($2 IS NULL OR ns.nspname = $2)
) as columns ORDER BY attnum), ','||chr(10))
--
--
-- multi-columns Index
--
--
|| (SELECT
CASE WHEN count(*) = 0 THEN ''
     ELSE ','||chr(10)||'  '||
-- SELECT
  array_to_string(ARRAY(
  SELECT CASE WHEN indisprimary THEN 'PRIMARY KEY ' ELSE 'UNIQUE ' END
      || substr(indexdef, strpos(indexdef, '('), strpos(indexdef, ')') - strpos(indexdef, '(') + 1)
      || ' /* '||index||' */'
   FROM (
  SELECT ic.relname as index, ns.nspname as schema, tb.relname as table -- , ia.attname, ia.attnum
       , ix.indnatts, ix.indisunique, ix.indisprimary, am.amname
       , ix.indkey
       , pg_get_indexdef(ic.oid) as indexdef
  --     , pg_get_expr(ix.indexprs, ix.indrelid), pg_get_expr(ix.indpred, ix.indrelid)
    FROM pg_class tb
   INNER JOIN pg_index ix ON ix.indrelid = tb.oid
   INNER JOIN pg_class ic ON ix.indexrelid = ic.oid
   INNER JOIN pg_am    am ON ic.relam = am.oid
  --  LEFT OUTER JOIN pg_attribute ia ON ia.attnum = any(ix.indkey) AND ia.attrelid = tb.oid
   INNER JOIN pg_namespace ns ON tb.relnamespace = ns.oid
   WHERE ix.indnatts > 1
     AND (ix.indisprimary OR ix.indisunique)
     AND tb.relname = $1
     AND ($2 IS NULL OR ns.nspname = $2)
  ) as def ORDER BY indisprimary desc, index), ','||chr(10))
 END
-- copy from the above conditions
  FROM pg_class tb
 INNER JOIN pg_index ix ON ix.indrelid = tb.oid
 INNER JOIN pg_namespace ns ON tb.relnamespace = ns.oid
 WHERE ix.indnatts > 1
   AND (ix.indisprimary OR ix.indisunique)
   AND tb.relname = $1
   AND ($2 IS NULL OR ns.nspname = $2)
)||
chr(10) || ')'
$$ language sql;
