Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] Make floating-point types consistent #48861

Merged
merged 4 commits into from
Dec 12, 2023

Conversation

hafezdivandari
Copy link
Contributor

@hafezdivandari hafezdivandari commented Oct 30, 2023

Fixes #3151, #7298, #9089, #9103, #13258, #18776, #21032, #21827, #42308 Related to laravel/ideas#1527 and #47080

This PR makes float and double column types consistent across all databases and also removes unsignedDecimal, unsignedDouble, and unsignedFloat column types.

Why?

Almost all databases have 4-bytes single-precision and 8-bytes double-precision storage types when declaring floating-point types (approximate numeric data values). Current syntax is inconsistent on Laravel and there is no way to declare precision value.

  • As of MySQL 8.0.17, the nonstandard FLOAT(M,D) and DOUBLE(M,D) syntax is deprecated and you should expect support for it to be removed in a future version of MySQL. Source
  • As of MySQL 8.0.17, the UNSIGNED attribute is deprecated for columns of type FLOAT, DOUBLE, and DECIMAL (and any synonyms); you should expect support for it to be removed in a future version of MySQL. Source

Changes

  • Changes $blueprint->float($column, $total = 8, $places = 2, $unsigned = false) to $blueprint->float($column, $precision = 53)
  • Changes $blueprint->double($column, $total = 8, $places = 2, $unsigned = false) to $blueprint->double($column)
  • Changes $blueprint->decimal($column, $total = 8, $places = 2, $unsigned = false) to $blueprint->decimal($column, $total = 8, $places = 2)
  • Removes $blueprint->unsignedFloat()
  • Removes $blueprint->unsignedDouble()
  • Removes $blueprint->unsignedDecimal()

Before this PR

Syntax MySQL PostgreSQL SQL Server SQLite
$table->float('foo') double(8, 2) double precision float float
$table->float('foo', 9) double(9, 2) double precision float float
$table->float('foo', 9, 3) double(9, 3) double precision float float
$table->float('foo', 9, null) double double precision float float
$table->float('foo', null, null) double double precision float float
$table->double('foo') double(8, 2) double precision float float
$table->double('foo', 9) double(9, 2) double precision float float
$table->double('foo', 9, 3) double(9, 3) double precision float float
$table->double('foo', 9, null) double double precision float float
$table->double('foo', null, null) double double precision float float

After this PR

Syntax MySQL PostgreSQL SQL Server SQLite
$table->float('foo') float(53) float(53) float(53) float
$table->float('foo', 24) float(24) float(24) float(24) float
$table->float('foo', null) float float float float
$table->double('foo') double double precision double precision double

Comparison of Floating-Point Types on Different DBs

Syntax MySQL PostgreSQL SQL Server SQLite
FLOAT(M, D) Deprecated N/A N/A Double (REAL)
DOUBLE(M, D) Deprecated N/A N/A Double (REAL)
FLOAT(1 to 24) Single (FLOAT) Single (real) Single (float(24)) Double (REAL)
FLOAT(25 to 53) Double (DOUBLE) Double (double precision) Double (float(53)) Double (REAL)
FLOAT Single (FLOAT) Double (double precision) Double (float(53)) Double (REAL)
DOUBLE Double (DOUBLE) N/A N/A Double (REAL)
DOUBLE PRECISION Double (DOUBLE) Double (double precision) Double (float(53)) Double (REAL)
REAL Double (DOUBLE) Single (real) Single (float(24)) Double (REAL)

Upgrade guide

