Basics
The system catalog information function pg_get_indexdef(index_oid) returns index definitions.
Use the type regclass to simplify input. For a table called tbl in the public schema:
SELECT pg_get_indexdef(indexrelid) || ';' AS idx
FROM pg_index
WHERE indrelid = 'public.tbl'::regclass; -- optionally schema-qualified
This includes all indexes: PK, partial, functional, unique, with special operator classes, etc.
Details for regclass:
Prepare DDL statements
Replace the old table name in the DDL script with the new one. But avoid false positives in the replacement, like a column name matching the table name.
With default names, index definitions look like this:
CREATE INDEX tbl_tbl_id_idx ON tbl USING btree (tbl_id);
CREATE INDEX tbl_people_gin_idx ON tbl
USING gin (((data -> 'people'::text)) jsonb_path_ops);
CREATE INDEX tbl_comecol_nonull_idx ON tbl
USING btree (somecol) WHERE (cutblade IS NOT NULL);
Note the 3 false positives, which we do not want to replace.
This query should work flawlessly - but verify that against your actual indexes yourself!
SELECT regexp_replace(regexp_replace(
pg_get_indexdef(indexrelid)
, 'tbl', 'tbl1')
, ' ON tbl ', ' ON tbl1 ')
|| ';' AS idx
FROM pg_index
WHERE indrelid = 'public.tbl'::regclass;
The 1st regexp_replace() only replaces the first match in the name (unless instructed otherwise).
The 2nd regexp_preplace() is more specific and only replaces the actual table name.
For the given examples you get:
CREATE INDEX tbl1_tbl_id_idx ON tbl1 USING btree (tbl_id);
CREATE INDEX tbl1_people_gin_idx ON tbl1
USING gin (((data -> 'people'::text)) jsonb_path_ops);
CREATE INDEX tbl1_somecol_nonull_idx ON tbl1
USING btree (somecol) WHERE (cutblade IS NOT NULL);
But we have not yet considered non-standard table names, schemas or the search_path setting. All of that is built into the function below.
Full automation
If you trust your naming scheme ...
CREATE OR REPLACE FUNCTION f_copy_idx(_tbl text, _tbl1 text
, _sch text = 'public', _sch1 text = 'public')
RETURNS void
LANGUAGE plpgsql SET search_path = '' AS
$func$
DECLARE
_full_tbl text := format('%I.%I', _sch, _tbl);
_full_tbl1 text := format('%I.%I', _sch1, _tbl1);
_sql text;
BEGIN
SELECT string_agg(
regexp_replace(regexp_replace(
pg_get_indexdef(indexrelid)
, _tbl, _tbl1)
, ' ON ' || _full_tbl || ' ', ' ON ' || _full_tbl1 || ' ')
, '; ')
FROM pg_index
WHERE indrelid = _full_tbl::regclass
INTO _sql;
IF _sql IS NULL THEN
RAISE WARNING 'Found no indexes to copy.';
ELSE
-- RAISE NOTICE '%', _sql; -- debug?
EXECUTE _sql;
END IF;
END
$func$;
Works for any schemas, and names that require double-quoting.
The function sets an empty search_path within its scope (SET search_path = '') to force schema-qualified table names.
I added 'public' as default value for the schema parameters _sch and _sch1. If both tables reside in the public schema, you can simply omit schema input.
Call:
SELECT f_copy_idx('mytbl', 'newtbl'); -- in schema public
For different schemas and with table names requiring double-quoting:
SELECT f_copy_idx('old_TBL', 'table', 'public', 'New_SCHEmA');
fiddle
Old sqlfiddle