一、我以为我是“半个小 DB”
刚接手项目时,我挺自信。我能 SELECT、INSERT、UPDATE、DELETE 业务表,还能建表、改表结构,就觉得差不多是“半个 DBA”了。
直到我写了人生中第一个存储过程,兴冲冲地执行:
CREATE OR REPLACE PROCEDURE p_test IS
BEGIN
NULL;
END;
/Oracle 冷冷地回了一句:
ORA-01031: insufficient privileges我愣住了:我能改表数据,居然连一个“什么也不干”的过程都建不了?
后来才明白,表权限和代码权限是两套东西。我能对表做增删改查,但 CREATE PROCEDURE 是另一回事。我联系 DBA,他只回了一句:
“正式表都在 OADBA 下,你别乱建过程。”那一刻,我觉得自己像个被没收了工具的装修工。
二、我在自己账号下测试,结果改错了表
DBA 不让我在 OADBA 下建过程,我就在自己的账号 A 下建。表里写的是:
SELECT * FROM yy010;我想当然地觉得,既然我一直操作 OADBA.YY010,Oracle 总该知道我要访问哪张表吧?
结果一测试,程序跑得飞快,没报错。我赶紧去查数据,却越查越心凉:改的不是 OADBA.YY010,而是我自己账号下的 A.YY010。
我这才发现,Oracle 根本不会“猜”你想访问哪个 schema。它只看当前用户。我写的 yy010,默认就是 A.YY010。
那天晚上我把所有表名都改成了 OADBA.YY010。虽然多打了几个字,但至少不再改错表了。
三、匿名块能跑,塞进存储过程就“表不存在”
解决了表名问题,我又遇到一个诡异的现象。
这段代码,我直接执行,一点问题没有:
DECLARE
v_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM oadba.yy010;
END;
/可一旦塞进存储过程,编译就报:
PL/SQL: ORA-00942: table or view does not exist我反复确认:oadba.yy010 就在那里,我能查,我能改,它怎么就“不存在”了?
查了很久才知道,原来我的表权限是通过一个角色拿到的。匿名块认角色,但存储过程不认。PL/SQL 编译时,只认直接授予我的权限。
我厚着脸皮找 DBA:
“能不能把权限直接授给我,别走角色?”
DBA 看了看我,叹了口气:
GRANT SELECT, INSERT, UPDATE, DELETE ON oadba.yy010 TO A;终于,存储过程编译通过了。那种感觉,就像终于拿到了一张写着自己名字的门票。
四、有人让我建同义词,我差点又踩坑
权限问题解决后,有个同事看我每次都写 OADBA.,忍不住说:
“你建个同义词啊,省得每次都写 schema。”
我想也没想,差点就执行:
CREATE SYNONYM yy010 FOR oadba.yy010;还好我停了一下,先查了查自己账号下有没有 YY010 表:
SELECT * FROM user_objects WHERE object_name = 'YY010';这一查,冷汗都出来了。我自己账号下还真有一张 YY010 测试表。
如果我建了同义词,Oracle 还是会先找我自己的表,同义词照样被遮蔽。yy010 这个名字,在我的环境里始终是指向 A.YY010。
后来我明白了:同义词只是别名,不是复制表,也不能帮我自动选对表。
从那以后,我的正式代码里永远写着:
SELECT * FROM oadba.yy010;我宁可多敲几个字母,也不想再猜 Oracle 到底把我指向了哪里。
五、我亲手删掉了自己插入的测试数据
有一次测试一个写数据的存储过程,我特意选了一条测试数据,跑完检查也没问题,然后放心地下班了。
第二天,有个同事在群里问:
“谁昨天往 OADBA.YY010 里插了一条奇怪的数据?”我一看,正是我昨晚测试的那条。我赶紧解释:
“我测试完执行 ROLLBACK 了啊!”同事回了一句:
“你那个存储过程里不是写了 COMMIT 吗?”那一刻我恨不得把键盘吃了。原来,过程内部的 COMMIT 已经把数据提交了,我在外面 ROLLBACK,什么也撤不回来。
从那以后,我测试任何带写操作的过程,都会先看一眼里面有没有 COMMIT,再决定是用 ROLLBACK 回滚,还是用专门的测试数据、测完手动清理。
六、别再让我建公共同义词了
还有一次,A 要访问 B 的表,有人提议:
“让 B 建个公共同义词吧,大家都方便。”
我当时就说:“别。”
不是我固执,而是我已经见过太多“悄悄访问错表”的事故了。只要某个用户自己下也有一张同名表,公共同义词就会被遮住。看起来大家都在访问 yy010,实际上可能各自访问的是不同 schema 下的表。
最后我们用了最朴素的方案:
- B 给 A 授权:
CODE9
- A 老老实实写:
CODE10
没有同义词,没有歧义,谁看代码都知道访问的是哪张表。
我最终学到的几件事
- 表权限不等于代码权限
我能改表,不代表我能建存储过程。
yy010不等于OADBA.YY010
当前用户是谁,yy010 默认就指向谁。
- 存储过程不认角色权限
权限必须直接授予我的用户。
- 同义词只是别名,不是保险箱
它不复制数据,也不能代替授权。
- 公共同义词是全局的,也是最危险的
一个同名表,就可能把你的 SQL 悄悄拐到别处。
- 先看清过程里有没有
COMMIT再测试
不然,你的测试数据可能就永远留下了。
如果你也常在 Oracle 里写存储过程,我只送你一句话:
别猜 Oracle 会访问哪张表,写清楚 schema.表名,让所有人都看得懂。 我踩过的坑,希望你不用再踩一遍。