简体   繁体   中英

Why would you set `null: false, default: “”` on a required DB column?

I'm building a Rails app with Devise for authentication, and its default DB migration sets the following columns:

## Database authenticatable
t.string :email,              null: false, default: ""
t.string :encrypted_password, null: false, default: ""

What is the purpose of setting null: false and default: "" at the same time?

My understanding is that null: false effectively makes a value required: ie, trying to save a record with a NULL value in that column will fail on the database level, without depending any validations on the model.

But then default: "" basically undoes that by simply converting NULL values to empty strings before saving.

I understand that for an optional column, you want to reject NULL values just to make sure that all data within that column is of the same type. However, in this case, email and password are decidedly not optional attributes on a user authentication model. I'm sure there are validations in the model to make sure you can't create a user with an empty email address, but why would you set default: "" here in the first place? Does it serve some benefit or prevent some edge case that I haven't considered?

Broadly speaking:

  • To make a column required, you must set null: false on it. This is true whether you are creating a new table or updating an existing one .
  • And in the event that you're updating an existing table, the DB engine will try to populate that new column with NULL in each row. In such cases, you must override this behavior with default: "" , or else it will conflict with null: false and the migraiton will fail.

With respect to Devise:

Devise uses two separate templates for building migrations: migration.rb , for creating new tables, and migration_existing.rb , for updating existing tables (see source on GitHub ). Both templates call the same migration_data method to generate the lines in question ( ie, the ones that specify null: false, default: "" ), but as mentioned above, default: "" is only really relevant in the latter case (see O. Jones' answer for more).

So the short answer to your question, specifically in the case of Devise migrations, is “because the generator uses borrowed code which doesn't always apply, but still doesn't break anything.”

A consideration for UNIQUE columns:

Note that in most popular SQL engines, uniquely indexed columns can still contain multiple rows of NULL values , as long as they are not required (naturally). But the effect of making a new column both required and unique ( ie, null: false , default: "" , and unique: true ) is that it cannot be added : the DB engine tries to populate the new column with an empty string in each row, which conflicts with the unique constraint and causes the migration to fail.

(The only scenario in which this mechanism fails is if you have exactly one row in your table — it gets a blank string value for the new column, which naturally passes the uniqueness constraint because it's the only record.)

So another way to look at it might be that these options are a safety mechanism preventing you from running migrations that you shouldn't ( ie, retroactively adding required columns to an already-populated table).

Some application software gacks on NULL values but not on zero-length text strings. In Oracle, they're the same thing, but not in MySQL.

Things get interesting upon altering tables to add columns. In that case a default value is mandatory, so the DBMS can populate the new column.

There is a difference in the insertion type. For example, let say you have a new_table table such that:

CREATE TABLE IF NOT EXISTS `new_table` (
  `col1` VARCHAR(10) NOT NULL,
  `col2` VARCHAR(10) NOT NULL DEFAULT '',
  `col3` VARCHAR(10) NULL DEFAULT '');

When you use explicit insert of NULL you'll get the NULL :

INSERT INTO new_table(col1,col2,col3) VALUES('a','b',NULL);
 'a','b',NULL

for col2 same trick will result in error:

INSERT INTO new_table(col1,col2,col3) VALUES('a',NULL,'c');

But when you use implicit insert of NULL you'll get the default value:

INSERT INTO new_table(col1,col2) VALUES('a','b');
 'a','b',''

meaning that setting a default value is not preventing NULL assertion to this column, but only used when the value is not explicitly given.

I'm thinking this is here because of MySQL 'strict mode' not allowing you to disallow a null value without providing a default.

From mysql docs:https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html

For data entry into a NOT NULL column that has no explicit DEFAULT clause, if an INSERT or REPLACE statement includes no value for the column, or an UPDATE statement sets the column to NULL, MySQL handles the column according to the SQL mode in effect at the time: If strict SQL mode is enabled, an error occurs for transactional tables and the statement is rolled back. For nontransactional tables, an error occurs, but if this happens for the second or subsequent row of a multiple-row statement, the preceding rows are inserted. If strict mode is not enabled, MySQL sets the column to the implicit default value for the column data type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM