GBase 8c schema 和 search_path 引发的对象定位问题
我最近整理 GBase 8c 的开发使用资料时,发现 schema 相关问题很适合单独拿出来讲。很多 SQL 报错看起来像"表不存在"或"权限不足",但真正查下去,根因可能是当前 schema 不对、search_path 不对、对象建到了 $user 模式下,或者同名对象被优先级更高的 schema 命中了。
GBase 8c 文档里提到,默认情况下新的数据库对象会创建在 $user 模式下。这个细节如果没有意识到,就很容易出现一种情况:开发以为表建在公共 schema,实际表在自己用户名对应的 schema;另一个账号执行同样 SQL 时,就找不到对象。schema 本来是为了对象隔离和命名管理,但如果缺少规范,也会变成排查成本。
表不存在,不一定是真的不存在
现场常见报错是:
text
relation "order_day" does not exist
很多人看到这个报错会马上查表有没有建。我的习惯是先问:在哪个数据库、哪个用户、哪个 schema 下执行的?同一个表名,如果 schema 不同,结果完全不同。
sql
-- 查看当前数据库和用户
SELECT current_database(), current_user;
-- 查看当前 search_path
SHOW search_path;
-- 查找同名对象在哪些 schema 中存在
SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = 'order_day'
ORDER BY schemaname;
如果能查到对象存在,但不在当前 search_path 里,那么 SQL 不写 schema 前缀就可能找不到。更稳的写法是明确 schema:
sql
SELECT COUNT(*)
FROM ods.order_day;
我自己更倾向于在生产 SQL 里写完整对象名,尤其是调度任务、报表任务、数据服务接口。开发临时查询可以依赖 search_path,生产链路尽量不要依赖隐式路径。
search_path 是对象定位规则,不是装饰项
search_path 决定了没有写 schema 前缀时,数据库按什么顺序查找对象。这个参数看起来只是一个默认路径,但它会直接影响 SQL 命中哪个对象。
sql
SHOW search_path;
SET search_path TO app_dw, public;
SELECT * FROM order_day LIMIT 10;
如果 app_dw.order_day 和 public.order_day 同时存在,当前 SQL 会优先命中 app_dw。这在多环境、多团队共用数据库时很危险。同名表、同名视图、同名函数都可能引发误判。
| 场景 | 风险 | 建议 |
|---|---|---|
| 多 schema 有同名表 | SQL 命中非预期对象 | 生产 SQL 写 schema 前缀 |
| 用户级 search_path 不同 | 不同账号结果不同 | 统一业务账号配置 |
| 应用连接初始化 SET | DBA 验证和应用表现不同 | 查连接池初始化 SQL |
| 临时调试改 search_path | 后续会话误用 | 用完 RESET 或新建连接 |
我排查对象定位问题时,会让业务用"真实执行账号"连库验证。管理员账号能查到,不代表业务账号也能查到;手工 gsql 能查到,也不代表应用连接池里能查到。
对象创建时要明确 schema
很多问题是从建表那一刻埋下的。比如开发执行:
sql
CREATE TABLE order_day (
order_id bigint,
order_day date,
pay_amt numeric(18,2)
);
如果当前 search_path 里默认 schema 是用户自己的 schema,表就会建到那里,而不是团队以为的 ods 或 app_dw。后续调度账号查 ods.order_day 找不到,或者直接查 order_day 命中另一张表,问题就出来了。
我更建议在 DDL 中明确 schema:
sql
CREATE SCHEMA IF NOT EXISTS ods;
CREATE TABLE ods.order_day (
order_id bigint,
order_day date,
pay_amt numeric(18,2)
);
同时,授权也要跟 schema 对齐:
sql
GRANT USAGE ON SCHEMA ods TO rpt_reader;
GRANT SELECT ON ods.order_day TO rpt_reader;
很多权限问题不是表没授权,而是 schema 没有 USAGE 权限。业务账号没有 schema 使用权限时,即使表权限看起来给了,也可能访问失败。
用户级和库级配置要纳入排查
search_path 可以在会话里设置,也可能通过用户级或数据库级配置影响新连接。排查时只看当前会话,有时候不够。
sql
-- 查看数据库级配置
SELECT datname, datconfig
FROM pg_database
WHERE datconfig IS NOT NULL;
-- 查看用户级配置
SELECT rolname, rolconfig
FROM pg_roles
WHERE rolconfig IS NOT NULL;
如果发现某个业务用户配置了固定 search_path,就要确认它是否符合当前发布规范。比如原来对象都在 public,后来迁到 app_dw,但用户级配置没改,应用就会继续按旧路径找对象。
配置示例:
sql
ALTER USER rpt_reader SET search_path TO app_dw, public;
ALTER DATABASE reportdb SET search_path TO app_dw, public;
我不建议随意在多个层级同时设置。最好明确一套规则:生产调度账号统一用户级 search_path,关键 SQL 仍写 schema 前缀;临时开发账号可以更灵活,但不能影响正式链路。
同名函数也会带来隐性问题
schema 问题不只影响表,也影响函数。比如两个 schema 下都有同名函数,参数类型又能匹配,SQL 可能调用到非预期版本。
sql
SELECT routine_schema, routine_name
FROM information_schema.routines
WHERE routine_name = 'format_channel_code';
如果业务 SQL 里写:
sql
SELECT format_channel_code(channel_code)
FROM app_dw.order_day;
到底调用哪个函数,就要看路径和解析规则。生产里我更建议对关键自定义函数写 schema 前缀:
sql
SELECT dim.format_channel_code(channel_code)
FROM app_dw.order_day;
这看起来啰嗦,但在多人协作和长期维护里更稳。
我会固定保留的排查 SQL
sql
-- 当前身份和路径
SELECT current_database(), current_user;
SHOW search_path;
-- 查同名表
SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = '目标表名'
ORDER BY schemaname;
-- 查同名视图
SELECT table_schema, table_name
FROM information_schema.views
WHERE table_name = '目标视图名'
ORDER BY table_schema;
-- 查 schema 权限相关对象
SELECT nspname
FROM pg_namespace
WHERE nspname NOT LIKE 'pg_%'
ORDER BY nspname;
如果是应用侧问题,我还会加一条:请应用把连接初始化 SQL 打出来。很多时候应用框架会自动执行 SET search_path,数据库侧手工验证不一定能复现。
常见坑
| 常见坑 | 表现 | 处理建议 |
|---|---|---|
| 建表不写 schema | 表建到非预期 schema | DDL 统一写完整对象名 |
| 只授权表不授权 schema | 表权限看似有,访问仍失败 | 补 GRANT USAGE ON SCHEMA |
| 多 schema 同名对象 | 同样 SQL 不同账号结果不同 | 生产 SQL 写 schema 前缀 |
| 用户级 search_path 旧值 | 应用找旧对象 | 查 pg_roles.rolconfig |
| 应用连接池重设路径 | DBA 手工验证正常,应用异常 | 查初始化 SQL |
| 函数未加 schema 前缀 | 调用到旧函数 | 关键函数写完整名 |
结尾总结
GBase 8c 的 schema 和 search_path 本身是对象管理能力,不是问题来源。但如果没有命名和授权规范,它们很容易让"表不存在""权限不足""结果不一致"变得难排查。我的经验是,生产 DDL 和核心 SQL 尽量写完整 schema,业务账号的 search_path 统一管理,权限同时覆盖 schema 和对象。
排查时不要只问表有没有,而要问表在哪个 schema、当前账号路径是什么、是否存在同名对象、应用连接是否改过路径。把这些问题问清楚,对象定位类问题通常很快就能收敛。
参考资料
text
[1] GBase 8c 数据库使用 https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8
[2] GBase 8c GUC参数说明 https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/GUC%E8%BF%90%E8%A1%8C%E5%8F%82%E6%95%B0
[3] GBase 社区优质文章区 https://www.gbase.cn/community/section/11