Laravel:一般错误:1615 准备语句需要重新准备

25
我正在使用最新的Laravel版本(5.1)在一个Homestead虚拟机(vagrant)上。我将我的项目连接到本地的MariaDB服务器,在其中有一些表和2个数据库视图。
自从我只对数据库视图表进行了一些选择后,我随机收到以下错误:
“General error: 1615 Prepared statement needs to be re-prepared”
从今天开始,我总是在对数据库视图进行仅选择时得到此错误。 如果我打开我的phpMyAdmin并进行相同的选择,则返回正确的结果。
我尝试打开“php artisan tinker”并选择db-view的一个记录,但返回相同的错误:
// Select one user from user table
>>> $user = new App\User
=> <App\User #000000006dc32a890000000129f667d2> {}
>>> $user = App\User::find(1);
=> <App\User #000000006dc32a9e0000000129f667d2> {
       id: 1,
       name: "Luca",
       email: "luca@email.it",
       customerId: 1,
       created_at: "2015-08-06 04:17:57",
       updated_at: "2015-08-11 12:39:01"
   }
>>> 
// Select one source from Source db-view
>>> $source = new App\Source
=> <App\Source #000000006dc32a820000000129f667d2> {}
>>> $source = App\Source::find(1);
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared (SQL: select * from `sources` where `sources`.`id` = 1 limit 1)'

我该如何解决这个问题?我读到了关于mysqldump的问题(但不是我的情况),并且要增加table_definition_cache的值,但不能确定它是否有效,而且我无法进行修改。

这是一种laravel的bug吗?

我该如何找出答案?


编辑:

如所请求,我添加了我的模型源代码。Source.php:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Source extends Model
{
    protected $table = 'sources';


    /*
    |--------------------------------------------------------------------------
    | FOREIGN KEYS
    |--------------------------------------------------------------------------
    */

    /**
     * 
     * @return [type] [description]
     */
    public function customersList(){
        return $this->hasMany("App\CustomerSource", "sourceId", "id");
    }


    /**
     * 
     * @return [type] [description]
     */
    public function issues(){
        return $this->hasMany("App\Issue", "sourceId", "id");
    }
}

编辑2:

如果我在使用mysqli的项目中执行相同的查询,它可以正常工作:

$db = new mysqli(getenv('DB_HOST'), getenv('DB_USERNAME'), getenv('DB_PASSWORD'), getenv('DB_DATABASE'));
if($db->connect_errno > 0){
    dd('Unable to connect to database [' . $db->connect_error . ']');
}
$sql = "SELECT * FROM `sources` WHERE `id` = 4";
if(!$result = $db->query($sql)){
    dd('There was an error running the query [' . $db->error . ']');
}

dd($result->fetch_assoc());

编辑3: 两个月过去了,我仍然没有解决同样的错误。 我决定尝试在artisan tinker中找到一种小的解决方案,但是没有好消息。 我报告了我尝试过的:

首先尝试获取表模型:

>>> $user = \App\User::find(1);
=> App\User {#697
     id: 1,
     name: "Luca",
     email: "luca.d@company.it",
     customerId: 1,
     created_at: "2015-08-06 04:17:57",
     updated_at: "2015-10-27 11:28:14",
   }

现在尝试获取视图表模型:

>>> $ir = \App\ContentRepository::find(15);
Illuminate\Database\QueryException with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dbname.content_repositories' doesn't exist (SQL: select * from `content_repositories` where `content_repositories`.`id` = 1 limit 1)'

当ContentRepository.php模型中没有正确设置表名时:

