Skip to content

HHH-14711 unsigned types causing out of range on MySQL/MariaDB #10303

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

peter1123581321
Copy link
Contributor

@peter1123581321 peter1123581321 commented Jun 10, 2025

MySQL fails for tinyint, smallint, integer and bigint, but works correctly for mediumint.
MariaDB fails for bigint, but works correctly for all others.
The MariaDB implementation of ResultSetMetaData#getColumnType() differs from the MySQL implementation.
MariaDB has a more accurate implementation concerning unsigned data types.

As MariaDbDialect extends MySQLDialect, the bugfix was implemented in MySQLDialect#resolveSqlTypeDescriptor().


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.


https://hibernate.atlassian.net/browse/HHH-14711

Copy link
Member

@gavinking gavinking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that this issue is only worth fixing if we can fix it in a clean way.

Please see my comments.

Comment on lines 451 to 453
protected boolean isUnsigned(String columnTypeName) {
return columnTypeName.contains( "UNSIGNED" );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsigned types are not, AFAIK, a standard or common feature of SQL databases, so this implementation doesn't belong as the default implementation on Dialect.

Comment on lines 537 to 563
case Types.TINYINT:
if ( isUnsigned( columnTypeName ) ) {
return jdbcTypeRegistry.getDescriptor( Types.SMALLINT );
}
break;
case Types.SMALLINT:
if ( isUnsigned( columnTypeName ) ) {
return columnTypeName.contains( "TINYINT" ) ? jdbcTypeRegistry.getDescriptor( Types.SMALLINT )
: jdbcTypeRegistry.getDescriptor( Types.INTEGER );
}
break;
case Types.INTEGER:
if ( isUnsigned( columnTypeName ) ) {
if ( columnTypeName.contains( "SMALLINT" ) ) {
return jdbcTypeRegistry.getDescriptor( Types.INTEGER );
}
else if ( !columnTypeName.contains( "MEDIUMINT" ) ) {
return jdbcTypeRegistry.getDescriptor( Types.BIGINT );
}
}
break;
case Types.BIGINT:
if ( isUnsigned( columnTypeName ) ) {
return columnTypeName.contains( "INTEGER" ) ? jdbcTypeRegistry.getDescriptor( Types.BIGINT )
: jdbcTypeRegistry.getDescriptor( Types.NUMERIC );
}
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah. This is ... a bit ugly. Surely there must be some more elegant way to do this?

Why not just directly switch on the columnTypeName, since you're depending on it anyway?

@gavinking
Copy link
Member

Actually, @peter1123581321, is this a breaking change to existing native SQL queries which are expecting the narrower types?

@gavinking
Copy link
Member

@peter1123581321
Copy link
Contributor Author

Many thanks Gavin for your comments on this PR.

  1. I agree, the switch statement is ... a bit ugly. :D
    Unfortunately, as mentioned above, MariaDB handles unsigned data types differently than MySQL,
    that's the main reason why the fix is not very straight forward.
    I also thought about splitting the code up to both dialects, but that would propably lead to a bug some day when the Mysql implementation of getColumnType matches the more accurate one of MariaDB.

  2. The more I think about it, the more I think that this should be fixed in the connectors. And as you said, ANSI SQL has no unsigned numeric types. So propably there is no real need to implement this workaround here.

  3. I think I did not get your point of 2) in your comment on Jira. IMO it makes totally sense to map a unsigned datatype to the next larger one, e.g. tinyint to a smallint. Why should we use an even greater one?

  4. Not sure if it can be considered as really breaking. If you define the column e.g. as usigned tinyint (0-255) with java type byte, but always only use the half range from (0-127), yes, in such cases it is breaking. Otherwise I think you would have got the out of range exception and changed your design.

Summarized, I also would suggest to close this bug.
If you agree, I would adopt the PR to only submit the tests for the cases for which the unsigned data types and the native queries work as expected (I did not have found any other tests that cover this situation).

@gavinking
Copy link
Member

I think I did not get your point of 2) in your comment on Jira. IMO it makes totally sense to map an unsigned datatype to the next larger one, e.g. tinyint to a smallint. Why should we use an even greater one?

I'm not referring to what you're doing here, which is, well, OK.

I was referring to creating a database schema where you have just one bit of headroom for your numeric types. That's a bad idea, since it's incredibly rare for someone to have such a good estimate of the largest possible value for a column.

Not sure if it can be considered as really breaking. If you define the column e.g. as usigned tinyint (0-255) with java type byte, but always only use the half range from (0-127), yes, in such cases it is breaking.

And that's probably a pretty common scenario, since numeric columns are typically specified with a lot of headroom (i.e. more than one bit).

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.

2 participants