2

I'm trying to use the System Versioned Tables feature of MariaDB 10.3 in Laravel. However, I'm getting the following exception whenever I try to run the artisan migration that alters the table.

C:\htdocs\Computers>php artisan migrate:fresh
Dropped all tables successfully.
Migration table created successfully.

   PDOException  : SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'row_start'

  at C:\htdocs\Computers\database\migrations\2018_07_14_191112_add_system_version_to_computers.php:16
    12|      * @return void
    13|      */
    14|     public function up()
    15|     {
  > 16|         DB::connection()->getPdo()->exec("ALTER TABLE `COMPUTERS` ADD SYSTEM VERSIONING;");
    17|     }
    18|
    19|     /**
    20|      * Reverse the migrations.

  Exception trace:

  1   PDO::exec("ALTER TABLE `COMPUTERS` ADD SYSTEM VERSIONING;")
      C:\htdocs\Computers\database\migrations\2018_07_14_191112_add_system_version_to_computers.php:16

  2   AddSystemVersionToComputers::up()
      C:\htdocs\Computers\vendor\laravel\framework\src\Illuminate\Database\Migrations\Migrator.php:359

  Please use the argument -v to see more details.
  1. I've tried this on an empty table as well as one that already has data

  2. I've tried suppressing the PDO exceptions with the following but it acts as if it works but the system versioning is not added when I check the table

    DB::connection()->getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
    
  3. I am able to add system versioning to the table if I execute the same SQL in HeidiSQL

  4. I've searched through the Laravel documentation and code for any methods that would allow me to add this table option when created but couldn't find anything.

I could manually work around this but I really want to include this in the Laravel migration files so it will be included in the Git repository.


Result of running command

SHOW CREATE TABLE `computers`;

...

+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                                                                                                                                                                                                          |
+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| computers | CREATE TABLE `computers` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `computers_name_unique` (`Name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.000 sec)

Below are the results of running the same SQL statement from the MariaDB command line. Notice the addition of "WITH SYSTEM VERSIONING" in the table definition.

MariaDB [computers]> ALTER TABLE COMPUTERS ADD SYSTEM VERSIONING;
Query OK, 0 rows affected (0.025 sec)
Records: 0  Duplicates: 0  Warnings: 0

MariaDB [computers]> SHOW CREATE TABLE `computers`;
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                                                                                                                                                                                                                                 |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| computers | CREATE TABLE `computers` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `computers_name_unique` (`Name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci WITH SYSTEM VERSIONING |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.002 sec)

Ran the same alter table command in Artisan Tinker and got same error:

    >>>             DB::connection()->getPdo()->exec("ALTER TABLE `COMPUTERS` ADD SYSTEM VERSIONING;");
PDOException with message 'SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'row_start''

Still get the same error if I try to grant all for computers-lcl user.

DROP USER IF EXISTS `computers-lcl`@`localhost`;
FLUSH PRIVILEGES;
GRANT ALL ON *.* TO `computers-lcl`@`localhost` IDENTIFIED BY 'qej8lOjoc81Ekub1d26uzerOv332Qa' WITH GRANT OPTION;

Thanks in advance for any guidance given.

Community
  • 1
  • 1
  • What version of PHP? – Rick James Jul 15 '18 at 15:29
  • Hi Rick! Sorry I forgot to tag that. Specifically it is 7.2.7. – Eric Medlin Jul 16 '18 at 18:24
  • Invalid default value for 'row_start', whats default value for that? – flakerimi Jul 16 '18 at 18:29
  • this is not laravel related, check if you can do it from command line then use that as laravel raw – flakerimi Jul 16 '18 at 18:34
  • @flakerimi there is no way I can see to tell what the default value is because the "ALTER TABLE `COMPUTERS` ADD SYSTEM VERSIONING;" statement throws the exception and when I try to see what show the table nothing has changed. I'll edit the original question to include the table definition. – Eric Medlin Jul 17 '18 at 09:10
  • @flakerimi running the SQL statement from the MariaDB command line works. I'll edit the original question to include the console output. – Eric Medlin Jul 17 '18 at 09:26
  • Btw, I tried running the following in tinker and got same message. See last update in question. – Eric Medlin Jul 17 '18 at 16:21
  • @flakerimi finally made a little progress on this. When I ran the the ALTER statement that worked from the command line, I was logged in as root. Laravel was using a user that was granted all privileges for the computers DB. If I create procedure in Laravel migration and call it in a migration it gives same error. If I create the procedure from root and then I call it from Laravel migration with the same user for computers it works fine. The dedicated user for the computers database still has the same problem even if I give it all global privileges. Seems to only work with root user. – Eric Medlin Jul 19 '18 at 01:28
  • Maybe you need https://serverfault.com/a/826374/76964 Grant – flakerimi Jul 19 '18 at 12:50
  • @flakerimi thanks for the suggestion, but still getting same error. Please see my question update. – Eric Medlin Jul 20 '18 at 09:20

1 Answers1

0

TL;DR: The issue is caused by having NO_ZERO_DATE set in your sql_mode used by laravel. Set 'strict' => false in the 'mysql' => [] part in config/database.php


I stumbled upon the problem myself and I'm quite sure we're dealing with the same issue and it's caused by having NO_ZERO_DATE set in your sql_mode.

Since the ALTER TABLE statement works fine when you're executing the statement via the CLI-client or HeidiSQL (or any other DB GUI for that matter), compare the sql_mode from that client to the sql_mode from your laravel database session.

Here is my SQL_MODE used by the CLI-client:

mysql -u root -p    
MariaDB [(none)]> SELECT @@SQL_MODE;
+-----------------------------------------------------------------------+
| @@SQL_MODE                                                            |
+-----------------------------------------------------------------------+
| ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------+
1 row in set (0.000 sec)

And here is my SQL_MODE used by laravel:

php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.4-1+ubuntu18.10.1+deb.sury.org+3 — cli) by Justin Hileman
>>> DB::select( DB::raw("SELECT @@SQL_MODE") );
=> [
     {#3102
       +"@@SQL_MODE": "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
     },
   ]

As you can see, laravel is using NO_ZERO_DATE while my command-line-client is not. This is due to the strict mode which is the default setting in config/database.php for MySQL databases. If 'strict' => true, the following code is triggered during DB-connection setup in laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:

    /**
     * Get the query to enable strict mode.
     *
     * @param  \PDO  $connection
     * @return string
     */
    protected function strictMode(PDO $connection)
    {
        if (version_compare($connection->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11') >= 0) {
            return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'";
        }

        return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'";
    }

And there is the the guilty NO_ZERO_DATE. Set 'strict' => false in config/database.php and laravel won't fiddle with your sql_mode anymore. If you only want to remove the NO_ZERO_DATE part while keeping the rest of the strict-mode, one solution could be providing your own MySqlConnector ServiceProvider.

morten.c
  • 3,414
  • 5
  • 40
  • 45