>>> $pdo = DB::connection()->getPdo();
=> PDO {#690
     inTransaction: false,
     errorInfo: [
       "00000",
       1146,
       "Table 'dbname.content_repositories' doesn't exist",
     ],
     attributes: [
       "CASE" => NATURAL,
       "ERRMODE" => EXCEPTION,
       "AUTOCOMMIT" => 1,
       "PERSISTENT" => false,
       "DRIVER_NAME" => "mysql",
       "SERVER_INFO" => "Uptime: 2513397  Threads: 12  Questions: 85115742  Slow queries: 6893568  Opens: 1596  Flush tables: 1  Open tables: 936  Queries per second avg: 33.864",
       "ORACLE_NULLS" => NATURAL,
       "CLIENT_VERSION" => "mysqlnd 5.0.11-dev - 20120503 - $Id: id_here $",
       "SERVER_VERSION" => "5.5.5-10.0.17-MariaDB-1~wheezy-wsrep-log",
       "STATEMENT_CLASS" => [
         "PDOStatement",
       ],
       "EMULATE_PREPARES" => 0,
       "CONNECTION_STATUS" => "localiphere via TCP/IP",
       "DEFAULT_FETCH_MODE" => BOTH,
     ],
   }
>>> 

在model ContentRepository.php中更改表格值:
>>> $ir = \App\ContentRepository::find(15);
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared (SQL: select * from `contentRepository` where `contentRepository`.`id` = 15 limit 1)'

当正确时,注意缺少的"errorInfo"

>>> $pdo = DB::connection()->getPdo();
=> PDO {#690
     inTransaction: false,
     attributes: [
       "CASE" => NATURAL,
       "ERRMODE" => EXCEPTION,
       "AUTOCOMMIT" => 1,
       "PERSISTENT" => false,
       "DRIVER_NAME" => "mysql",
       "SERVER_INFO" => "Uptime: 2589441  Threads: 13  Questions: 89348013  Slow queries: 7258017  Opens: 1604  Flush tables: 1  Open tables: 943  Queries per second avg: 34.504",
       "ORACLE_NULLS" => NATURAL,
       "CLIENT_VERSION" => "mysqlnd 5.0.11-dev - 20120503 - $Id: id_here $",
       "SERVER_VERSION" => "5.5.5-10.0.17-MariaDB-1~wheezy-wsrep-log",
       "STATEMENT_CLASS" => [
         "PDOStatement",
       ],
       "EMULATE_PREPARES" => 0,
       "CONNECTION_STATUS" => "localIPhere via TCP/IP",
       "DEFAULT_FETCH_MODE" => BOTH,
     ],
   }

显示数据库的表格:

