3

查询 1。

我尝试使用 PHP 运行它:

<?php

$pdo = new \PDO('pgsql:host=localhost;dbname=postgres', 'postgres', 'postgres');

$sql = <<<SQL
SELECT *
FROM (
  SELECT 'CHAC TECHNOLOG*' as alias
  UNION 
  SELECT 'KINDERY LIGHTING SALES DE?T*'
) m 
JOIN (
  SELECT 'CHACTECHNOLOGICO\\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 
ORDER BY ie_clean;
SQL;

echo $sql . PHP_EOL . PHP_EOL;

$stmt = $pdo->query($sql);

print_r($stmt->fetchAll(PDO::FETCH_ASSOC));

我有下一个输出:

SELECT * 
FROM (
  SELECT 'CHAC TECHNOLOG*' as alias
  UNION 
  SELECT 'KINDERY LIGHTING SALES DE?T*'
) m 
JOIN (
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 
ORDER BY ie_clean;

Array
(
    [0] => Array
        (
            [alias] => CHAC TECHNOLOG*
            [ie_clean] => CHACTECHNOLOGICO\
        )

)

只有一个记录(这是错误的)。

但是当我尝试直接在 PostgreSQL 中运行它时,这个查询返回了两条记录。是正确的结果。

https://www.db-fiddle.com/f/qNZY5SauB87na2pWf8uwxm/0

查询 2。

这是类似的查询,但现在我将部分条件从部分WHERE移至SELECT部分:

<?php

$pdo = new \PDO('pgsql:host=localhost;dbname=postgres', 'postgres', 'postgres');

$sql = <<<SQL
SELECT *
FROM (
  SELECT REPLACE(REPLACE(REPLACE('CHAC TECHNOLOG*', '*', '%'), '?', '_'), ' ', '') as alias
  UNION
  SELECT REPLACE(REPLACE(REPLACE('KINDERY LIGHTING SALES DE?T*', '*', '%'), '?', '_'), ' ', '')
) m
JOIN (
  SELECT 'CHACTECHNOLOGICO\\' as ie_clean
  UNION
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE m.alias
ORDER BY ie_clean;
SQL;

echo $sql . PHP_EOL . PHP_EOL;

$stmt = $pdo->query($sql);

print_r($stmt->fetchAll(PDO::FETCH_ASSOC));

输出是:

SELECT *
FROM (
  SELECT REPLACE(REPLACE(REPLACE('CHAC TECHNOLOG*', '*', '%'), '?', '_'), ' ', '') as alias
  UNION
  SELECT REPLACE(REPLACE(REPLACE('KINDERY LIGHTING SALES DE?T*', '*', '%'), '?', '_'), ' ', '')
) m
JOIN (
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE m.alias
ORDER BY ie_clean;

Array
(
    [0] => Array
        (
            [alias] => CHACTECHNOLOG%
            [ie_clean] => CHACTECHNOLOGICO\
        )

    [1] => Array
        (
            [alias] => KINDERYLIGHTINGSALESDE_T%
            [ie_clean] => KINDERYLIGHTINGSALESDEPT
        )

)

两个记录!是正确的结果。

Postgres 也返回两条记录(它是正确的):

https://www.db-fiddle.com/f/nSv1Tg9YJMgfUUhn7urFyF/0

问题

我认为问题出在 中的尾部斜线'CHACTECHNOLOGICO\\',但我重新检查了它,并且我认为它是正确的。

为什么查询 1 从 PHP 中只返回一条记录。是 PDO 的错误还是我做错了什么?

UPD

https://bugs.php.net/bug.php?id=78534

4

2 回答 2

3

您的问题的根源是 REPLACE 函数之一内的问号。

到达服务器的实际查询看起来像

SELECT *
FROM (
  SELECT 'CHAC TECHNOLOG*' as alias
  UNION
  SELECT 'KINDERY LIGHTING SALES DE?T*'
) m
JOIN (
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '$1', '_'), ' ', '')
ORDER BY ie_clean;

所以 PDO 取代了? $1并且查询的第二部分变得无效,导致 1 行

您必须以某种方式绑定该值(什么是棘手的并且可能不起作用)或更改您的查询使用?

试试这个看看实际结果(不推荐,这里有很多关于这个选项的讨论)

$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);

这是一个 PostgreSQL 日志

于 2019-09-12T12:06:56.930 回答
1

总之,反斜杠会影响 PDO 中的查询生成。

我会说这是 PHP 和 Postgresql 策略之间的错误或冲突。(请参阅附加信息)

在 PHP PDO 中,

期待正常行为

--RAW QUERY
SELECT ?
--PHP parsed
SELECT ?
--Postgresql received
SELECT $1
--RAW QUERY
SELECT '?'
--PHP parsed
SELECT '?'
--Postgresql received
SELECT '?'

意外的异常行为

--RAW QUERY
SELECT '?\'
  UNION
SELECT '?'
--PHP parsed
SELECT '?\'
  UNION
SELECT '?'
--Postgresql received
SELECT '?\'
  UNION
SELECT '$1'

请比较下一个例子。我将重点关注下一行两行。

A线

SELECT 'CHACTECHNOLOGICO\\' as ie_clean

和 B 线

) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '')

示例1

LINE A use double backslash  \\  
LINE B's ? will become $1
//RAW QUERY
...
  SELECT 'CHACTECHNOLOGICO\\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 

--PHP parsed \\ => \
...
  SELECT 'CHACTECHNOLOGICO\' as ie_clean  /* double became single */
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 

--Postgresql received  ? => $1
...
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '$1', '_'), ' ', '') 

示例2

LINE A use single backslash  \  
LINE B's ? will become $1
//RAW QUERY
...
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 

--PHP parsed  \ => \
...
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '')


--Postgresql received ? => $1
...
  SELECT 'CHACTECHNOLOGICO\' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '$1', '_'), ' ', '') 

示例3

LINE A does not use any backslash 
LINE B's ? will become ?

问号(?)不会更改为 $1没有反斜杠\

//RAW QUERY
...
  SELECT 'CHACTECHNOLOGICO' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 

--PHP parsed
...
  SELECT 'CHACTECHNOLOGICO' as ie_clean
  UNION
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '')

--Postgresql received ? => ?
...
  SELECT 'CHACTECHNOLOGICO' as ie_clean
  UNION 
  SELECT 'KINDERYLIGHTINGSALESDEPT' as ie_clean
) t ON t.ie_clean ILIKE REPLACE(REPLACE(REPLACE(m.alias, '*', '%'), '?', '_'), ' ', '') 

附加信息

$sql = <<<SQL
SELECT 'CHACTECHNOLOGICO\\' as ie_clean
SQL;

PHP 将上述解析为(单 \

SELECT 'CHACTECHNOLOGICO\' as ie_clean

在 postgresql 中发现上述行的 OUTPUT 因版本而异。

//v9.0.4 use \ to escape character
SQLSTATE[42601]: Syntax error: 7 ERROR:  unterminated quoted string at or near "'CHACTECHNOLOGICO\' as ie_clean"
LINE 1: SELECT 'CHACTECHNOLOGICO\' as ie_clean

//v9.3+ treats \ as character
CHACTECHNOLOGICO\

Postgresql 更改了“\”的语法

pdo_pgsql 可能不会相应地更新。

于 2019-09-15T10:56:18.980 回答