在 PL/pgSQL 中使用 USING 关键字清理用户输入
Sanitize user input with the USING keyword in PL/pgSQL
这就是我创建 search_term
的方式:
IF char_length(search_term) > 0 THEN
order_by := 'ts_rank_cd(textsearchable_index_col, to_tsquery(''' || search_term || ':*''))+GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
search_term := 'to_tsquery(''' || search_term || ':*'') @@ textsearchable_index_col';
ELSE
search_term := 'true';
END IF;
我在使用 PLPGSQL 函数时遇到了一些问题:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500'
USING search_term;
END; $$;
returns 错误:
argument of AND must be type boolean, not type text at character 64
相对于:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
' || search_term || ' AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500';
END; $$;
...有效。我错过了什么吗?
我的目标是清理我的用户输入。
如果您的某些输入参数可以是 NULL 或 empty 并且在这种情况下应被忽略,您最好构建整个语句动态地取决于用户输入 - 并完全省略相应的 WHERE
/ ORDER BY
子句。
关键是在这个过程中正确、安全(优雅)地处理NULL和空字符串。对于初学者来说,search_term <> ''
是比 char_length(search_term) > 0
更聪明的测试。参见:
- Best way to check for "empty or null value"
并且您需要对 PL/pgSQL 有深入的了解,否则您可能会不知所措。您的案例的示例代码:
CREATE OR REPLACE FUNCTION my_func(
_search_term text = NULL -- default value NULL to allow short call
, _publication_date_query date = NULL
-- , more parameters
)
RETURNS SETOF articles AS
$func$
DECLARE
sql text;
sql_order text; -- defaults to NULL
BEGIN
sql := concat_ws(' AND '
,'SELECT * FROM articles WHERE status <> ''DELETED''' -- first WHERE clause is immutable
, CASE WHEN _search_term <> '' THEN ' @@ textsearchable_index_col' END -- ELSE NULL is implicit
, CASE WHEN _publication_date_query <> '' THEN 'publication_date > ' END -- or similar ...
-- , more more parameters
);
IF search_term <> '' THEN -- note use of !
sql_order := 'ORDER BY ts_rank_cd(textsearchable_index_col, ) + GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
END IF;
RETURN QUERY EXECUTE concat_ws(' ', sql, sql_order, 'LIMIT 500')
USING to_tsquery(_search_term || ':*') -- -- prepare ts_query once here!
, _publication_date_query -- -- order of params must match!
-- , more parameters
;
END
$func$ LANGUAGE plpgsql;
我为函数参数添加了默认值,因此您可以省略在调用中不适用的参数。喜欢:
SELECT * FROM my_func(_publication_date_query => '2016-01-01');
更多:
- Functions with variable number of input parameters
- The forgotten assignment operator "=" and the commonplace ":="
注意 concat_ws()
的策略性使用。参见:
- How to concatenate columns in a Postgres SELECT?
这是一个有很多解释的相关答案:
- Test for null in function with varying parameters
这就是我创建 search_term
的方式:
IF char_length(search_term) > 0 THEN
order_by := 'ts_rank_cd(textsearchable_index_col, to_tsquery(''' || search_term || ':*''))+GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
search_term := 'to_tsquery(''' || search_term || ':*'') @@ textsearchable_index_col';
ELSE
search_term := 'true';
END IF;
我在使用 PLPGSQL 函数时遇到了一些问题:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500'
USING search_term;
END; $$;
returns 错误:
argument of AND must be type boolean, not type text at character 64
相对于:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
' || search_term || ' AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500';
END; $$;
...有效。我错过了什么吗?
我的目标是清理我的用户输入。
如果您的某些输入参数可以是 NULL 或 empty 并且在这种情况下应被忽略,您最好构建整个语句动态地取决于用户输入 - 并完全省略相应的 WHERE
/ ORDER BY
子句。
关键是在这个过程中正确、安全(优雅)地处理NULL和空字符串。对于初学者来说,search_term <> ''
是比 char_length(search_term) > 0
更聪明的测试。参见:
- Best way to check for "empty or null value"
并且您需要对 PL/pgSQL 有深入的了解,否则您可能会不知所措。您的案例的示例代码:
CREATE OR REPLACE FUNCTION my_func(
_search_term text = NULL -- default value NULL to allow short call
, _publication_date_query date = NULL
-- , more parameters
)
RETURNS SETOF articles AS
$func$
DECLARE
sql text;
sql_order text; -- defaults to NULL
BEGIN
sql := concat_ws(' AND '
,'SELECT * FROM articles WHERE status <> ''DELETED''' -- first WHERE clause is immutable
, CASE WHEN _search_term <> '' THEN ' @@ textsearchable_index_col' END -- ELSE NULL is implicit
, CASE WHEN _publication_date_query <> '' THEN 'publication_date > ' END -- or similar ...
-- , more more parameters
);
IF search_term <> '' THEN -- note use of !
sql_order := 'ORDER BY ts_rank_cd(textsearchable_index_col, ) + GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
END IF;
RETURN QUERY EXECUTE concat_ws(' ', sql, sql_order, 'LIMIT 500')
USING to_tsquery(_search_term || ':*') -- -- prepare ts_query once here!
, _publication_date_query -- -- order of params must match!
-- , more parameters
;
END
$func$ LANGUAGE plpgsql;
我为函数参数添加了默认值,因此您可以省略在调用中不适用的参数。喜欢:
SELECT * FROM my_func(_publication_date_query => '2016-01-01');
更多:
- Functions with variable number of input parameters
- The forgotten assignment operator "=" and the commonplace ":="
注意 concat_ws()
的策略性使用。参见:
- How to concatenate columns in a Postgres SELECT?
这是一个有很多解释的相关答案:
- Test for null in function with varying parameters