PHP Conference Japan 2024

pg_query_params

(PHP 5 >= 5.1.0,PHP 7,PHP 8)

pg_query_params将命令提交到服务器并等待结果,能够将参数与 SQL 命令文本分开传递

描述

pg_query_params(PgSql\Connection $connection = ?, string $query, array $params): PgSql\Result|false

将命令提交到服务器并等待结果,能够将参数与 SQL 命令文本分开传递。

pg_query_params() 类似于 pg_query(),但提供了额外的功能:参数值可以与命令字符串本身分开指定。pg_query_params() 仅在连接到 PostgreSQL 7.4 或更高版本时受支持;在使用早期版本时将失败。

如果使用参数,则在 query 字符串中将它们称为 $1、$2 等。同一个参数可以在 query 中出现多次;在这种情况下将使用相同的值。params 指定参数的实际值。此数组中的 null 值表示相应的参数为 SQL NULL

pg_query_params() 相对于 pg_query() 的主要优势在于,参数值可以与 query 字符串分开,从而避免了繁琐且容易出错的引用和转义。与 pg_query() 不同,pg_query_params() 允许在给定字符串中最多使用一个 SQL 命令。(其中可以包含分号,但非空命令不能超过一个。)

参数

connection

一个 PgSql\Connection 实例。当 connection 未指定时,将使用默认连接。默认连接是 pg_connect()pg_pconnect() 最后建立的连接。

警告

从 PHP 8.1.0 开始,使用默认连接已弃用。

query

参数化的 SQL 语句。必须只包含单个语句。(不允许用分号分隔的多个语句。)如果使用任何参数,则将它们称为 $1、$2 等。

用户提供的值应始终作为参数传递,而不是插值到查询字符串中,因为它们构成了可能的 SQL 注入 攻击媒介,并在处理包含引号的数据时引入错误。如果由于某种原因您无法使用参数,请确保插值的值已 正确转义

params

一个参数值数组,用于替换原始准备好的查询字符串中的 $1、$2 等占位符。数组中的元素数量必须与占位符的数量匹配。

用于 bytea 字段的值不受支持作为参数。请改用 pg_escape_bytea(),或使用大对象函数。

返回值

成功时返回一个 PgSql\Result 实例,失败时返回 false

变更日志

版本 描述
8.1.0 现在返回 PgSql\Result 实例;以前返回的是 资源
8.1.0 connection 参数现在期望一个 PgSql\Connection 实例;以前期望的是 资源

示例

示例 #1 使用 pg_query_params()

<?php
// 连接到名为“mary”的数据库
$dbconn = pg_connect("dbname=mary");

// 查找所有名为 Joe's Widgets 的商店。请注意,无需转义“Joe's Widgets”
$result = pg_query_params($dbconn, 'SELECT * FROM shops WHERE name = $1', array("Joe's Widgets"));

// 与仅使用 pg_query 进行比较
$str = pg_escape_string("Joe's Widgets");
$result = pg_query($dbconn, "SELECT * FROM shops WHERE name = '{$str}'");

?>

参见

添加注释

用户贡献的注释 14 条注释

8
victor dot engmark at terreactive dot ch
13 年前
您不能使用 pg_query_params 运行多个语句,但您仍然可以在没有回退到 pg_query 的情况下获得事务支持



<?php
$connection
= pg_connect("host=127.0.0.1 port=5432 dbname=foo user=bar password=baz");
pg_query($connection, 'DROP TABLE IF EXISTS example');
pg_query($connection, 'CREATE TABLE example (col char(1))');
pg_query($connection, 'INSERT INTO example (col) VALUES (\'a\')');
// 在另一个会话中执行 'SELECT col FROM example' 将返回 "a"
pg_query($connection, 'BEGIN');
pg_query_params($connection, 'UPDATE example SET col = $1', array('b'));
// 在另一个会话中执行 'SELECT col FROM example' 仍然返回 "a"
pg_query_params($connection, 'UPDATE example SET col = $1', array('c'));
// 在另一个会话中执行 'SELECT col FROM example' 仍然返回 "a"
pg_query($connection, 'COMMIT');
// 在另一个会话中执行 'SELECT col FROM example' 将返回 "c"
?>
6
php at richardneill dot org
10年前
如果你想直接将参数化查询粘贴到PSQL中进行调试,可能会比较繁琐。这里有一个技巧可以帮助你。

<?php
$sql
= "SELECT * from table WHERE col_a = $1 and col_b=$2 and col_c=$3";
$params = array (42, "a string", NULL);

$debug = preg_replace_callback(
'/\$(\d+)\b/',
function(
$match) use ($params) {
$key=($match[1]-1); return ( is_null($params[$key])?'NULL':pg_escape_literal($params[$key]) );
},
$sql);

echo
"$debug";
// 输出:SELECT * from table WHERE col_a = '42' and col_b='a string' and col_c=NULL
?>

