PostgreSQL quote ident and literal
PostgreSQL quote ident and literal
2013-05-21 16:08:58| 分类: PgSQL Develop | 标签: |字号大中小 订阅
前面一篇介绍了SQL注入, 其中利用字符逃逸漏洞的占了主要部分.
PostgreSQL 提供了几个函数用来输出转义后的字符串.
quote_ident(string text) | text | Return the given string suitably quoted to be used as an identifier in an SQL statement string. Quotes are added only if necessary (i.e., if the string contains non-identifier characters or would be case-folded). Embedded quotes are properly doubled. See also Example 40-1. | quote_ident('Foo bar') | "Foo bar" |
quote_literal(string text) | text | Return the given string suitably quoted to be used as a string literal in an SQL statement string. Embedded single-quotes and backslashes are properly doubled. Note that quote_literal returns null on null input; if the argument might be null, quote_nullable is often more suitable. See also Example 40-1. |
quote_literal(E'O\'Reilly') | 'O''Reilly' |
quote_literal(valueanyelement) | text | Coerce the given value to text and then quote it as a literal. Embedded single-quotes and backslashes are properly doubled. | quote_literal(42.5) | '42.5' |
quote_nullable(string text) | text | Return the given string suitably quoted to be used as a string literal in an SQL statement string; or, if the argument is null, return NULL. Embedded single-quotes and backslashes are properly doubled. See also Example 40-1. | quote_nullable(NULL) | NULL |
quote_nullable(valueanyelement) | text | Coerce the given value to text and then quote it as a literal; or, if the argument is null, return NULL. Embedded single-quotes and backslashes are properly doubled. | quote_nullable(42.5) | '42.5' |
一般可用于构造SQL语句, 例如dblink中的动态SQL语句的构造 :
防止因为输入问题造成的不正确SQL.
quote_ident用于构造数据库对象名, 例如表名, 列名.
quote_literal用于构造字符串.
quote_nullable和quote_literal类似, 只是空参数的处理不一样.
quote_literal是strict的, 输入空, 返回空.
quote_nullable是not strict的, 内部处理空值, 返回NULL字符串.
digoal=# select proisstrict,provolatile,proname from pg_proc where proname ~ 'quote';
proisstrict | provolatile | proname
-------------+-------------+----------------
t | i | quote_ident
t | i | quote_literal
t | s | quote_literal
f | i | quote_nullable
f | s | quote_nullable
(5 rows)
src/backend/utils/adt/quote.c
/*
* quote_nullable -
* Returns a properly quoted literal, with null values returned
* as the text string 'NULL'.
*/
Datum
quote_nullable(PG_FUNCTION_ARGS)
{
if(PG_ARGISNULL(0))
PG_RETURN_TEXT_P(cstring_to_text("NULL"));
else
PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
PG_GETARG_DATUM(0)));
}
quote_nullable和quote_literal的差异 :
digoal=# select 1 where quote_nullable(null) is null;
?column?
----------
(0 rows)
digoal=# select 1 where quote_literal(null) is null;
?column?
----------
1
(1 row)
digoal=# select 1 where quote_literal(null)='NULL';
?column?
----------
(0 rows)
digoal=# select 1 where quote_nullable(null)='NULL';
?column?
----------
1
(1 row)
构造动态SQL举例.
不使用quote :
digoal=# create or replace function f_test(i_tablename text, i_cname text, i_cval text) returns void as $$
declare
v_sql text;
begin
v_sql := 'create table '||i_tablename||'('||i_cname||' text)';
raise notice '%', v_sql;
execute v_sql;
v_sql := 'insert into '||i_tablename||'('||i_cname||') values ('||i_cval||')';
raise notice '%', v_sql;
execute v_sql;
end;
$$ language plpgsql;
CREATE FUNCTION
# 当表名, 列名中有空格或其他特殊字符时, 必须要用双引号, 因此以下调用将报错.
digoal=# select f_test('a b','d e','''');
NOTICE: 00000: create table a b(d e text)
LOCATION: exec_stmt_raise, pl_exec.c:2985
ERROR: 42601: syntax error at or near "b"
LINE 1: create table a b(d e text)
^
QUERY: create table a b(d e text)
CONTEXT: PL/pgSQL function f_test(text,text,text) line 7 at EXECUTE statement
LOCATION: scanner_yyerror, scan.l:1044
# 当字符串中使用单引号时, 也必须逃逸, 使用双单引号. 这里也会报错.
digoal=# select f_test('ab','de','''');
NOTICE: 00000: create table ab(de text)
LOCATION: exec_stmt_raise, pl_exec.c:2985
NOTICE: 00000: insert into ab(de) values (')
LOCATION: exec_stmt_raise, pl_exec.c:2985
ERROR: 42601: unterminated quoted string at or near "')"
LINE 1: insert into ab(de) values (')
^
QUERY: insert into ab(de) values (')
CONTEXT: PL/pgSQL function f_test(text,text,text) line 10 at EXECUTE statement
LOCATION: scanner_yyerror, scan.l:1044
使用quote :
使用quote函数后, 数据库会自动根据需要帮你加上双引号和其他逃逸.
digoal=# create or replace function f_quote(i_tablename text, i_cname text, i_cval text) returns void as $$
declare
v_sql text;
begin
v_sql := 'create table '||quote_ident(i_tablename)||'('||quote_ident(i_cname)||' text)';
raise notice '%', v_sql;
execute v_sql;
v_sql := 'insert into '||quote_ident(i_tablename)||'('||quote_ident(i_cname)||') values ('||quote_literal(i_cval)||')';
raise notice '%', v_sql;
execute v_sql;
end;
$$ language plpgsql;
digoal=# select f_quote('a b','d e','''');
NOTICE: 00000: create table "a b"("d e" text)
LOCATION: exec_stmt_raise, pl_exec.c:2985
NOTICE: 00000: insert into "a b"("d e") values ('''')
LOCATION: exec_stmt_raise, pl_exec.c:2985
f_quote
---------
(1 row)
digoal=# select * from "a b";
d e
-----
'
(1 row)