使用 Postgres 9.1.4 和 PostGIS 1.5,作为 Rails 3.2.x 应用程序的一部分。
我有一个包含模式 A 和 B 的数据库,模式 A 为空,模式 B 包含表 foo 和 bar,在非 PK 字段中的每个表上都有索引,例如email.
如果我用 设置我的模式搜索路径SET search_path TO A,B;,那么我可以运行:
CREATE TABLE foo (...);
Run Code Online (Sandbox Code Playgroud)
并在模式 A 中创建表 foo。但以下迁移失败:
CREATE TABLE bar (...);
CREATE UNIQUE INDEX index_bar_on_email ON bar USING btree (email);
Run Code Online (Sandbox Code Playgroud)
和
Index name 'index_bar_on_email' on table 'bar' already exists
Run Code Online (Sandbox Code Playgroud)
这表明create table和create unique index处理搜索路径的方式不同。有没有办法哄 PG 在模式 A 中创建索引?
更新
这个问题似乎与 Rails 如何与 Postgres 接口有关。创建 Users 表后,在 Rails 尝试创建索引之前运行以下查询:
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 = 'users'
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN ('B','A') )
AND i.relam = am.oid
ORDER BY i.relname
SELECT a.attnum, a.attname, t.typname
FROM pg_attribute a, pg_type t
WHERE a.attrelid = 311384
AND a.attnum IN (2)
AND a.atttypid = t.oid
Run Code Online (Sandbox Code Playgroud)
基于其中一个或两个的结果,这似乎是与模式无关的查询,我猜测非空返回告诉 Rails 这个索引已经存在,所以它甚至不费心去尝试。
现在,我只想重命名索引,但这可能是 Rails Postgres 适配器中的一个错误。
鉴于以下设置:
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
Run Code Online (Sandbox Code Playgroud)
我无法重现您在 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
Run Code Online (Sandbox Code Playgroud)
但是,与使用 相比search_path,使用显式模式限定更安全。例如,我将上面的内容重写为:
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);
Run Code Online (Sandbox Code Playgroud)
索引是在其关联表的架构中自动创建的;看:
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)
Run Code Online (Sandbox Code Playgroud)
根据问题变化更新:
是的,您所展示的确实看起来像是 Rails 适配器问题。它正在检查索引是否存在于任何架构中。应该检查 中给定名称的第一个表是否search_path具有命名索引。
我会以不同的方式编写查询。我会pg_class完全放弃加入,而是使用演员表来为我regclass处理search_path解决方案。我会使用生成的 oid 来搜索索引。比较原始,然后更新,如下。请注意,更新的查询确实需要search_path首先设置。
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)
Run Code Online (Sandbox Code Playgroud)