这个方法运行良好,除了在(不常见)的情况下,我们有一个字面量 $N;正则表达式会在不应该替换的地方替换它。例如
<?php
// ' ... $1 ... ' 和 $1 都被替换了;前者是错误的,后者是正确的。
$sql = "SELECT 'Your bill is for $1' AS invoice WHERE 7 = $1";
$params = array(7);
//$debug: SELECT 'Your bill is for $7' AS invoice WHERE 7 = '7'"
?>
5
ac at esilo dot com
14年前
pg_query 和 pg_query_params 可以合并成一个函数。这样也可以避免为 pg_query_params 构造参数数组。

<?php
function my_query($conn, $query)
{
if(
func_num_args() == 2)
return
pg_query($conn, $query);

$args = func_get_args();
$params = array_splice($args, 2);
return
pg_query_params($conn, $query, $params);
}
?>

用法

<?php
/* 非参数化示例 */
my_query($conn, "SELECT $val1 + $val2");

/* 参数化示例 */
my_query($conn, "SELECT $1 + $2", $val1, $val2);
?>
5
dt309 at f2s dot com
17年前
如果你需要在 select 查询中为一个字段提供多个可能的值,那么以下方法将有所帮助。

<?php
// 假设 $values[] 是一个包含你感兴趣的值的数组。
$values = array(1, 4, 5, 8);

// 使用 pg_query() 选择可变数量的参数,你可以使用:
$valuelist = implode(', ', $values);
$query = "SELECT * FROM table1 WHERE col1 IN ($valuelist)";
$result = pg_query($query)
or die(
pg_last_error());

// 因此,你可以假设以下方法将有效。
$query = 'SELECT * FROM table1 WHERE col1 IN ($1)';
$result = pg_query_params($query, array($valuelist))
or die(
pg_last_error());
// 产生错误消息:'ERROR: invalid input syntax for integer'
// 它仅在指定单个值时有效。