The double and float column types of database migrations have been rewritten to make these types consistent across all databases:

  • The double column type now creates a DOUBLE equivalent column without total digits and places (digits after decimal point), which is the standard syntax in SQL. Therefore, you may remove the arguments for $total and $places:

    - $table->double('amount', $total = 8, $places = 2, $unsigned = false);
    // `DOUBLE(8, 2)` on MySQL, `DOUBLE PRECISION` on PostgreSQL, and `FLOAT` on SQLite and SQL Server
    + $table->double('amount');
    // `DOUBLE` on MySQL and SQLite, `DOUBLE PRECISION` on PostgreSQL and SQL Server
  • The float column type now creates a FLOAT equivalent column without total digits and places (digits after decimal point), but with an optional $precision specification to determine storage size as a 4-byte single-precision column or an 8-byte double-precision column. Therefore, you may remove the arguments for $total and $places and specify the optional $precision to your desired value and according to your database's documentation. The default $precision is set to 53 that would result in 8-byte double-precision column on all database drivers:

    - $table->float('amount', $total = 8, $places = 2, $unsigned = false);
    // `DOUBLE(8, 2)` on MySQL, `DOUBLE PRECISION` on PostgreSQL, and `FLOAT` on SQLite and SQL Server
    + $table->float('amount', $precision = 53);
    // `FLOAT(53)` on MySQL, PostgreSQL, and SQL Server. `FLOAT` on SQLite
  • The $unsigned parameter of the $table->decimal(), $table->double(), and $table->float() methods has been removed. Actually, this were only supported on MySQL. However, You may force deprecated UNSIGNED attribute using ->unsigned() modifier:

    - $table->decimal('foo', $total = 8, $places = 2, $unsigned = true);
    - $table->double('bar', $total = 8, $places = 2, $unsigned = true);
    - $table->float('baz', $total = 8, $places = 2, $unsigned = true);
    + $table->decimal('foo', $total = 8, $places = 2)->unsigned();
    + $table->double('bar')->unsigned();
    + $table->float('baz', $precision = 53)->unsigned();
  • The unsignedDecimal, unsignedDouble, and unsignedFloat column types have been removed. Therefore, you may use equivalent column types and forcing deprecated UNSIGNED attribute using ->unsigned() modifier. Actually, this were only supported on MySQL:

    - $table->unsignedDecimal('foo', $total = 8, $places = 2);
    - $table->unsignedDouble('bar', $total = 8, $places = 2);
    - $table->unsignedFloat('baz', $total = 8, $places = 2)
    + $table->decimal('foo', $total = 8, $places = 2)->unsigned();
    + $table->double('bar')->unsigned();
    + $table->float('baz', $precision = 53)->unsigned();

@taylorotwell
Copy link
Member

Please provide the documentation that would need to be included in a step by step upgrade for Laravel 11 if this PR was merged.

@taylorotwell
Copy link
Member

Mark as ready for review when done please.

@taylorotwell taylorotwell marked this pull request as draft November 16, 2023 14:36
@hafezdivandari hafezdivandari marked this pull request as ready for review November 16, 2023 16:20
@taylorotwell taylorotwell merged commit 4c1aa68 into laravel:master Dec 12, 2023
16 checks passed
@hafezdivandari hafezdivandari deleted the master-patch-1 branch December 12, 2023 20:52
@hafezdivandari hafezdivandari changed the title [11.x] Make floating-types consistent [11.x] Make floating-point types consistent Jan 19, 2024
@bluedreamer
Copy link

Why was the unsigned removed from decimal()? Who made the decision to merge this. I use this and am having issues upgrading to 11. 1 database out of them all deprecating a feature doesn't mean it should be removed. Mariadb still has it and mysql hasn't got rid of it yet. What a stupid decision to allow the removal of the unsigned attribute on decimal()

@hafezdivandari
Copy link
Contributor Author

@bluedreamer just use decimal()->unsigned() as explained on the upgrade guide.

@bluedreamer
Copy link

But why was a breaking public API change allowed for what amounts to a cosmetic reason? Why wasn't the param deprecated in documentation only? Wasted my time.

@devajmeireles
Copy link
Contributor

Regardless of anything, the PRs that @hafezdivandari delivers are always of a high standard of description, incredible!

@Vimiso
Copy link

Vimiso commented Oct 6, 2024

Laravel: ^11.9
MySQL: 8.4.2

Heads up, your documentation specifies before your PR $table->float('foo') will create a double, and after a float.

This is not the case.

$table->float('foo'), with no precision argument, will continue to create a double.

I have found having to set the precision explicitly to null, eg. $table->float('foo', precision: null) then indeed sets the data type to float.

@hafezdivandari
Copy link
Contributor Author

hafezdivandari commented Oct 6, 2024

@Vimiso I've explained that the default precisions is 53:

$table->float('foo') => FLOAT(53) => double-precision on all DBs
$table->float('foo', 24) => FLOAT(24) => single-precision on all DBs except SQLite
$table->float('foo', null) => FLOAT => double-precision on all DBs except MySQL and MariaDB

@Vimiso
Copy link

Vimiso commented Oct 13, 2024

Ah got you, my bad!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants