【问题标题】:How to pass a php array to a postgres function如何将 php 数组传递给 postgres 函数
【发布时间】:2012-08-16 08:02:34
【问题描述】:

我尝试将 PHP 数组传递给以下函数,但收效甚微。

$recipients = array();
$recipients['6c2f7451-bac8-4cdd-87ce-45d55d152078'] = 5.00;
$recipients['8c2f3421-bac8-4cdd-87ce-45d55d152074'] = 10.00;

$originator = '5630cc6d-f994-43ff-abec-1ebb74d39a3f';
$params = array($originator, $recipients);

pg_query_params(_conn, 'SELECT widgetallocationrequest($1, $2)', $params);

$results = pg_fetch_all(pg_query_params);
...

该函数接受一个自定义类型的数组:

CREATE TYPE widgetallocationrequest AS (id uuid, amount numeric(13,4));

枚举每个项目并执行一个动作:

CREATE FUNCTION distributeWidgets(pid uuid, precipients widgetallocationrequest[])
 RETURNS BOOLEAN AS
 $BODY$
{
FOREACH v_recipient IN ARRAY precipients
LOOP
    ...do something
END LOOP;
}
  RETURN TRUE;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100;

***(if the specific code sample contains errors it's only pseudocode, i'm really just looking for the best way to pass a php array to a postgres custom type array as a parameter so it can be enumerated in the postgres function)***

更新: 我可以使用以下命令直接从 postgres(而不是 PHP)成功调用该函数:

SELECT distributeWidgets('5630cc6d-f994-43ff-abec-1ebb74d39a3f',
ARRAY[('ac747f0e-93d4-43a9-bc5b-09df06593239', '5.00'), ('8c2f3421-bac8-4cdd-87ce-45d55d152074', '10.00')]::widgetallocationrequest[]);

但仍然不确定如何从这个 postgres 示例转换回 PHP

我尝试了以下建议,引用函数的输出产生以下错误:

函数的字符串如下:

'SELECT account.hldtoexpalloc('0d6311cc-0d74-4a32-8cf9-87835651e1ee', '0124a045-b2e8-4a9f-b8c4-43b1e4cf638d', '{{\"6c2f7451-bac8-4cdd-87ce-45d55d152078\",5.00},{\"8c2f3421-bac8-4cdd-87ce-45d55d152074\",10.00}}')'

【问题讨论】:

  • 到目前为止您尝试过什么?错误信息?
  • code Query failed: ERROR: array value must start with "{" or dimension information in
  • 我从错误中收集到,这不是对自定义类型的直接数组转换支持,这是否必须是手动字符串构建,或者是否有元类可用于将数组映射到 postgres 自定义输入?
  • 你不必在pg_query_params的参数中写$recipients吗?我不会说 PHP,所以这可能完全不合时宜。如果 PHP 的 Pg 支持不支持简单数组,我会感到有点惊讶。另外,$params 是干什么用的?它似乎没有使用。
  • 很抱歉,$params 应该是传递给 pg_query_params 函数的参数数组,在该对象内部是我试图传递的自定义类型数组。

标签: php postgresql postgresql-9.1


【解决方案1】:

更新:我刚刚注意到您不仅需要数组,还需要使用复合类型的数组。伊克。我从来不需要和他们一起工作,所以我不得不做一些检查。

widgetallocationrequest 数组的正确 PostgreSQL 语法似乎是:

'{"(8c2f3421-bac8-4cdd-87ce-45d55d152074,10.0000)","(6c2f7451-bac8-4cdd-87ce-45d55d152078,5.0000)"}'::widgetallocationrequest[]

查看每个复合类型的行是如何包含在数组{a,b,c} 容器内的"(col1,col2)" 中的?

这是我如何创建值的 PostgreSQL SQL 示例:

-- Create the array of composites from a VALUES() statement
--
SELECT array_agg(x::widgetallocationrequest) 
FROM (VALUES 
    ('8c2f3421-bac8-4cdd-87ce-45d55d152074',10.00),
    ('6c2f7451-bac8-4cdd-87ce-45d55d152078',5.00)
) x;

...以及我如何验证它是有效的:

-- Unpack it back into a row-set of columns
SELECT * FROM unnest('{"(8c2f3421-bac8-4cdd-87ce-45d55d152074,10.0000)","(6c2f7451-bac8-4cdd-87ce-45d55d152078,5.0000)"}'::widgetallocationrequest[]);

现在,PHP 的 Pg 驱动程序甚至不支持数组,更不用说复合类型的数组了,所以你将不得不找其他人写你想要的东西或自己写。编写一个可靠的解析器会很“有趣”,而不是有效地利用时间。

让我们采用另一种方法:生成一个查询,让您通过在 PostgreSQL 中转换为 widgetallocationrequest[] 来合理地调用该函数。

这是一个虚拟函数,其参数与您的真实函数相同,我们将用作调用目标:

CREATE OR REPLACE FUNCTION distributeWidgets(pid uuid, precipients widgetallocationrequest[]) RETURNS boolean AS $$
SELECT 't'::boolean;
$$ LANGUAGE 'sql';

您可以看到它可以使用给您带来很多麻烦的复合数组语法来调用:

SELECT distributewidgets(null, '{"(8c2f3421-bac8-4cdd-87ce-45d55d152074,10.0000)","(6c2f7451-bac8-4cdd-87ce-45d55d152078,5.0000)"}');

...但理想情况下,您希望避免从 PHP 生成任何可怕的东西,并且驱动程序缺少重要功能,因此无法为您完成。

相反,您可以使用TEMPORARY 表来生成参数,INSERT 将每个参数行放入带有常规参数化INSERTs 的表中,然后执行查询以执行函数。

BEGIN;

CREATE TEMPORARY TABLE dw_args ( id uuid, amount numeric(13,4) );

-- Use proper parameterized INSERTs from PHP, this is just an example
INSERT INTO dw_args(id,amount) VALUES ('8c2f3421-bac8-4cdd-87ce-45d55d152074',10.00);
INSERT INTO dw_args(id,amount) VALUES ('6c2f7451-bac8-4cdd-87ce-45d55d152078',5.00);

SELECT distributewidgets(null, array_agg(ROW(x.*)::widgetallocationrequest)) 
FROM dw_args x;

DROP TABLE dw_args;

COMMIT;

警告:如果处理不当,以下内容很容易受到SQL injection 的攻击。如果可能,请使用上述临时表方法。不要成为bobby的下一个受害者;阅读PHP docs on SQL injection

如果出于某种原因绝对有必要在一个语句中运行所有这些,您可以改为使用 PHP 设置的 VALUES 生成查询,然后使用 PostgreSQL 查询将其转换为 widgetallocationrequest[]。我在上面演示了它,但这里是如何将它与对 distributeWidgets(...) 的调用结合起来:

SELECT distributewidgets(null, array_agg(x::widgetallocationrequest)) 
FROM (VALUES 
        ('8c2f3421-bac8-4cdd-87ce-45d55d152074',10.00),
        ('6c2f7451-bac8-4cdd-87ce-45d55d152078',5.00)
) x;

只要您对SQL injection 非常小心,您就可以使用字符串操作在PHP 中轻松构建。

如果可能,请使用临时表方法。

另见PHP array to postgres array

【讨论】:

  • 是的。我是一名 PHP+PGSQL 开发人员,我总是最终手动构建查询。这是最好的!
  • @sudowned 我无法相信没有人处理合并数组。代码就在那里,只是还没有进入 PHP pgsql 驱动程序。让你想知道还有什么是行不通的。复合类型?商店? xml?
  • 我敢打赌,这是因为任何关心语言正常工作的人都已经离开 PHP。前进一步,后退两步。特别是在涉及 OO 支持的情况下。
  • 我试过引用的链接,输出是:{{"6c2f7451-bac8-4cdd-87ce-45d55d152078",5.00},{"8c2f3421-bac8-4cdd-87ce-45d55d152074", 10.00}}
  • 但在 pg_query_params 对象中使用时仍然会引发错误,@sudowned 你是否有一个使用 php 传递数组以供参考的示例,我担心它在那个对象中我不应该都在使用。
【解决方案2】:

«现在,PHP 的 Pg 驱动程序甚至不支持数组,更不用说复合类型的数组了,所以您将不得不寻找其他人来编写您想要的内容或自己编写。编写一个可靠的解析器将是“有趣的”,而不是有效地利用时间。»

你可以使用Pomm's converter system它解析数组和HStore,你甚至可以定义你自己的类型转换器,它会处理它的数组。

希望对您有所帮助。

【讨论】:

  • 谢谢。似乎有人必须实现了它,但文档中没有任何内容。 PHP 是否真的很难将补丁添加到核心代码库中?
  • PDO 真是一团糟,你不想那样做 :)
猜你喜欢
  • 2012-07-19
  • 2013-01-16
  • 2012-10-30
  • 2019-02-26
  • 2012-08-14
  • 2011-09-24
  • 2012-06-01
  • 1970-01-01
相关资源
最近更新 更多