Opened 3 years ago

Closed 2 years ago

#32672 closed Cleanup/optimization (fixed)

Primary key constraints aren't detected on SQLite.

Reported by: jgr88 Owned by: Anvesh Mishra
Component: Database layer (models, ORM) Version: 3.2
Severity: Normal Keywords: SQLite3 PrimaryKey Datatypes
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description (last modified by jgr88)

While creating models with "inspectdb" i discovered, that there are currently some issues with SQLite3 Databases.

  • PrimaryKeys ain't detected properly
  • Datatype double ain't detected properly
  • Datatype unsigned int ain't detected properly

Reproduce these issues by creating a SQLite3 Database:
CREATE TABLE "test" ( pId INTEGER NOT NULL, doubleField DOUBLE NOT NULL, uInt UNSIGNED INTEGER NOT NULL, PRIMARY KEY(pId) )

I added a pullrequest:
https://github.com/django/django/pull/14293

Change History (13)

comment:1 by jgr88, 3 years ago

Description: modified (diff)
Easy pickings: set

comment:2 by Girish Sontakke, 3 years ago

Owner: changed from nobody to jgr88
Status: newassigned

in reply to:  description comment:3 by jgr88, 3 years ago

Replying to jgr88:

While creating models with "inspectdb" i discovered, that there are currently some issues with SQLite3 Databases.

  • PrimaryKeys ain't detected properly
  • Datatype double ain't detected properly
  • Datatype unsigned int ain't detected properly

Reproduce these issues by creating a SQLite3 Database:
CREATE TABLE "test" ( pId INTEGER NOT NULL, doubleField DOUBLE NOT NULL, uInt UNSIGNED INTEGER NOT NULL, PRIMARY KEY(pId) )

I added a pullrequest:
https://github.com/django/django/pull/14293

Update:
I added https://github.com/django/django/pull/14297 since i messed up the first one

comment:4 by Carlton Gibson, 3 years ago

Triage Stage: UnreviewedAccepted

OK, this seems a reasonable proposal to me.

Generated model:

class Test(models.Model):
    pid = models.AutoField(db_column='pId')  # Field name made lowercase.
    doublefield = models.TextField(db_column='doubleField')  # Field name made lowercase. This field type is a guess.
    uint = models.TextField(db_column='uInt')  # Field name made lowercase. This field type is a guess.

    class Meta:
        managed = False
        db_table = 'test'

comment:5 by Mariusz Felisiak, 3 years ago

I'm fine with fixing detection of primary key, but I think we should not add datatypes that are not used by Django itself. I also don't see a reason to add only DOUBLE.

SQLite uses complicated rules to determine a datatype affinity based on its name (see Determination Of Column Affinity which have a lot of caveats, e.g.

  • CHARINT is mapped to INT,
  • POINT is mapped to INT,
  • STRING is mapped to NUMERIC.


I would prefer to warn users about datatypes that are not used by Django instead of mapping some/all of them.

comment:6 by Mariusz Felisiak, 3 years ago

Patch needs improvement: set

comment:7 by Mariusz Felisiak, 3 years ago

Type: BugCleanup/optimization

comment:8 by Anvesh Mishra, 3 years ago

Owner: changed from jgr88 to Anvesh Mishra
Last edited 3 years ago by Mariusz Felisiak (previous) (diff)

comment:9 by Anvesh Mishra, 3 years ago

So as per Charettes's comment on my PR https://github.com/django/django/pull/14878#discussion_r713268066, I implemented this issue with sqlparse but failed testcases one of them was inspectdb.tests.InspectDBTestCase.test_attribute_name_not_python_keyword. The change which I did was

method_match = sqlparse.parse(field_desc)[0]
if method_match:
    if len(method_match.tokens) == 3 and method_match.tokens[0].value == "PRIMARY":
        column_name_token = str(method_match.tokens[2])
        column_name = column_name_token[4:len(column_name_token) - 1]
        return column_name
    elif len(method_match.tokens) > 6 and method_match.tokens[6].value == "PRIMARY":
        return str(method_match.tokens[0])
Last edited 3 years ago by Anvesh Mishra (previous) (diff)

comment:10 by Simon Charette, 3 years ago

Looks like you forgot to strip leading and trailing quotes, Identifier.get_real_name() does that

  • django/db/backends/sqlite3/introspection.py

    diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py
    index f109b5d5ee..983c000e8b 100644
    a b def get_primary_key_column(self, cursor, table_name):  
    214214        if table_type == 'view':
    215215            # Views don't have a primary key.
    216216            return None
    217         fields_sql = create_sql[create_sql.index('(') + 1:create_sql.rindex(')')]
    218         for field_desc in fields_sql.split(','):
    219             field_desc = field_desc.strip()
    220             m = re.match(r'(?:(?:["`\[])(.*)(?:["`\]])|(\w+)).*PRIMARY KEY.*', field_desc)
    221             if m:
    222                 return m[1] if m[1] else m[2]
     217        latest_identifier = None
     218        fields = sqlparse.parse(create_sql)[0].tokens[-1]
     219        for index, token in enumerate(fields):
     220            if isinstance(token, sqlparse.sql.Identifier):
     221                latest_identifier = token
     222                continue
     223            elif (token.match(sqlparse.tokens.Keyword, 'PRIMARY') and
     224                    fields.token_next(index)[1].match(sqlparse.tokens.Keyword, 'KEY')):
     225                return latest_identifier.get_real_name()
    223226        return None
    224227
    225228    def _get_foreign_key_constraints(self, cursor, table_name):

comment:11 by Simon Charette, 2 years ago

Patch needs improvement: unset

comment:12 by Mariusz Felisiak, 2 years ago

Summary: Primary Key, type double and type unsigned integer ain't detected correctly for Sqlite3 DatabasePrimary key constraints aren't detected on SQLite.
Triage Stage: AcceptedReady for checkin

comment:13 by Mariusz Felisiak <felisiak.mariusz@…>, 2 years ago

Resolution: fixed
Status: assignedclosed

In 69af4d09:

Fixed #32672 -- Fixed introspection of primary key constraints on SQLite.

Thanks Simon Charette for the implementation idea.

Note: See TracTickets for help on using tickets.
Back to Top