framework: sql_require_primary_key Causes Tables With String Primary Key To Fail
- Laravel Version: 7
- PHP Version: 7.3.14
- Database Driver & Version: MySQL 8
Description:
When MySQL has sql_require_primary_key
enabled, migrations that call ->primary()
on a column during table creation fail because it appears as though two queries are ran. One to create the table, and then a second to alter the table and add the primary key.
For context, DigitalOcean recently enforced primary keys on all newly created tables in their managed databases.
Steps To Reproduce:
- Ensure
sql_require_primary_key
is enabled on the MySQL server - Create a migration that creates a new table with a string as the primary key (we used
$table->string('string')->primary();
) and does not have a default$table->id();
column - Run the migration
The below error is generated when attempting to run the first query to create table.
General error: 3750 Unable to create or change a table without a primary key, when the system variable 'sql_require_primary_key' is set. Add a primary key to the table or unset this variable to avoid this message. Note that tables without a primary key can cause performance problems in row-based replication, so please consult your DBA before changing this setting.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 28
- Comments: 65 (16 by maintainers)
I’ve had the following response from DigitalOcean regarding this issue:
Therefore I managed to get this to work by calling
\Illuminate\Support\Facades\DB::statement('SET SESSION sql_require_primary_key=0');
above mySchema::create
command in the migrations causing issues.If someone wants to submit a PR to somehow make this one query behind the scenes be our guest 😄
Currently, this is not a “bug” in Laravel. It is a bug in DigitalOcean’s configuration they are forcing upon you.
Unable to do this on managed database instances (DigitalOcean) as far as I’m aware?
One simple solution is add this code at
AppServiceProvider@register
and listen the Migrations eventsOr you can add a config variable, for example
allow_disabled_pk
in config filedatabases.php
and callconfig('databases.allow_disabled_pk')
instead ofenv('ALLOW_DISABLED_PK')
Controlled if you want run
SET SESSION sql_require_primary_key
from the.env
, which normally only on servers you want to do.I’m experiencing the same issue. Unsure if it was a recent change to DigitalOcean’s config but Laravel applications utilising Passport are effectively incompatible with DigitalOcean Managed Databases at the moment.
I don’t think this issue should be closed because current workarounds are not solving the root cause. The problem is that row based replication requires you to define primary keys, so this is not only an issue for DigitalOcean managed databases. Current workarounds with session variables are error prone - you can create tables without primary keys (passwords_reset for example doesn’t have a primary key).
Hello, developers!
I just faced the exact same issue.
If you are running migrations in the server, there is an even better wat, using tinker:
After configuring the connection to the remote database, go to the console in your server and use
php artisan tinker
.Once there, execute
DB::statement('SET SESSION sql_require_primary_key=0');
to disable the check and then executeArtisan::call('migrate --force');
to run all your migrations.Of course, if you are using an SQL file, so put
SET SESSION sql_require_primary_key=0;
in the top of that file and use it.@taylorotwell Would you consider reopening the issue? You say above that it’s a “bug” with the Digital Ocean setup but in reality it’s a recommended and increasingly common way to configure MySQL and simply turning off required primary keys is not a solution. I believe as things stand several first-party laravel packages such as Nova and Telescope contain migrations that simply can’t be run in these setups, and it also makes it a pain to use uuid primary ids.
I did look at the revert commit that was merged earlier but couldn’t find much context about why the fix was reverted so forgive me if there’s a good reason to NOT fix this!
I just want to add, if you use PostgreSQL, everything is working fine. Just for the people who doesn’t know 😃
Yes that’s what I’m doing right now but it’s not ideal as it essentially turns off the primary key requirement completely, since all migrations will run without it meaning there’s no functional difference to just turning it off in the db’s config. Again this is not a solution to the problem, it’s a workaround, and I believe there is value in just fixing it at source
A workaround that worked for me:
SET SESSION sql_require_primary_key=0
first thing in the DB dump.@adddz in order to make it compatible with both locally on MySQL 5.7 and on DigitalOcean MySQL 8 I had to add the following to the
CreateSessionsTable
:This would first check if if the sql_require_primary_key exists and is enabled before trying to temporarily disable it.
Are you using a Tool like TablePlus? Then you can login to the database, execute
SET SESSION sql_require_primary_key=0
manually and import your dump. This works because it’s the same session 😃Laravel 8 + DigitalOcean database
💡 Just add the first statement to your migration:
love you so much
Should be fixed by: https://github.com/laravel/framework/pull/49374
Never ever disable primary keys on HA MySQL and MariaDB clusters as this might lead to corruption of replication between server instances. Probably developers that made this migrations where not aware of how MySQL and MariaDB replication works, especially how Galera, and simply decided as they thought not to add primary keys.
update below solution won’t work if you run tests in sqlite for example. See https://github.com/laravel/framework/issues/33238#issuecomment-648071415 for a simpler workaround.
Same issue here, it affects the
sessions
,password_resets
and the two Telescope tablestelescope_entries_tags
andtelescope_monitoring
as far as I know at this point.For anyone that needs a fix, this is my workaround for now:
CREATE TABLE ...
statement. (TablePlus: open table, at the bottom: [Structure] and then the button [Info] on the right).CREATE TABLE ...
statement with:\DB::statement($query);
sessions
password_resets You could probably also choose
email
as a primary key but in my case I have some extra (tenant related) columns in this table.Laravel Telescope
See https://laravel.com/docs/7.x/telescope#migration-customization on how to use your own migrations instead of the default ones
telescope_entries_tags
telescope_monitoring
I agree that this needs to be solved, however I added that comment in case someone stumbles upon the issue (like I did) regarding sql_require_primary_key and laravel. There’s very few info online on this matter atm.
Digital Ocean has an API that you can use to configure the settings of your DB, which I would highly recommend over other solutions. You can do the following:
Now your database cluster will be configured, permanently, without any code changes.
If anyone else who comes across this thread has a similar issue on Azure.
sql_generate_invisible_primary_key <- Make sure this one is turned off as well 😄
DO engineers team has updated API documentation to make database configuration changes via API; you will now be able to disable
sql_require_primary_key
using API calls.Then they add:
Should this issue be reopened since the fix was reverted and the issue still exists?
Creating a listener on
Illuminate\Database\Events\MigrationsStarted
withDB::statement('SET SESSION sql_require_primary_key=0');
in the handle() worked for me on my DigitalOcean dbs.I did this way, I dumped database and add this line
/*!80013 SET SESSION sql_require_primary_key=0*/;
to the config section in the dumped file.Where in that file should i use it ?
I encountered this issue myself recently and I think I found a better work around. It’s pretty similar to those who suggested dumping the schema using another tool such as TablePlus but uses the new
schema:dump
command and works with the migration system.On the machine you use for development create a fresh database using (this will delete all existing tables and re-create them using your migrations):
Then dump your schema:
Then commit the changes and push them to the server. Now when you run the migrate command, Laravel will restore the database using the dumped schema file before running the migrations.
If you make any changes to the migrations, you will have to re-dump the schema file using this method. Once you are in production though, you shouldn’t have to do that again.
Hope this helps; sorry for commenting on an old issue; thanks Taylor and the Laravel Team for your amazing work 😁
Note: don’t use primary keys and in case of replication failure, deal with the problem and accept also the financial loses for the downtime caused.
The thing is I don’t want to turn off this setting just in order to use uuid primary keys. This is the recommended setting for a reason and i want to retain it. I don’t believe it should be necessary to use a less reliable mysql setup because of the way these migrations run under the hood
I would like to say we decided to remove Telescope from the project because of missing primary keys and infinite Digital Ocean warnings.
DB::statement('SET SESSION sql_require_primary_key=0');
workaround works, but risks remain.Sad, but nothing to do if maintainers refuse of primary keys.
Same, i use the
config('database.allow_disabled_pk')
Only don’t use env() in your other files besides your config files. Or ask DO to change the sql requirement for you.
Hi guys!
I opened a ticket with DO and they changed the parameter for me. So no need for any “hacky” stuff 😉
@usamamuneerchaudhary I have solved the problem in this PR #37715 .
Thank you works for me as well. You can add this into
so it works every time you use the migrate command.
Current workaround is setting
SET sql_require_primary_key=0
, and I set the sql variable.And I change to