GBase 8c 里 search_path 没管住,SQL 可能跑到另一个对象上

GBase 8c 里 search_path 没管住,SQL 可能跑到另一个对象上

我最近复盘 GBase 8c 开发规范时,发现 search_path 是一个很容易被低估的点。很多环境上线初期只有一个业务 schema,大家写 SQL 时表名前面不带 schema 也能跑。等系统拆出多个业务域、多个租户、多个临时 schema 后,同名表、同名函数、同名视图一多,SQL 的解析路径就成了隐患。

这种问题现场不一定表现为报错,更麻烦的是"能跑,但跑错对象"。比如开发环境查的是 public.customer_info,生产环境会话的 search_path 被应用改过,最后查到 crm.customer_info。结果不是语法错,而是数据口径错。

search_path 影响的是对象解析

我自己理解下来,search_path 就是数据库在遇到未带 schema 的对象名时,按什么顺序去找对象。表、视图、函数、类型都可能受影响。

sql 复制代码
SHOW search_path;

SET search_path TO crm, public;

SELECT * FROM customer_info LIMIT 10;

上面这条查询没有写 schema,数据库会按当前路径去解析 customer_info。如果 crmpublic 下都有同名对象,解析结果就取决于路径顺序。

写法 风险 我更倾向的方式
SELECT * FROM customer_info 依赖当前会话路径 核心 SQL 带 schema
SELECT * FROM crm.customer_info 对象明确 推荐用于生产 SQL
函数里直接访问表名 函数执行时路径变化可能影响结果 函数内限定 schema 或设置路径
应用连接后随意 SET search_path 连接池复用时影响下个请求 连接初始化统一处理

开发阶段省几个字符,后期可能换来很高的排查成本。尤其是连接池场景,会话不是请求结束就销毁,路径状态可能被复用。

先查库里有哪些重名对象

遇到"同样 SQL 在不同环境结果不一致",我会先查重名对象。这个动作很简单,但经常能直接定位问题。

sql 复制代码
-- 查不同 schema 下的同名表或视图
SELECT
    relname,
    string_agg(nspname, ', ' ORDER BY nspname) AS schemas,
    count(*) AS object_cnt
FROM (
    SELECT n.nspname, c.relname
    FROM pg_class c
    JOIN pg_namespace n ON n.oid = c.relnamespace
    WHERE c.relkind IN ('r', 'v', 'm')
      AND n.nspname NOT IN ('pg_catalog', 'information_schema')
) t
GROUP BY relname
HAVING count(*) > 1
ORDER BY object_cnt DESC, relname;

函数也要查。函数重载、参数类型、schema 路径叠在一起时,排查会比表复杂。

sql 复制代码
SELECT
    p.proname AS function_name,
    string_agg(n.nspname, ', ' ORDER BY n.nspname) AS schemas,
    count(*) AS function_cnt
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
GROUP BY p.proname
HAVING count(*) > 1
ORDER BY function_cnt DESC, function_name;

如果结果里出现大量同名对象,就说明环境已经不适合依赖默认路径了。

连接池里最容易留下脏状态

应用使用连接池后,一个物理连接会服务多个请求。某个请求执行了 SET search_path TO tenant_a, public,如果没有恢复,后续请求可能沿用这个路径。问题不一定马上暴露,因为只要对象名存在,SQL 就会正常执行。

我更倾向于在连接初始化时固定路径,并禁止业务代码临时改全局会话状态。如果确实需要租户路径,也要在请求结束时恢复。

sql 复制代码
-- 连接初始化阶段设置
SET search_path TO app_core, public;

-- 或者更保守,生产核心 SQL 全部带 schema
SELECT order_id, order_status
FROM app_core.order_base
WHERE order_id = 10086;

如果应用确实要动态切 schema,至少要把它写成明确的请求上下文,而不是散落在 SQL 片段里。

场景 建议
单业务 schema 可以设置固定 search_path,但核心 SQL 仍建议显式 schema
多租户按 schema 隔离 连接获取后设置,释放前重置
存储过程内部访问业务表 函数内写完整对象名
报表平台拼 SQL 禁止无 schema 的跨域对象访问
临时排查会话 执行前先 SHOW search_path

函数里的路径更要谨慎

函数和存储过程里如果使用未限定表名,后续迁移 schema、同名对象增加、调用方路径变化,都可能让行为变得难以预期。我的习惯是在函数里把核心对象写完整。

sql 复制代码
CREATE OR REPLACE FUNCTION app_core.get_customer_level(p_customer_id bigint)
RETURNS varchar
AS $$
DECLARE
    v_level varchar(32);
BEGIN
    SELECT customer_level
      INTO v_level
      FROM app_core.customer_info
     WHERE customer_id = p_customer_id;

    RETURN v_level;
END;
$$ LANGUAGE plpgsql;

如果函数必须依赖特定路径,也要在创建和评审时明确写出来,不要让它隐含在数据库默认值里。

权限隔离不能只靠 schema 名字

有些系统把 schema 当成天然隔离边界,但如果权限给得太宽,用户仍然可以访问不该访问的对象。对象路径和权限是两件事:路径决定先找谁,权限决定能不能用。

sql 复制代码
-- 示例:给应用用户授予指定 schema 的使用权
GRANT USAGE ON SCHEMA app_core TO app_user;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA app_core TO app_user;

-- 限制 public 下随意创建对象
REVOKE CREATE ON SCHEMA public FROM PUBLIC;

我在现场更关注 public schema。很多库默认对象都堆在 public,下游人员也习惯把临时表、测试函数放进去。时间一长,public 就从公共空间变成了风险集合。

上线前可以加一个扫描脚本

为了减少对象解析风险,我会在上线检查里加几类扫描。

sql 复制代码
-- 检查业务 SQL 中可能遗漏 schema 的高风险对象,示例只查重名表
SELECT relname
FROM (
    SELECT c.relname, count(*) AS cnt
    FROM pg_class c
    JOIN pg_namespace n ON n.oid = c.relnamespace
    WHERE c.relkind IN ('r','v','m')
      AND n.nspname NOT IN ('pg_catalog','information_schema')
    GROUP BY c.relname
) x
WHERE cnt > 1;

-- 检查当前会话路径
SHOW search_path;

如果有代码仓库,可以配合静态扫描,把 FROM 表名JOIN 表名 这类未带 schema 的 SQL 片段标出来。不是所有 SQL 都要立刻改,但核心链路和跨 schema 查询要优先处理。

我会采用的命名和使用规范

规范 说明
业务 schema 不用 public 承载核心表 public 只保留必要公共对象
生产 SQL 优先写完整对象名 降低路径依赖
不同业务域避免同名核心表 即使 schema 不同,也会增加误读
函数内部访问表要带 schema 避免调用方路径影响
连接池初始化统一设置路径 不让请求自己决定默认路径
临时对象命名带前缀和批次 防止和业务对象混淆

GBase 8c 的 schema 机制本身很灵活,适合做对象分层、权限隔离、业务域拆分。但灵活性也意味着需要规范。我的感受是,search_path 管得越早,后期迁移、拆库、权限改造时越轻松。

参考资料

text 复制代码
GBase 8c SQL参考指南 https://www.gbase.cn/docs/gbase-8c/05%20SQL%E5%8F%82%E8%80%83/SQL%E8%AF%AD%E6%B3%95
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/%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%BC%8F
GBase 8c 文档介绍 https://www.gbase.cn/docs/gbase-8c/%E6%AC%A2%E8%BF%8E/
相关推荐
Omics Pro几秒前
「自兹以往」动物肠道微生物组
数据库·人工智能·机器学习·语言模型·自然语言处理
zzz_23688 分钟前
【Redis】分布式锁完整演进
数据库·redis·分布式
mN9B2uk1729 分钟前
数据库的约束简介
java·数据库·sql
计算机安禾31 分钟前
【数据库系统原理】第4篇:关系数据结构的形式化定义:域、笛卡尔积与关系模式
数据结构·数据库·算法
Henry-SAP31 分钟前
SAP(ERP) BOM变更实时同步MRP方案
数据库·云原生
AI人工智能+电脑小能手33 分钟前
【大白话说Java面试题 第99题】【Mysql篇】第29题:如何选择合适的分布式主键方案?
java·数据库·分布式·mysql·面试
花酒锄作田44 分钟前
DeepAgents - 使用Postgres作为Checkpoint
postgresql·deepagents
倔强的石头_2 小时前
kingbase备份与恢复实战(七)—— 恢复演练与验收:从“能恢复”到“可交付预案”
数据库
满昕欢喜2 小时前
第2章 SQL Server 2019服务器管理
数据库·sqlserver
giaz14n9X2 小时前
Redis 分布式锁进阶第五十一篇
数据库·redis·分布式