调试数据库功能
给数据库函数加上日志和错误处理,这样你就能在运行时看到它的行为。在复杂的函数中,日志最重要。
🌐 Add logs and error handling to a database function so you can see what it does at runtime. Logs matter most in a complex function.
关于如何编写和调用函数,请参见 数据库函数。
🌐 For how to write and call a function, see Database functions.
值得记录的好目标包括:
🌐 Good targets to log include:
- (非敏感)变量的数值
- 查询返回的结果
常规日志 #
🌐 General logging
在仪表板中使用 raise 关键字将自定义日志写入 Postgres 日志。默认显示三种严重级别:
🌐 Use the raise keyword to write custom logs to the Postgres logs in the Dashboard. Three severity levels appear by default:
logwarningexception(错误级别)
create function logging_example( log_message text, warning_message text, error_message text)returns voidlanguage plpgsqlas $$begin raise log 'logging message: %', log_message; raise warning 'logging warning: %', warning_message; -- immediately ends function and reverts transaction raise exception 'logging error: %', error_message;end;$$;select logging_example('LOGGED MESSAGE', 'WARNING MESSAGE', 'ERROR MESSAGE');错误处理 #
🌐 Error handling
你可以用 raise exception 关键字创建自定义错误。
🌐 You can create custom errors with the raise exception keywords.
一个常见的模式是在变量不满足条件时抛出错误:
🌐 A common pattern is to throw an error when a variable doesn't meet a condition:
create or replace function error_if_null(some_val text)returns textlanguage plpgsqlas $$begin -- error if some_val is null if some_val is null then raise exception 'some_val should not be NULL'; end if; -- return some_val if it is not null return some_val;end;$$;select error_if_null(null);数值检查很常见,所以 Postgres 提供了 assert 关键字作为简写。它的格式如下:
🌐 Value checking is common, so Postgres provides the assert keyword as a shorthand. It takes the following format:
assert <some condition>, 'message';例如:
🌐 For example:
-- assumes an attendance_table with an id uuid column and a student text columncreate function assert_example(name text)returns uuidlanguage plpgsqlas $$declare student_id uuid;begin -- save a user's id into the user_id variable select id into student_id from attendance_table where student = name; -- throw an error if the student_id is null assert student_id is not null, 'assert_example() ERROR: student not found'; -- otherwise, return the user's id return student_id;end;$$;select assert_example('Harry Potter');你也可以使用 exception 关键字捕获并修改错误信息:
🌐 You can also capture and modify an error message with the exception keyword:
create function error_example()returns voidlanguage plpgsqlas $$begin -- fails: cannot read from nonexistent table select * from table_that_does_not_exist; exception when others then raise exception 'An error occurred in function <function name>: %', sqlerrm;end;$$;高级日志记录 #
🌐 Advanced logging
对于更复杂的函数,或更难调试的情况,记录以下内容:
🌐 For a more complex function, or for harder debugging, log the following:
- 格式化变量
- 单独的行
- 函数调用的开始和结束
-- assumes a some_table with a col_1 int column and a col_2 text columncreate or replace function advanced_example(num int default 10)returns textlanguage plpgsqlas $$declare var1 int := 20; var2 text;begin -- Logging start of function raise log 'logging start of function call: (%)', (select now()); -- Logging a variable from a SELECT query select col_1 into var1 from some_table limit 1; raise log 'logging a variable (%)', var1; -- It is also possible to avoid using variables, by returning the values of your query to the log raise log 'logging a query with a single return value(%)', (select col_1 from some_table limit 1); -- If necessary, you can even log an entire row as JSON raise log 'logging an entire row as JSON (%)', (select to_jsonb(some_table.*) from some_table limit 1); -- When using INSERT or UPDATE, the new value(s) can be returned -- into a variable. -- When using DELETE, the deleted value(s) can be returned. -- All three operations use "RETURNING value(s) INTO variable(s)" syntax insert into some_table (col_2) values ('new val') returning col_2 into var2; raise log 'logging a value from an INSERT (%)', var2; return var1 || ',' || var2;exception -- Handle exceptions here if needed when others then raise exception 'An error occurred in function <advanced_example>: %', sqlerrm;end;$$;select advanced_example();