// 相反,你必须使用以下方法:
$valuelist = '{' . implode(', ', $values . '}'
$query = 'SELECT * FROM table1 WHERE col1 = ANY ($1)';
$result = pg_query_params($query, array($valuelist));
?>

此示例中产生的错误是由PostGreSQL生成的。

最后一种方法通过创建一个包含所需值的SQL数组来工作。'IN (...)' 和 ' = ANY (...)' 是等价的,但 ANY 用于处理数组,而 IN 用于处理简单的列表。
1
jsnell at e-normous dot com
17年前
当插入到类型为 bool 的 pg 列时,你不能提供 PHP 类型为 bool 的值。你必须改为使用字符串 "t" 或 "f"。PHP 尝试将作为参数提供的布尔值更改为字符串,然后尝试对 false 使用空字符串。

失败示例
pg_query_params('insert into table1 (bool_column) values ($1)', array(false));

有效
pg_query_params('insert into lookup_permissions (system) values ($1)', array(false ? 't' : 'f'));
1
peter dot kehl+nospam at gmail dot com
12年前
pg_query_params() 的第三个参数 $params 会忽略字符串值中零字节字符(PHP 的 "\0" 或 chr(0))之后的所有部分。这可能是 serialize() 的结果。

参见 https://bugs.php.net/bug.php?id=63344
1
alec at smecher dot bc dot ca
12年前
请注意,由于你的区域设置的数字格式设置,你可能无法将数字值作为参数传递并使其在 PostgreSQL 中仍然是数字。

如果你的系统区域设置使用 "," 作为小数分隔符,则以下操作将导致数据库错误

pg_query_params($conn, 'SELECT $1::numeric', array(3.5));

要使其正常工作,需要使用例如 number_format 手动将 3.5 转换为字符串。

(我将其作为错误 #46408 提交,但显然这是预期的行为。)
1
strata_ranger at hotmail dot com
15年前
关于布尔值,在查询中传递它们时,只需将其类型转换为 (integer) - '0' 和 '1' 是 SQL 布尔输入的完全可接受的字面量

- https://postgresql.ac.cn/docs/8.2/interactive/datatype-boolean.html

将参数化查询写入双引号中也是安全的,这允许你在查询中混合常量值和占位符,而无需担心 PHP 是否会尝试替换参数化字符串中的任何变量。

当然,这也意味着与 PHP 的双引号字符串语法不同,你可以在 SQL 字符串中包含字面量 $1、$2 等,例如

<?php
// 有效 ($1 是占位符,$2 是字面量)
pg_query_params("INSERT INTO foo (col1, col2) VALUES ($1, 'costs $2')", Array($data1));

// 抛出 E_WARNING (传递了太多参数)
pg_query_params("INSERT INTO foo (col1, col2) VALUES ($1, 'costs $2')", Array($data1, $data2));
?>
-1
mledford
18年前
如果您尝试复制函数 pg_query_params,您可能还想支持 NULL 值。虽然 is_int 对 NULL 值返回 true,但 SQL 的格式。

function pg_query_params( $db, $query, $parameters ) {
// 根据需要转义参数并为回调函数构建参数
global $pg_query_params__parameters;
foreach( $parameters as $k=>$v ) {
if ( is_null($v) ) {
$parameters[$k] = 'NULL';
} else {
$parameters[$k] = ( is_int( $v ) ? $v : "'".pg_escape_string( $v )."'" );
}
}
$pg_query_params__parameters = $parameters;

// 使用 pg_query 调用
return pg_query( $db, preg_replace_callback( '/\$([0-9]+)/', 'pg_query_params__callback', $query));
}
-1
cc+php at c2se dot com
18年前
这是一个用于防止 SQL 注入攻击的有用函数,因此,对于我们这些尚未能够升级到 PHP5.1 的人来说,这里有一个在 PHP 的旧版本上类似工作的替换函数……

<?php # Postgresql 和旧版 PHP 的参数化查询实现

if( !function_exists( 'pg_query_params' ) ) {

function
pg_query_params__callback( $at ) {
global
$pg_query_params__parameters;
return
$pg_query_params__parameters[ $at[1]-1 ];
}

function
pg_query_params( $db, $query, $parameters ) {

// 根据需要转义参数并为回调函数构建参数
global $pg_query_params__parameters;
foreach(
$parameters as $k=>$v )
$parameters[$k] = ( is_int( $v ) ? $v : "'".pg_escape_string( $v )."'" );
$pg_query_params__parameters = $parameters;

// 使用 pg_query 调用
return pg_query( $db, preg_replace_callback( '/\$([0-9]+)/', 'pg_query_params__callback', $query ) );

}
}

// 示例:pg_query_params( $db_resource, "SELECT * FROM table WHERE col1=$1 AND col2=$2", array( 42, "It's ok" ) );
?>
-1
匿名用户
7年前
如果其中一个参数是数组(例如,传递给存储过程的整数数组),则必须在数组中将其表示为集合,而不是 php 数组表示法。

例如:2 个参数(一个整数和一个整数数组)的 var_dump 输出
aaa 是:数组
(
[0] => 1
[1] => {2,3}
)

您不希望

bbb 是:数组
(
[0] => 1
[1] => 数组
(
[0] => 2
[1] => 3
)

)
-2
php at richardneill dot org
10年前
pg_query_params() *确实*接受 NULL。它们将自动正确转换为 SQL NULL。因此,例如

<?php
$sql
= "UPDATE tbl_example SET column_a = $1, column_b=$2";
$params = array(NULL, 42);
$result = pg_params ($sql, $params);

//等价于:
$result = pg_query ("UPDATE tbl_example SET column_a = NULL column_b = '42')";

//而不是,正如人们可能担心的那样,以下两者中的任何一个(不正确):
// ... column_a = '' ...
// ... column_a = 'NULL' ...
?>

请注意,您可以在 UPDATE 或 INSERT 语句中以这种方式使用 NULL,但不能在 WHERE 子句中使用。这不是 pg_query_params() 的限制,而是 SQL 语言的结果。
因此,如果您想要以下类型的查询

<?php
//根据数据,where-test 参数可能是 NULL 也可能不是 NULL
//以下对于 $1 是错误的。
$sql = "SELECT * from tbl_example WHERE column_a = $1 and column_b = $2";
$params = array(NULL, 42);
$result = pg_params ($sql, $params);
?>

这将失败,因为 SQL 无效:因为您应该使用“= 42”而不是“IS NULL”。解决方案是使用 SQL 结构“IS [NOT] DISTINCT FROM”。

<?php
$sql
= "SELECT ... WHERE column IS NOT DISTINCT FROM $1"
$params = array (42); //这有效,与“where column = 42”相同
$params = array (NULL); //这有效,与“where column is null”相同
?>

(旁注:虽然这很烦人,但行为是正确的。有一个 postgresql 兼容性选项“transform_null_equals”,但它在这里无济于事,即使您可能期望它能做到。)
-1
php at richardneill dot org
10年前
关于布尔值的类型转换的说明
pg_query_params() 及其相关函数在适当情况下会在 PHP-NULL 和 SQL-NULL 之间进行无缝、自动的转换。
然而,其他所有内容都作为字符串传入(和传出)。
处理布尔字段时,以下方法可能会有所帮助

<?php
$sql
= " ... ";
$params = array (1, 2, 3, true, false);

//将布尔值转换为 'true' 和 'false'。[NULL 已处理]。
foreach ($params as &$value){
if (
is_bool($value)){
$value = ($value) ? 'true':'false';
}
}

//现在执行查询:
$result = pg_query_params ($sql, $params);
$row = pg_fetch_assoc ($result,0) //第一行

//对于布尔值,将 't' 和 'f' 转换回 true 和 false。检查列类型,以免意外转换错误的内容。
foreach ($row as $key => &$value){
$type = pg_field_type($result,pg_field_num($result, $key));
if (
$type == 'bool'){
$value = ($value == 't');
}
}

//$row[] 现在包含布尔值、NULL 和字符串。
?>
-4
php at richardneill dot org
10年前
对于参数化的日期,不允许使用 NOW() 值(它将被转换为字面量字符串并导致 postgres 阻塞),但是 'now'
允许作为参数,并且具有相同的效果。
To Top