How to permanently disable foreign key check - mysql

I have to cooperate with an old school programmer. A long time ago he set up MySQL database. When he created it, he used his primary language as a language for naming tables, columns etc.
Now I would like to have a relation graph for this database and I need it for Prisma to figure out relations between tables, thus I would like to add foreign keys, but I cannot add them easily as he does violate them.
I suppose the easiest thing to do would be to disable foreign key check SET foreign_key_checks = 0;, but this only ever lasts session. Is there a way around this?
Can I do something SET foreign_key_checks = 0;, but for every connected user, and ideal after DB restart too?

https://dev.mysql.com/doc/refman/8.0/en/set-variable.html
Sets it only current session, even same user upon next reconnect won't have this setting.
SET [SESSION] key = value (SESSION is default and thus optional)
Sets it to all users, till next restart of MySQL server happens.
SET GLOBAL key = value
Same as global, but will also apply after restart. This will, in fact, change the config file of the MySQL server.
SET PERSIST key = value (PERSIST is only available since v8)
There is also PERSIST_ONLY which will apply the effect only after a restart.

Related

Why does Django drop the SQL DEFAULT constraint when adding a new column?

In the latest Django (2.2), when I add a new field to a model like this:
new_field= models.BooleanField(default=False)
Django runs the following commands for MySQL:
ALTER TABLE `app_mymodel` ADD COLUMN `new_field` bool DEFAULT b'0' NOT NULL;
ALTER TABLE `app_mymodel` ALTER COLUMN `new_field` DROP DEFAULT;
COMMIT;
While this works when everything is updated, this is very problematic because old versions of the application can no longer create models after this migration is run (they do not know about new_field). Why not just keep the DEFAULTconstraint?
Why not just keep the DEFAULT constraint?
Because Django handles the default model field option at the application level, not the database level. So the real question is why it sets the DEFAULT constraint at all.
But first: Django does not use database-level defaults. (From the documentation: "Django never sets database defaults and always applies them in the Django ORM code."). This has been true from the beginning of the project. There has always been some interest in changing it (the first issue on the subject is 14 years old), and certainly other frameworks (Rails, I think, and SQLAlchemy) have shown that it is possible.
But there are good reasons beyond backwards compatibility for handling defaults at the application level. Such as: the ability to express arbitrarily complex computations; not having to worry about subtle incompatibilities across database engines; the ability to instantiate a new instance in code and have immediate access to the default value; the ability to present the default value to users in forms; and more.
Based on the two most recent discussions on the subject, I'd say there's little appetite to incorporate database defaults into the semantics of default, but there is support for adding a new db_default option.
Now, adding a new non-nullable field to an existing database is a very different use case. In that situation, you have to provide a default to the database for it to perform the operation. makemigrations will try to infer the right value from your default option if it can, and if not it will force you to specify a value from the command line. So the DEFAULT modifier is used for this limited purpose and then removed.
As you've noticed, the lack of database-level defaults in Django can make continuous deployment harder. But the solution is fairly straightforward: just re-add the default yourself in a migration. One of the great benefits of the migrations system is that it makes it easy to make arbitrary, repeatable, testable changes to your database outside of Django's ORM. So just add a new RunSQL migration operation:
operations = [
# Add SQL for both forward and reverse operations
migrations.RunSQL("ALTER TABLE app_mymodel ALTER COLUMN new_field SET DEFAULT 0;",
"ALTER TABLE app_mymodel ALTER COLUMN new_field DROP DEFAULT;")
]
You can put that in a new migration file or simply edit the automatically generated one. Depending on your database and its support for transactional DDL, the sequence of operations may or may not be atomic.
I found this ticket from 2 years ago: https://code.djangoproject.com/ticket/28000
It is stated in there that:
Django uses database defaults to set values on existing rows in a table. It doesn't leave the default values in the database so dropping the default is correct behavior. There might be an optimization not to set/drop the default in this case -- I'm not sure it's needed since the column isn't null. A separate ticket could be opened for this.
I also saw the same reference in another question here: Django Postgresql dropping column defaults at migrate
And searching a bit more I came upon this SO question: Django implementation of default value in database that led to the code of the _alter_field method from django.db.backends.base.schema where this comment exists:
# When changing a column NULL constraint to NOT NULL with a given
# default value, we need to perform 4 steps:
# 1. Add a default for new incoming writes
# 2. Update existing NULL rows with new default
# 3. Replace NULL constraint with NOT NULL
# 4. Drop the default again.
Although the last one is about altering an existing nullable field to a non-nullable, this seems to be the way that Django handles the default case :/

Which is the better, dropping and recreating FK constraints OR Disabling and enabling FK checks?

I have few columns in various tables of MYSQL database which were allocated more length than actually needed. So now I try to make them of proper length. They were VARCHAR(64) and I want to make them CHAR(36). Those columns are involved in Foreign Keys. These changes are going to be a new SQL file, which is run with Flyway Engine. Which of the two options is better?
1) drop the constraints and modify columns and recreate constraints.
2) execute set foreign_key_checks=0, alter columns and execute set foreign_key_checks=1.
Since you are going to change the length its better to disable foreign_key_checks. Because it will affect only your current session (unless you mention global). so that it will not affect other session which depends on foreign key.

prevent delete * from table unless primary key specified

I want to prevent user from using deleting * from table unless primary key specified, one of our team member accendently used "delete * from table_name" i want to prevent such scenarios in future.
Would safe updates be viable for you? This is an option you can enable on the command line, in the option file or set a variable in SQL code that prevents updates and deletes without a where clause that includes the key columns defining the rows to change.
In MySQL Workbench there is a setting in Preferences -> SQL Editor -> Safe Updates (rejects UPDATEs and DELETEs with no restriction). I believe this is even on by default.

Clarification required for the 'Disable foreign key checks' option

I'm looking for some clarification on a statement from the MySQL docs
I intend to export a DB with the Disable foreign key checks option selected, as doing it without causes an error when I import the DB. However, after reading the docs I'm left with two questions with regard to importing the DB -
Will foreign_key_checks be set back to 1 automatically after the import for that DB, or do I have to do it manually?
will the Foreign Keys already in place when the DB was exported still be valid?
The relevant part of the docs reads as below, which to me is not particularly clear -
Setting foreign_key_checks to 1 does not trigger a scan of the existing table data. Therefore, rows added to the table while foreign_key_checks = 0 will not be verified for consistency.
http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html
See the big table at the start. foreign_key_checks setting is session based. You do not have to reset it back to 1 yourself. (Although it doesn't hurt.)
Yes. No actual relations or constraints will be affected. Only the checking of those constraints during the time when the checks are off.

Check for referential integrity break

In my process, I do something like:
SET FOREIGN_KEY_CHECKS = 0;
LOAD DATA INFILE '/path/to/mytable.txt' INTO TABLE mytable;
SET FOREIGN_KEY_CHECKS = 1;
Now, I need to check that the data after this import is not breaking the referential integrity. I would like to do something like
check database all foreign_keys;
Is a similar command exists? If not, how to do this control?
Environment: MySQL v5.1.xx with InnoDB
Thanks
Answer
Here is some code which does what you need. It looks like there's no such command.
History
OK, I'm not a MySQL expert but referential integrity is managed constantly unless you disable it. You cannot insert a row into a table which violates a constraint unless you've dropped or disabled the constraint first. There's no need to "check" them.
If you did "disable" them, then enabling them will force a check.
This is in fact completely wrong and very scary indeed.
at least in 5.1
I think if they had that function, they would just call it when you re-enabled the constraints, so I doubt you'll find it in the server.
The above link is dead, sadly.
The script mentioned in this blog post does a nice job of showing FKs which aren't referenced (though it will also show them when the FK is nullable, so may be legitimately null, so not always helpful!):
http://www.mysqlperformanceblog.com/2011/11/18/eventual-consistency-in-mysql/