-
Notifications
You must be signed in to change notification settings - Fork 277
Description
Things to check first
I have searched the existing issues and didn't find my bug already reported there
I have checked that my bug is still present in the latest release
To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Sqlacodegen version
2.3.0.post1
SQLAlchemy version
2.0.15
RDBMS vendor
MySQL (or compatible)
What happened?
Summary
I encountered an issue when using the sqlacodegen
library to generate SQLAlchemy models from my database schema. The generated code for a CHAR
column contains an error that causes a TypeError
during application execution.
Steps to Reproduce
- Use the
sqlacodegen
library to generate SQLAlchemy models from a database schema that includes aCHAR
column with specific collation settings. - Attempt to use the generated SQLAlchemy model in an application.
- Observe the
TypeError
with the following traceback:
[ERROR] TypeError: init() takes from 1 to 2 positional arguments but 3 were given
Traceback (most recent call last):
...
...
File "models.py", line XX, in <module>
result_code = Column(CHAR(1, "utf8mb3_bin"), nullable=False)
Expected Behavior
I expected the generated SQLAlchemy model to correctly represent the CHAR
column with the specified collation settings and not produce a TypeError
during application execution.
Actual Behavior
The generated code for the CHAR
column includes incorrect syntax, resulting in a TypeError
when the model is used in the application.
How I solved
Instead of entering the arguments in order in the CHAR method, the collation
keyword was defined and used.
result_code = Column(CHAR(1, collation="utf8mb3_bin"), nullable=False)
Environment
- SQLAlchemy Version: 2.0.15
- sqlacodegen Version: 2.3.0.post1
Additional Information
I believe the issue may be related to how sqlacodegen
handles collation settings for CHAR
columns. The generated code attempts to pass three arguments to the Column
constructor instead of the expected two, which leads to the TypeError
. Manually adjusting the generated code to specify the collation settings separately resolves the issue.
Database schema for reproducing the bug
Environment
- MySQL Version: 8.0.34
- Infra: AWS RDS
CREATE TABLE `TableNameForIssue` (
`id` int NOT NULL AUTO_INCREMENT,
`plate_number` varchar(100) COLLATE utf8mb3_bin NOT NULL,
`result_code` char(1) COLLATE utf8mb3_bin NOT NULL,
`result_msg` varchar(100) COLLATE utf8mb3_bin DEFAULT NULL,
`createdAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `TableNameForIssue_createdAt_IDX` (`createdAt`) USING BTREE,
KEY `TableNameForIssue_plate_number_IDX` (`plate_number`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='comment'
Activity
agronholm commentedon Sep 21, 2023
Have you tried with the latest 3.0 release candidate?
hyoj0942 commentedon Sep 21, 2023
Yes, I just tested it with version 3.0.0rc3 and it's the same
Note: Part of SQLAlchemy
Fixes agronholm#285 drop duplicate imports
sheinbergon commentedon Feb 22, 2025
@agronholm this looks important. Maybe not for this milestone, but for the one after that
agronholm commentedon Feb 22, 2025
I can take a stab at this tomorrow if I have time.
sheinbergon commentedon Feb 22, 2025
I can also have a look. Just wanted to make sure it doesn't get overlooked. I marked it for milestone 3.1.
sheinbergon commentedon Feb 28, 2025
@agronholm looking at this issue I can report that:
The only remaining issue here, is the
collation
andcharset
data, while being reflected. is not part of the rendered column types. This is due to the fact themysql.CHAR
passescollation
usingkwargs
, so signature identification taking place here cannot pickup on them. It does detect the kw args, but that's about all.If I were to add some parent type signature parsing, it'd probably solve this issue .wdyt?
agronholm commentedon Feb 28, 2025
Finding the correct kwargs to add is a massive PITA. Does one of its parent classes take such keyword arguments?
agronholm commentedon Mar 1, 2025
To answer my own question, yes. But it takes plenty of other keyword arguments too. It seems just like I feared - that there is no way to automatically determine the correct keyword arguments to render.
sheinbergon commentedon Mar 2, 2025
In this case - the type hierarchy is
mysql.CHAR
->sqltypes.CHAR
->sqltypes.String
I think it should not be too hard to implement a logic that, when encountering
kwargs
parse named arguments all the way to the simplest base type and then try and extract them from the passedkwargs
dict. Do you see a scenario where this approach might break?agronholm commentedon Mar 3, 2025
What passed kwargs dict? Where do we get that?
sheinbergon commentedon Mar 3, 2025
The signature and parameters extracted from a
mysql.CHAR
instance. Not sure I understand your questionagronholm commentedon Mar 3, 2025
I'm asking how we can reliably get the values passed to the class initializer. The classes are in no way obligated to retain attributes with the same names.
sheinbergon commentedon Mar 3, 2025
We're already getting the reflected type properly from SQLAlchemy. Meaning, the values it holds already rely on how implementation classes declare constructor field names and pass them up the type hierarchy. All we need to do is follow hierarchy constructor field trail in a best effort manner in order to make sure types are being rendered following the same principal. I believe it should work
agronholm commentedon Mar 3, 2025
OK so in other words, follow the MRO, get initializer signatures, extract argument names and their default values, check for attributes with matching names whose values differ from the defaults?
sheinbergon commentedon Mar 3, 2025
Pretty much so