>>> $tables = DB::select('SHOW TABLES');
=> [
     {#702
       +"Tables_in_dbname": "table_name_there",
     },
     {#683
       +"Tables_in_dbname": "table_name_there",
     },
     {#699
       +"Tables_in_dbname": "table_name_there",
     },
     {#701
       +"Tables_in_dbname": "table_name_there-20150917-1159",
     },
     {#704
       +"Tables_in_dbname": "contentRepository", */ VIEW TABLE IS THERE!!!! /*
     },
     {#707
       +"Tables_in_dbname": "table_name_there",
     },
     {#684
       +"Tables_in_dbname": "table_name_there",
     },
   ]

尝试使用普通选择器:

>>> $results = DB::select('select * from dbname.contentRepository limit 1');
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared (SQL: select * from dbname.contentRepository limit 1)'

尝试未准备好的查询:

>>> DB::unprepared('select * from dbname.contentRepository limit 1')
=> false

尝试第二次未准备好的查询:

>>> DB::unprepared('select * from dbname.contentRepository limit 1')
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active.  Consider using PDOStatement::fetchAll().  Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. (SQL: select * from dbname.contentRepository limit 1)'

尝试使用PDOStatement :: fetchAll():
>>> DB::fetchAll('select * from dbname.contentRepository limit 1'); 
PHP warning:  call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Database\MySqlConnection' does not have a method 'fetchAll' in /Users/luca/company/Laravel/dbname/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php on line 296

尝试使用第二个PDOStatement::fetchAll():

>>> $pdo::fetchAll('select * from dbname.contentRepository limit 1');
  [Symfony\Component\Debug\Exception\FatalErrorException]  
  Call to undefined method PDO::fetchAll()           

尝试语句...

>>> $pdos = DB::statement('select * from dbname.contentRepository limit 1')
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared (SQL: select * from dbname.contentRepository limit 1)'

谢谢您


请添加您的源代码模型。 - Blake
3个回答

77

似乎加入工作了

'options'   => [
                \PDO::ATTR_EMULATE_PREPARES => true
            ]

在 DB 配置中,projectName/config/database.php 文件中。它将像这样:

'mysql' => [
    'driver'    => 'mysql',
    'host'      => env('DB_HOST', 'localhost'),
    'database'  => env('DB_DATABASE', 'forge'),
    'username'  => env('DB_USERNAME', 'forge'),
    'password'  => env('DB_PASSWORD', ''),
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
    'strict'    => false,
    'options'   => [
        \PDO::ATTR_EMULATE_PREPARES => true
    ]
],

Laravel 5.1。希望它能帮到你!

编辑: 我现在正在使用Laravel 8,这个解决方案仍然有效。


10
请注意,不应该推荐这种做法,因为这可能会引入安全漏洞(SQL 注入),因为仿真并非完美无缺。 - David
10
大多数解决方案建议在MySQL配置中增加 table_definition_cache 的值。 - David
2
正如 @David 所提到的,SET GLOBAL table_definition_cache = 1024 对我有用。https://mariadb.com/kb/en/library/server-system-variables/#table_definition_cache - Amirmasoud
2
适用于 Laravel 5.6 :-) - drake24
5
关闭预处理语句仿真会导致其他错误。特别是这个,是一个完全的阻碍问题:https://github.com/laravel/framework/issues/23850。在PHP 7.2中,当保存到MySQL十进制字段时,所有浮点数都会被四舍五入为整数。似乎在MariaDB修复这个问题之前,唯一的选择是将table_definition_cache设置得比服务器上的表总数更大(https://jira.mariadb.org/browse/MDEV-17124)。 - Daniel Howard
显示剩余5条评论

22

7
这个漏洞似乎会特别影响视图。该漏洞已经在 https://jira.mariadb.org/browse/MDEV-17124 提交,并且MariaDB开发人员已经有了一个补丁程序。在补丁发布之前,从讨论中得知需要将table_definition_cache设置为服务器上所有数据库中表的总数以上(SELECT COUNT(*) FROM information_schema.tables;)。 - Daniel Howard
这是更好的解决方案。错误仅在使用VIEWS时出现。在Laravel上执行:\PDO :: ATTR_EMULATE_PREPARES => true,我们会遇到另一个问题:返回的整数字段像字符串一样,并且可能会在某些JS / react等情况下造成问题。设置SET GLOBAL table_definition_cache = 1024,可以轻松解决该问题。 - Henrique Felix
在我的情况下,这个运行得非常完美。谢谢。 - shahsani
这对我在Mariadb和Laravel 8上也适用。 - Heru Handoko

0

看起来这是一个已经记录的MySQL Bug

编辑:

你的模型是否使用'id'作为主键?即使是,我也喜欢在模型中明确设置主键。

protected $primaryKey = 'id'; // 如果不同于id,则绝对需要在此处设置列

您还可以尝试注释掉hasMany()函数,然后再次尝试。有时候Laravel在eagerLoad上可能会做一些奇怪的事情,特别是如果它正在尝试映射大量记录。


正如问题中所写的那样,我已经找到了那个bug,但是我使用的是MariaDB(MySQL的一个分支),我无法增加该属性的值。缺陷报告已被暂停。 - Tenaciousd93
@Tenaciousd93 更新的答案 - Blake
感谢您的编辑。不,这不是主键。尝试一下,但它不起作用。我试图注释掉customersList()issues()方法,并调用$s = Source::find(1);,但它也不起作用。 - Tenaciousd93
你的可填列是什么?你可能有保留字或其他破坏查询的内容。 - Blake
你能发布一下你的表结构吗?自从发布这个问题以后,你尝试过其他的方法吗?有什么进展吗? - Blake
SQL视图由2列组成:id和name。它是通过在另一个数据库中的表上进行选择而创建的。这是可以的,因为phpmyadmin的查询工作良好,并且Laravel项目中的php mysqli代码也可以正常工作。现在我将这2个数据库视图转换为2个表,它可以工作。但问题并没有解决...您是否尝试使用artisan tinker查询SQL视图表?谢谢。 - Tenaciousd93

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接