Given the following setup:
regress=> CREATE SCHEMA A;
CREATE SCHEMA
regress=> CREATE SCHEMA B;
CREATE SCHEMA
regress=> SET search_path = B, public;
SET
regress=> CREATE TABLE bar(email text);
CREATE TABLE
CREATE UNIQUE INDEX index_bar_on_email ON bar USING btree (email);
CREATE INDEX
I cannot reproduce the problem you report in PostgreSQL 9.2:
regress=> SET search_path = A, B;
SET
regress=> CREATE TABLE bar(email text);
CREATE TABLE
regress=> CREATE UNIQUE INDEX index_bar_on_email ON bar USING btree (email);
CREATE INDEX
However, rather than using the search_path
, it's safer to use explicit schema-qualification. For example, I'd re-write the above as:
regress=> RESET search_path;
RESET
regress=> SHOW search_path ;
search_path
----------------
"$user",public
(1 row)
CREATE TABLE B.bar(email text);
CREATE UNIQUE INDEX b.index_bar_on_email ON b.bar USING btree (email);
CREATE TABLE A.bar(email text);
CREATE UNIQUE INDEX index_bar_on_email ON A.bar USING btree (email);
The indexes are automatically created in the schema of their associated table; see:
regress=> \di B.
List of relations
Schema | Name | Type | Owner | Table
--------+--------------------+-------+-------+-------
b | index_bar_on_email | index | craig | bar
(1 row)
regress=> \di A.
List of relations
Schema | Name | Type | Owner | Table
--------+--------------------+-------+-------+-------
a | index_bar_on_email | index | craig | bar
(1 row)
Update based on question change:
Yes, what you've shown does look like a Rails adapter issue. It's checking to see whether the index exists in any schema. It should be checking to see whether the first table of the given name in the search_path
has the named index.
I would write the query differently. I'd leave off the join on pg_class
entirely, instead using a cast to regclass
to handle search_path
resolution for me. I'd use the resulting oid to search for the index. Compare original, then updated, below. Note that the updated query does require search_path
to be set first.
regress=> SELECT DISTINCT i.relname, d.indisunique, d.indkey, t.oid, am.amname
FROM pg_class t, pg_class i, pg_index d, pg_am am
WHERE i.relkind = 'i'
AND d.indexrelid = i.oid
AND d.indisprimary = 'f'
AND t.oid = d.indrelid
AND t.relname = 'bar'
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN ('b','a') )
AND i.relam = am.oid
ORDER BY i.relname
;
relname | indisunique | indkey | oid | amname
--------------------+-------------+--------+-------+--------
index_bar_on_email | t | 1 | 28585 | btree
index_bar_on_email | t | 1 | 28592 | btree
(2 rows)
regress=> SELECT DISTINCT i.relname, d.indisunique, d.indkey, 'bar'::regclass::oid, am.amname
FROM pg_class i, pg_index d, pg_am am
WHERE i.relkind = 'i'
AND d.indexrelid = i.oid
AND d.indisprimary = 'f'
AND 'bar'::regclass = d.indrelid
AND i.relam = am.oid
ORDER BY i.relname
;
relname | indisunique | indkey | regclass | amname
--------------------+-------------+--------+----------+--------
index_bar_on_email | t | 1 | 28585 | btree
(1 row)
Generally, refreshing the MV immediately only seems reasonable if write access to underlying tables is a rare event. A statement-level trigger is better than a row-level trigger, but may still prove too much for big tables. I would consider a solution that polls the database every n minutes checking for updates. You could have a trigger write to a table with a single row.
Or you could use LISTEN
/ NOTIFY
.
As long as you want to propagate every change (almost) immediately, you may be better off with row-level triggers on insert / update / delete writing to a hand-knit "materialized view" (a table), like @trygvis suggested.
Since pgbouncer keeps sessions open, there may be locking issues with uncommitted transactions slowing the process down, but I am merely speculating here. You could run tests in a copy of your database without pgbouncer. Or check for zombi transactions by looking up pg_stat_activity
.
Best Answer
That's like asking Microsoft or GNU.org to remove PATH from the command line. Ain't gonna happen.
Use fully qualified names or always run
ALTER ROLE <your_login_role> SET search_path TO a,b,c;
at the beginning of your session. I'm not sure if you can run this with psqlrc but you could try.You can view your search_path with
show search_path
You could write a cron or pgagent job to run often and check information_schema for views and tables with the same name