I. 教程
II. SQL 语言
III. 服务器管理
- 章14. 安装指导
- 章15. 在 Windows 上安装客户端
- 章16. 操作系统环境
- 章17. 服务器配置
- 章18. 数据库角色和权限
- 章19. 管理数据库
- 章21. 区域
- 章20. 用户认证
- 章22. 日常数据库维护工作
- 章23. 备份与恢复
- 章24. 高可用性与负载均衡
- 章25. 监控数据库的活动
- 章26. 监控磁盘使用情况
- 章27. 可靠性和预写式日志
- 章28. 回归测试
IV. 客户端接口
- Chapter 28. libpq - C 库
- 章29.
- Chapter 30. ecpg - 在 C 里嵌入 SQL
- Chapter 31. 信息模式
- 31.2. 数据类型
- 31.3. informationschemacatalog_name
- 31.4. applicable_roles
- 31.5. check_constraints
- 31.6. columndomainusage
- 31.7. column_privileges
- 31.8. columnudtusage
- 31.9. columns
- 31.10. constraintcolumnusage
- 31.11. constrainttableusage
- 31.12. datatypeprivileges
- 31.13. domain_constraints
- 31.14. domainudtusage
- 31.15. domains
- 31.16. element_types
- 31.17. enabled_roles
- 31.18. keycolumnusage
- 31.20. referential_constraints
- 31.21. rolecolumngrants
- 31.22. roleroutinegrants
- 31.23. roletablegrants
- 31.24. roleusagegrants
- 31.25. routine_privileges
- 31.26. routines
- 31.27. schemata
- 31.28. sql_features
- 31.29. sqlimplementationinfo
- 31.30. sql_languages
- 31.31. sql_packages
- 31.32. sql_sizing
- 31.33. sqlsizingprofiles
- 31.34. table_constraints
- 31.35. table_privileges
- 31.36. tables
- 31.37. triggers
- 31.38. usage_privileges
- 31.39. viewcolumnusage
- 31.40. viewtableusage
- 31.41. views
V. 服务器端编程
- 章33. 扩展 SQL
- 章34. 触发器
- 章35. 规则系统
- 章36. 过程语言
- 章37. PL/pgSQL - SQL 过程语言
- 章38. PL/Tcl - Tcl 过程语言
- 章39. PL/Perl - Perl 过程语言
- 章40. PL/Python - Python 过程语言
- 章41. 服务器编程接口
VI. 参考手册
- I. SQL 命令
- ABORT
- ALTER AGGREGATE
- ALTER CONVERSION
- ALTER DATABASE
- ALTER DOMAIN
- ALTER FUNCTION
- ALTER GROUP
- ALTER INDEX
- ALTER LANGUAGE
- ALTER OPERATOR
- ALTER OPERATOR CLASS
- ALTER ROLE
- ALTER SCHEMA
- ALTER SEQUENCE
- ALTER TABLE
- ALTER TABLESPACE
- ALTER TRIGGER
- ALTER TYPE
- ALTER USER
- ANALYZE
- BEGIN
- CHECKPOINT
- CLOSE
- CLUSTER
- COMMENT
- COMMIT PREPARED
- COMMIT
- COPY
- CREATE AGGREGATE
- CREATE CAST
- CREATE CONSTRAINT TRIGGER
- CREATE CONVERSION
- CREATE DATABASE
- CREATE DOMAIN
- CREATE FUNCTION
- CREATE GROUP
- CREATE INDEX
- CREATE LANGUAGE
- CREATE OPERATOR CLASS
- CREATE OPERATOR
- CREATE ROLE
- CREATE RULE
- CREATE SCHEMA
- CREATE SEQUENCE
- CREATE TABLE
- CREATE TABLE AS
- CREATE TABLESPACE
- CREATE TRIGGER
- CREATE TYPE
- CREATE USER
- CREATE VIEW
- DEALLOCATE
- DECLARE
- DELETE
- DROP OWNED
- DROP AGGREGATE
- DROP CAST
- DROP CONVERSION
- DROP DATABASE
- DROP DOMAIN
- DROP FUNCTION
- DROP GROUP
- DROP INDEX
- DROP LANGUAGE
- DROP OPERATOR CLASS
- DROP OPERATOR
- DROP ROLE
- DROP RULE
- DROP SCHEMA
- DROP SEQUENCE
- DROP TABLE
- DROP TABLESPACE
- DROP TRIGGER
- DROP TYPE
- DROP USER
- DROP VIEW
- END
- EXECUTE
- EXPLAIN
- FETCH
- GRANT
- INSERT
- LISTEN
- LOAD
- LOCK
- MOVE
- NOTIFY
- PREPARE TRANSACTION
- PREPARE
- REASSIGN OWNED
- REINDEX
- RELEASE SAVEPOINT
- RESET
- REVOKE
- ROLLBACK PREPARED
- ROLLBACK TO SAVEPOINT
- ROLLBACK
- SAVEPOINT
- SELECT
- SELECT INTO
- SET
- SET CONSTRAINTS
- SET ROLE
- SET SESSION AUTHORIZATION
- SET TRANSACTION
- SHOW
- START TRANSACTION
- TRUNCATE
- UNLISTEN
- UPDATE
- VACUUM
- II. PostgreSQL 客户端应用程序
- III. PostgreSQL 服务器应用程序
VII. 内部
- 章42. PostgreSQL 内部概貌
- 章43. 系统表
- 43.1. 概述
- 43.2. pg_aggregate
- 43.3. pg_am
- 43.4. pg_amop
- 43.5. pg_amproc
- 43.6. pg_attrdef
- 43.7. pg_attribute
- 43.8. pg_authid
- 43.9. pgauthmembers
- 43.10. pg_autovacuum
- 43.11. pg_cast
- 43.12. pg_class
- 43.13. pg_constraint
- 43.14. pg_conversion
- 43.15. pg_database
- 43.16. pg_depend
- 43.17. pg_description
- 43.18. pg_index
- 43.19. pg_inherits
- 43.20. pg_language
- 43.21. pg_largeobject
- 43.22. pg_listener
- 43.23. pg_namespace
- 43.24. pg_opclass
- 43.25. pg_operator
- 43.26. pg_pltemplate
- 43.27. pg_proc
- 43.28. pg_rewrite
- 43.29. pg_shdepend
- 43.30. pg_shdescription
- 43.31. pg_statistic
- 43.32. pg_tablespace
- 43.33. pg_trigger
- 43.34. pg_type
- 43.35. 系统视图
- 43.36. pg_cursors
- 43.37. pg_group
- 43.38. pg_indexes
- 43.39. pg_locks
- 43.40. pgpreparedstatements
- 43.41. pgpreparedxacts
- 43.42. pg_roles
- 43.43. pg_rules
- 43.44. pg_settings
- 43.45. pg_shadow
- 43.46. pg_stats
- 43.47. pg_tables
- 43.48. pgtimezoneabbrevs
- 43.49. pgtimezonenames
- 43.50. pg_user
- 43.51. pg_views
- 章44. 前/后端协议
- 章45. PostgreSQL 编码约定
- 章46. 本地语言支持
- 章47. 书写一个过程语言处理器
- 章48. 基因查询优化器
- 章49. 索引访问方法接口定义
- 章50. GiST 索引
- 章51. GIN 索引
- 章52. 数据库物理存储
- 章53. BKI 后端接口
- 章54. 规划器如何使用统计信息
VIII. 附录
37.11. 从 Oracle PL/SQL 进行移植
本节解释了 Oracle 的 PL/SQL 和 PostgreSQL 的 PL/pgSQL 语言之间的差别,希望能对那些从 Oracle 向 PostgreSQL 移植应用的人有所帮助。
PL/pgSQL 与 PL/SQL 在许多方面都非常类似。它是一种块结构的,祈使语气(命令性)的语言并且必须声明所有变量。赋值、循环、条件等都很类似。在从 Oracle 向 PostgreSQL 移植的时候必须记住一些事情:
在 PostgreSQL 中参数没有缺省值。
你可以在 PostgreSQL 里重载函数。这个特性常用于绕开缺乏缺省参数的问题。
不能使用已被函数引用的列同名的参数名。如果把参数名用
function_name.parameter_name
限定的话,Oracle 允许你这样做。在 PL/pgSQL 中,你应该避免与列名或表名冲突。在PL/pgSQL里不需要游标,只要把命令放在
FOR
语句里就可以了(见例37-6)。在 PostgreSQL 里,函数体必须写成字符串文本,因此你需要使用美元符界定或者逃逸函数体里面的单引号(见节37.2.1)。
应该用模式把函数组织成不同的组,而不是用包。
因为没有包,所以也没有包级别的变量。这一点有时候挺讨厌。你可以在临时表里保存会话级别的状态。
37.11.1. 移植样例
例37-5 演示了如何从 PL/SQL 向 PL/pgSQL 移植一个简单的函数。
例37-5. 从 PL/SQL 向 PL/pgSQL 移植一个简单的函数
下面是一个 Oracle PL/SQL 函数:
CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar, v_version varchar) RETURN varchar IS BEGIN IF v_version IS NULL THEN RETURN v_name; END IF; RETURN v_name || '/' || v_version; END; / show errors;
让我们读一遍这个函数然后看看 PL/pgSQL 与之的不同:
在函数原型里的
RETURN
(不是函数体里的)关键字到了 PostgreSQL 里就是RETURNS
。还有,IS
变成AS
,并且你还需要增加一个LANGUAGE
子句,因为 PL/pgSQL 并非唯一可用的函数语言。在 PostgreSQL 里,函数体被认为是一个字符串文本,所以你需要使用单引号或者美元符界定它,这个包围符代替了 Oracle 最后的那个
/
在 PostgreSQL 里没有
show errors
命令,不需要这个命令是因为错误是自动报告的。
下面是这个函数移植到 PostgreSQL 之后的样子:
CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar, v_version varchar) RETURNS varchar AS $$ BEGIN IF v_version IS NULL THEN RETURN v_name; END IF; RETURN v_name || '/' || v_version; END; $$ LANGUAGE plpgsql;
例37-6演示了如何移植一个创建另外一个函数的函数的方法,以及演示了如何处理引号逃逸的问题。
例37-6. 从 PL/SQL 向 PL/pgSQL 移植一个创建其它函数的函数
下面的过程从一个 SELECT
语句中抓取若干行,然后为了提高效率,又用 IF
语句中的结果制作了一个巨大的函数。请特别注意在游标和 FOR
循环里面的不同。
这是 Oracle 的版本:
CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS CURSOR referrer_keys IS SELECT * FROM cs_referrer_keys ORDER BY try_order; func_cmd VARCHAR(4000); BEGIN func_cmd := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host IN VARCHAR, v_domain IN VARCHAR, v_url IN VARCHAR) RETURN VARCHAR IS BEGIN'; FOR referrer_key IN referrer_keys LOOP func_cmd := func_cmd || ' IF v_' || referrer_key.kind || ' LIKE ''' || referrer_key.key_string || ''' THEN RETURN ''' || referrer_key.referrer_type || '''; END IF;'; END LOOP; func_cmd := func_cmd || ' RETURN NULL; END;'; EXECUTE IMMEDIATE func_cmd; END; / show errors;
面是这个函数在 PostgreSQL 里面的样子:
CREATE OR REPLACE FUNCTION cs_update_referrer_type_proc() RETURNS void AS $func$ DECLARE referrer_key RECORD; -- 声明一个在 FOR 里用的通用的记录 func_body text; func_cmd text; BEGIN func_body := 'BEGIN'; -- 请注意是如何在一个 FOR 循环中使用 FOR <record> 构造扫描各条结果的 FOR referrer_key IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP func_body := func_body || ' IF v_' || referrer_key.kind || ' LIKE ' || quote_literal(referrer_key.key_string) || ' THEN RETURN ' || quote_literal(referrer_key.referrer_type) || '; END IF;' ; END LOOP; func_body := func_body || ' RETURN NULL; END;'; func_cmd := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host varchar, v_domain varchar, v_url varchar) RETURNS varchar AS ' || quote_literal(func_body) || ' LANGUAGE plpgsql;' ; EXECUTE func_cmd; END; $func$ LANGUAGE plpgsql;
请注意函数体是如何独立制作并且传递给 quote_literal
,对其中的单引号复制双份的。需要这个技巧是因为无法使用美元符界定定义新函数:没法保证 referrer_key.key_string
字段过来的字符串会解析成什么样子。可以假设 referrer_key.kind
是只有 host
, domain
, url
,但是 referrer_key.key_string
可能是任何东西,特别是它可能包含美元符。这个函数实际上是对原来 Oracle 版本的一个改进,因为如果在 referrer_key.key_string
或 referrer_key.referrer_type
包含单引号的时候,它不会生成有毛病的代码。
例37-7演示了如何移植一个带有 OUT
参数和字符串处理的函数。PostgreSQL 里面没有内置 instr
函数,但是你可以用其它函数的组合来绕开它。在节37.11.3里有一个 PL/pgSQL 的 instr
实现,你可以用它让你的移植变得更简单些。
例37-7. 从 PL/SQL 向 PL/pgSQL 移植一个字符串操作和 OUT
参数的过程
下面的 Oracle PL/SQL 过程用于分析一个 URL 并且返回若干个元素(主机、路径、命令)。
下面是 Oracle 的版本:
CREATE OR REPLACE PROCEDURE cs_parse_url( v_url IN VARCHAR, v_host OUT VARCHAR, -- 这个变量是要传回的 v_path OUT VARCHAR, -- 这个也是 v_query OUT VARCHAR) -- 还有这个 IS a_pos1 INTEGER; a_pos2 INTEGER; BEGIN v_host := NULL; v_path := NULL; v_query := NULL; a_pos1 := instr(v_url, '//'); IF a_pos1 = 0 THEN RETURN; END IF; a_pos2 := instr(v_url, '/', a_pos1 + 2); IF a_pos2 = 0 THEN v_host := substr(v_url, a_pos1 + 2); v_path := '/'; RETURN; END IF; v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2); a_pos1 := instr(v_url, '?', a_pos2 + 1); IF a_pos1 = 0 THEN v_path := substr(v_url, a_pos2); RETURN; END IF; v_path := substr(v_url, a_pos2, a_pos1 - a_pos2); v_query := substr(v_url, a_pos1 + 1); END; / show errors;
下面就是把这个过程翻译成 PL/pgSQL 可能的样子:
CREATE OR REPLACE FUNCTION cs_parse_url( v_url IN VARCHAR, v_host OUT VARCHAR, -- 这个将被传回 v_path OUT VARCHAR, -- 这个也传回 v_query OUT VARCHAR) -- 还有这个 AS $$ DECLARE a_pos1 INTEGER; a_pos2 INTEGER; BEGIN v_host := NULL; v_path := NULL; v_query := NULL; a_pos1 := instr(v_url, '//'); IF a_pos1 = 0 THEN RETURN; END IF; a_pos2 := instr(v_url, '/', a_pos1 + 2); IF a_pos2 = 0 THEN v_host := substr(v_url, a_pos1 + 2); v_path := '/'; RETURN; END IF; v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2); a_pos1 := instr(v_url, '?', a_pos2 + 1); IF a_pos1 = 0 THEN v_path := substr(v_url, a_pos2); RETURN; END IF; v_path := substr(v_url, a_pos2, a_pos1 - a_pos2); v_query := substr(v_url, a_pos1 + 1); END; $$ LANGUAGE plpgsql;
这个函数可以这么用:
SELECT * FROM cs_parse_url('http://foobar.com/query.cgi?baz');
例37-8演示了如何一个使用各种 Oracle 专有特性的过程。
例37-8. 从 PL/SQL 向 PL/pgSQL 移植一个过程
Oracle 版本:
CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS a_running_job_count INTEGER; PRAGMA AUTONOMOUS_TRANSACTION;(1) BEGIN LOCK TABLE cs_jobs IN EXCLUSIVE MODE;(2) SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; IF a_running_job_count > 0 THEN COMMIT; -- free lock(3) raise_application_error(-20000, 'Unable to create a new job: a job is currently running.'); END IF; DELETE FROM cs_active_job; INSERT INTO cs_active_job(job_id) VALUES (v_job_id); BEGIN INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, sysdate); EXCEPTION WHEN dup_val_on_index THEN NULL; -- 如果已经存在,别担心 END; COMMIT; END; / show errors
像这样的过程可以很容易用返回 void
的函数移植到 PostgreSQL 里。对这个过程特别感兴趣是因为它可以教一些东西:
- (1)
- 在 PostgreSQL 里没有
PRAGMA
语句。 - (2)
- 如果你在 PL/pgSQL 里做一个
LOCK TABLE
,那么这个锁在调用该命令的事务完成之前将不会释放。 - (3)
- 你不能在 PL/pgSQL 函数里发出
COMMIT
。函数是在外层的事务里运行的,因此COMMIT
蕴涵着结束函数的执行。不过,在这个特殊场合下,这是不必要的了,因为LOCK TABLE
获取的锁将在抛出错误的时候释放。
下面是把这个过程移植到 PL/pgSQL 里的一种方法:
CREATE OR REPLACE FUNCTION cs_create_job(v_job_id integer) RETURNS void AS $$ DECLARE a_running_job_count integer; BEGIN LOCK TABLE cs_jobs IN EXCLUSIVE MODE; SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; IF a_running_job_count > 0 THEN RAISE EXCEPTION 'Unable to create a new job: a job is currently running';(1) END IF; DELETE FROM cs_active_job; INSERT INTO cs_active_job(job_id) VALUES (v_job_id); BEGIN INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, now()); EXCEPTION WHEN unique_violation THEN (2) -- 如果已经存在,别担心 END; END; $$ LANGUAGE plpgsql;
- (1)
RAISE
的语法和Oracle的类似语句差别相当明显。- (2)
- PL/pgSQL 里支持的异常的名字和 Oracle 的不同。 PL/pgSQL 内置的异常名要大的多(参阅附录A)。目前还不能声明用户定义的异常名。
cs_jobs
上持有的排他锁将保持到调用的事务结束。同样,如果调用者后来退出(比如说因为错误),这个过程的效果将被回滚掉。37.11.2. 其它注意事项
本节解释几个从 Oracle PL/SQL 函数向 PostgreSQL 移植的几个其它方面的事情。
37.11.2.1. 异常后的隐含回滚
在 PL/pgSQL 里,如果一个异常被 EXCEPTION
子句捕获,那么所有自这个块的 BEGIN
以来的数据库改变都会被自动回滚。也就是说,这个行为等于你在 Oracle 里的
BEGIN SAVEPOINT s1; ... code here ... EXCEPTION WHEN ... THEN ROLLBACK TO s1; ... code here ... WHEN ... THEN ROLLBACK TO s1; ... code here ... END;
如果你在把这么使用 SAVEPOINT
和 ROLLBACK TO
的 Oracle 过程翻译过来,那么你的活儿很好干:只要省略 SAVEPOINT
和 ROLLBACK TO
即可。如果你要翻译的过程使用了不同的 SAVEPOINT
和 ROLLBACK TO
,那么就需要想想了。
37.11.2.2. EXECUTE
PostgreSQL 版本的 EXECUTE
运转得类似 PL/SQL 不果你必须记住要像节37.6.5里描述的那样用 quote_literal
和 quote_ident
。如果你不用这些函数,那么像 EXECUTE 'SELECT * FROM $1';
这样的构造是不会运转的。
37.11.2.3. Optimizing PL/pgSQL Functions
PostgreSQL 给你两个创建函数的修饰词用来优化执行:"volatility"(易变的,在给出的参数相同时,函数总是返回相同结果)和"strictness"(严格的,如果任何参数是 NULL ,那么函数返回 NULL)。参考 CREATE FUNCTION 的手册获取细节。
如果要使用这些优化属性,那么你的 CREATE FUNCTION
语句可能看起来像这样:
CREATE FUNCTION foo(...) RETURNS integer AS $$ ... $$ LANGUAGE plpgsql STRICT IMMUTABLE;
37.11.3. 附录
本节包含 Oracle 兼容的 instr
函数,你可以用它简化你的移植过程。
-- -- 模拟 Oracle 概念的 instr 函数 -- 语法: instr(string1, string2, [n], [m]) 这里的 [] 表示可选参数 -- -- 从 string1 的第 n 个字符开始寻找 string2 的第 m 个出现。 -- 如果 n 是负数,则从后向前着。如果没有传递 m ,假定为 1(从第一个字符开始找)。 -- CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS $$ DECLARE pos integer; BEGIN pos:= instr($1, $2, 1); RETURN pos; END; $$ LANGUAGE plpgsql STRICT IMMUTABLE; CREATE FUNCTION instr(string varchar, string_to_search varchar, beg_index integer) RETURNS integer AS $$ DECLARE pos integer NOT NULL DEFAULT 0; temp_str varchar; beg integer; length integer; ss_length integer; BEGIN IF beg_index > 0 THEN temp_str := substring(string FROM beg_index); pos := position(string_to_search IN temp_str); IF pos = 0 THEN RETURN 0; ELSE RETURN pos + beg_index - 1; END IF; ELSE ss_length := char_length(string_to_search); length := char_length(string); beg := length + beg_index - ss_length + 2; WHILE beg > 0 LOOP temp_str := substring(string FROM beg FOR ss_length); pos := position(string_to_search IN temp_str); IF pos > 0 THEN RETURN beg; END IF; beg := beg - 1; END LOOP; RETURN 0; END IF; END; $$ LANGUAGE plpgsql STRICT IMMUTABLE; CREATE FUNCTION instr(string varchar, string_to_search varchar, beg_index integer, occur_index integer) RETURNS integer AS $$ DECLARE pos integer NOT NULL DEFAULT 0; occur_number integer NOT NULL DEFAULT 0; temp_str varchar; beg integer; i integer; length integer; ss_length integer; BEGIN IF beg_index > 0 THEN beg := beg_index; temp_str := substring(string FROM beg_index); FOR i IN 1..occur_index LOOP pos := position(string_to_search IN temp_str); IF i = 1 THEN beg := beg + pos - 1; ELSE beg := beg + pos; END IF; temp_str := substring(string FROM beg + 1); END LOOP; IF pos = 0 THEN RETURN 0; ELSE RETURN beg; END IF; ELSE ss_length := char_length(string_to_search); length := char_length(string); beg := length + beg_index - ss_length + 2; WHILE beg > 0 LOOP temp_str := substring(string FROM beg FOR ss_length); pos := position(string_to_search IN temp_str); IF pos > 0 THEN occur_number := occur_number + 1; IF occur_number = occur_index THEN RETURN beg; END IF; END IF; beg := beg - 1; END LOOP; RETURN 0; END IF; END; $$ LANGUAGE plpgsql STRICT IMMUTABLE;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论