Thinking on this for a moment, perhaps better than `pg_get_indexdef` is pulling column ordinals out of the `indexprs` column noted in the first answer, and specifically the `varattno` field. `regexp_matches` for that (and only that :)) I threw together the following, going back to the definition in the `columns` table of `information_schema`: ``` SELECT (SELECT JSON_AGG(columns.column_name) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = i.indrelid::regclass::text AND ordinal_position IN (SELECT matches[1]::INTEGER FROM regexp_matches(i.indexprs::TEXT, 'varattno (\d)', 'g') as matches)) AS argument_columns , pg_get_indexdef(att.attrelid, att.attnum, true) , i.indrelid::regclass::text AS table , c.relname AS index_nameq , i.indisunique AS is_unique , att.attname as column_names FROM pg_catalog.pg_namespace n JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid JOIN pg_catalog.pg_attribute att ON att.attrelid = c.oid JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid WHERE n.nspname !~ '^pg_' AND c.relkind IN ('r', 't', 'i') ``` and that produces: | argument\_columns |pg\_get\_indexdef |table |index\_nameq |is\_unique |column\_names | | --- | --- | --- | --- | --- | --- | | \["content"\] |"substring"\(content, 1, 5\) |file\_lookup\_4k |date5\_index |0 |substring | | \["file\_id", "sequence\_no"\] |abs\(file\_id \- sequence\_no\) |file\_lookup\_4k |date6\_index |0 |abs | | \["file\_id"\] |abs\(file\_id\) |file\_lookup\_4k |date2\_index |0 |abs | | \["sequence\_no"\] |round\(sequence\_no::double precision\) |file\_lookup\_4k |date3\_index |0 |round | | \["sequence\_no"\] |round\(sequence\_no::double precision\) |file\_lookup\_4k |date4\_index |0 |round | I feel that this is far more in line with what we would both want. Note that I joined only to the `attname` table, because that is really all I need