【问题标题】:Getting SQL query Syntax error or access violation while running raw SQL in yii2在 yii2 中运行原始 SQL 时出现 SQL 查询语法错误或访问冲突
【发布时间】:2018-07-08 05:03:34
【问题描述】:

我正在尝试创建一个搜索功能,以便能够搜索产品并按相关性过滤结果,但查询后出现语法错误。 以下是我也遇到的错误

 sql error
    SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'mens', 'winter', 'jacket'%',6,0) + if (title LIKE '%'mens'%',5,0) + if (title LI' at line 5
The SQL being executed was: SELECT p.product_id,p.title,p.price,p.unit_sold,
p.profile_img,p.store_name,p.item_number,
(
(-- Title score
if (title LIKE '%'mens', 'winter', 'jacket'%',6,0) + if (title LIKE '%'mens'%',5,0) + if (title LIKE '%'winter'%',5,0) + if (title LIKE '%'jacket'%',5,0)
)+
(-- description
if (description LIKE '%'mens', 'winter', 'jacket'%',5,0) + if (description LIKE '%'mens'%',4,0) + if (description LIKE '%'winter'%',4,0) + if (description LIKE '%'jacket'%',4,0)
)

) as relevance
FROM products p
WHERE p.is_active = '1'
HAVING relevance > 0
ORDER BY relevance DESC,p.unit_sold DESC
LIMIT 10

和搜索功能

function search($q){
            if (mb_strlen(trim($q))===0){
                // no need for empty search
                return false; 
            }
            $query = $this->limitChars(trim($q));

            // Weighing scores
            $scoreFullTitle = 6;
            $scoreTitleKeyword = 5;
            $scoreFullDescription = 5;
            $scoreDescriptionKeyword = 4;

            $keywords = $this->filterSearchKeys($query);
            $escQuery = $this->escape($keywords); 
            $titleSQL = array();
            $descSQL = array();

            /** Matching full occurences **/
            if (count($keywords) > 1){
                $titleSQL[] = "if (title LIKE '%".$escQuery."%',{$scoreFullTitle},0)";
                $descSQL[] = "if (description LIKE '%".$escQuery."%',{$scoreFullDescription},0)";

            /** Matching Keywords **/
            foreach($keywords as $key){
                $titleSQL[] = "if (title LIKE '%".Yii::$app->db->quoteValue($key)."%',{$scoreTitleKeyword},0)";
                $descSQL[] = "if (description LIKE '%".Yii::$app->db->quoteValue($key)."%',{$scoreDescriptionKeyword},0)";
            }

            //add 0 is query string is empty to avoid error
            if (empty($titleSQL)){
                $titleSQL[] = 0;
            }
            if (empty($descSQL)){
                $descSQL[] = 0;
            }
            $sql = "SELECT p.product_id,p.title,p.price,p.unit_sold,
                    p.profile_img,p.store_name,p.item_number,
                    (
                        (-- Title score
                        ".implode(" + ", $titleSQL)."
                        )+
                        (-- description
                        ".implode(" + ", $descSQL)." 
                        )

                    ) as relevance
                    FROM products p
                    WHERE p.is_active = '1'
                    HAVING relevance > 0
                    ORDER BY relevance DESC,p.unit_sold DESC
                    LIMIT 10";
            $results = Yii::$app->db->createCommand($sql)->queryAll();
            if (!$results){
                return false;
            }
            return $results;
        }

我也在使用 escape() 方法来转义查询字符串以避免 sql 注入,但我不太相信这是最佳实践,因为 escape 方法的作用是在字符串周围添加单引号,这反过来会甚至没有返回表中的任何匹配项,我也尝试使用 mysqli_escape_string() 但也无法使其工作,所以我想知道 Yii2 中转义查询字符串和避免 sql 注入攻击的最佳做法是什么。

function escape($values)
        {
                $values = (array)$values;
                $escaped = array();
                foreach($values as $value) {
                        if(!is_scalar($value)) {
                                throw new CException('One of the values passed to values() is not a scalar.');
                        }
                        $escaped[] = Yii::$app->db->quoteValue($value);
                }
                return implode(', ', $escaped);
        }

【问题讨论】:

  • 你想实现什么查询?
  • @everyone 感谢大家,但我已经能够解决由 Yii::$app->db->quoteValue($value); 创建的单引号引起的问题删除功能后,它可以正常工作
  • 没有它,你很容易受到 SQL 注入的攻击,所以你可能只是在自己的脚上开枪......
  • @rob006 是的,您是完全正确的,并且仍在搜索,但有一种完美的方法来转义查询字符串以避免 SQL 注入,因为使用 quoteValue($value) 方法,代码不会工作所以我需要一种方法来逃避它我试过 mysqli_escape_string();但是由于我需要一个连接而无法使其工作并且我提供了 Yii::$app->db 但出现错误,即第一个 arg 应该是 mysqli 但对象已通过,请问有没有针对此问题的解决方法,我知道我不会那样离开它,因为它很容易受到攻击,但我删除它以确定错误来自哪里,
  • 你仍然没有解释这个查询应该是什么样子,也没有分享这个“工作”代码。你需要像if (title LIKE '%mens, winter, jacket%',6,0)这样的东西吗?

标签: mysql sql yii yii2


【解决方案1】:

您应该转义LIKE 的整个表达式,包括% 通配符:

$value = Yii::$app->db->quoteValue('%' . implode(', ', (array) $keywords) . '%');
$titleSQL[] = "if (title LIKE $value,$scoreFullTitle,0)";

这将产生类似的东西:

if (title LIKE '%mens, winter, jacket%',6,0)

【讨论】:

    猜你喜欢
    • 2019-02-04
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-29
    • 2020-09-29
    • 2014-04-29
    • 2019-08-08
    相关资源
    最近更新 更多