Laravel:一般错误:1615 Prepared statement需要重新准备

Posted

技术标签:

【中文标题】Laravel:一般错误:1615 Prepared statement需要重新准备【英文标题】:Laravel: General error: 1615 Prepared statement needs to be re-prepared 【发布时间】:2015-11-04 14:29:17 【问题描述】:

我在宅基地虚拟机 (vagrant) 中使用最新版本的 laravel (5.1)。

我将我的项目连接到本地 mariaDB 服务器,其中我有一些表和 2 个 db-view。

由于我仅在 db-view 表上进行了一些选择,因此我随机收到此错误:

一般错误:1615 Prepared statement需要重新准备

从今天开始,当只在数据库视图上进行选择时,我总是会收到此错误。 如果我打开我的 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 吗?

我怎么知道?


编辑:

按照要求,我添加了我的模型源代码。 源码.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: 2个月后,我还在。同样的错误,没有找到解决方案。 我决定在 aritsan 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 在模型 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,
     ],
   
>>> 

在模型 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,
     ],
   

显示 db 的表:

>>> $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()           

Try 语句...:

>>> $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)'

谢谢

【问题讨论】:

请添加您的源模型。 【参考方案1】:

似乎可以添加

'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 上,这个解决方案仍然有效。

【讨论】:

请注意,不建议这样做,因为这可能会引入安全漏洞(SQL 注入),因为模拟并非完美无缺。 大多数解决方案建议在 MySQL 配置中增加 table_definition_cache 的值。 正如@David 提到的,SET GLOBAL table_definition_cache = 1024 为我工作。 mariadb.com/kb/en/library/server-system-variables/… 适用于 Laravel 5.6 :-) 关闭准备好的语句模拟会导致其他错误。特别是,这个是一个总的节目停止者:github.com/laravel/framework/issues/23850。在 PHP 7.2 上,当保存在 mysql 十进制字段中时,浮点数都四舍五入为整数。似乎在 MariaDB 中修复错误之前,唯一的选择是让 table_definition_cache 大于服务器上的表总数 (jira.mariadb.org/browse/MDEV-17124)。【参考方案2】:

根据已接受答案中的 cmets, 运行

SET GLOBAL table_definition_cache = 1024

在 MariaDB 中解决了这个问题。

https://mariadb.com/kb/en/library/server-system-variables/#table_definition_cache

【讨论】:

这个错误似乎特别影响视图。该错误已在此处提交jira.mariadb.org/browse/MDEV-17124,MariaDB 开发人员已经为它提供了补丁。在推出之前,从讨论看来,table_definition_cache 的设置必须超过服务器上所有数据库的表总数(SELECT COUNT(*) FROM information_schema.tables;)【参考方案3】:

似乎这是一个已记录在案的MySQL Bug。

编辑:

您的模型是否使用“id”作为主键?我喜欢在模型中显式设置主键,即使是这样。

protected $primaryKey = 'id'; // If different than id, definitely need to set the column here

您也可以尝试注释掉hasMany() 函数并重试。有时 Laravel 会在 eagerLoad 上做一些奇怪的事情,尤其是当它试图映​​射到很多记录时。

【讨论】:

正如问题中所写,我已经发现了该错误,但我有 mariaDB(MySQL 的分支),我无法增加该属性的值。错误报告已暂停。 @Tenaciousd93 更新答案 感谢您的编辑。不,它不是主键。无论如何尝试它,但它不起作用。我尝试评论 customersList()issues() 方法并调用 $s = Source::find(1); 但它也不起作用。 您的可填充列是什么?您可能有保留字或其他破坏查询的内容。 你能发布你的表结构吗?自发布此消息以来,您是否尝试过其他任何方法?运气好吗?

以上是关于Laravel:一般错误:1615 Prepared statement需要重新准备的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.4 给出了错误的 COM_STMT_PREPARE 响应大小

如何从 SQL 中的视图中选择? (一般错误:1615 Prepared statement需要重新准备)

SQLSTATE[HY000]:一般错误:无法创建表

1615 准备好的语句需要在codeigniter中重新准备

在 null sanctun laravel mongodb 上调用成员函数 prepare()

Laravel 5.1 GCM 推送通知 SSL 错误