Search code examples
sqlitejdbckey

Strange org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch -


I'm running into org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch - ... with a statement, that proceeds without any complaints using the normal SQLite-frontend. This creates the crucial part of my database:

CREATE TABLE IF NOT EXISTS artists (
    aid INTEGER PRIMARY KEY AUTOINCREMENT,
    aname VARCHAR(200) NOT NULL,
    CONSTRAINT one UNIQUE(aname)
);

CREATE TABLE IF NOT EXISTS discs (
    did INTEGER PRIMARY KEY AUTOINCREMENT,
    testAddCD1 BIGINT(10) NOT NULL,
    dtitle VARCHAR(125) NOT NULL,
    dreleaseyear YEAR(4) DEFAULT NULL,
    dlang VARCHAR(3) DEFAULT NULL
);

CREATE TABLE IF NOT EXISTS tracks (
    discs_did INTEGER NOT NULL,
    tnumber INT(4) NOT NULL,
    ttitle VARCHAR(125) NOT NULL,
    tseconds INT(4) NOT NULL,
    CONSTRAINT pk PRIMARY KEY(discs_did, tnumber),
    CONSTRAINT fk FOREIGN KEY(discs_did) REFERENCES discs(did) ON DELETE RESTRICT ON UPDATE RESTRICT,
    CONSTRAINT val CHECK(tseconds> 0)
);

CREATE TABLE IF NOT EXISTS track_by_artist (
    discs_did INTEGER NOT NULL,
    tracks_tnumber INT(4) NOT NULL,
    artists_aid INTEGER NOT NULL,
    CONSTRAINT pk PRIMARY KEY(discs_did, tracks_tnumber, artists_aid),
    CONSTRAINT fk1 FOREIGN KEY(discs_did) REFERENCES discs(did) ON DELETE RESTRICT ON UPDATE RESTRICT,
    CONSTRAINT fk2 FOREIGN KEY(tracks_tnumber) REFERENCES tracks(tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT,
    CONSTRAINT fk3 FOREIGN KEY(artists_aid) REFERENCES artists(aid) ON DELETE RESTRICT ON UPDATE RESTRICT

The database gets created and the JDBC-driver inserts an artist, a disc and the disc's tracks - all good. The final insert should assign an artist to a track and looks like

INSERT INTO track_by_artist (discs_did, tracks_tnumber, artists_aid) VALUES (1, 1, 1);

Using the JDBC this yields

SQLite-Error #1
org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch - "track_by_artist" referencing "tracks")
    at org.sqlite.core.DB.newSQLException(DB.java:1012)
    at org.sqlite.core.DB.newSQLException(DB.java:1024)
    at org.sqlite.core.DB.throwex(DB.java:989)
    at org.sqlite.core.NativeDB.prepare_utf8(Native Method)
    at org.sqlite.core.NativeDB.prepare(NativeDB.java:134)
    at org.sqlite.core.DB.prepare(DB.java:257)
    at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:45)
    at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
    at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:25)
    at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:35)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:241)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:205)

Issuing the same SQL-Insert with SQLite's text-frontend works like cream.

I'm a little lost and don't know what to do about my Java-code.

Some advise, pls?

Chris


Solution

  • The problem is that in track_by_artist you defined this foreign key constraint:

    CONSTRAINT fk2 FOREIGN KEY(tracks_tnumber) REFERENCES tracks(tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT
    

    although tnumber in tracks is not UNIQUE (and it shouldn't be).
    A foreign key's parent must be defined as UNIQUE.

    In tracks the PRIMARY KEY is defined as the combination of discs_did and tnumber, which makes sense, so the combination of these 2 columns is unique.

    What you can do is define in track_by_artist a composite foreign key for the columns discs_did and tracks_tnumber that reference discs_did and tnumber of tracks:

    CREATE TABLE IF NOT EXISTS track_by_artist (
        discs_did INTEGER NOT NULL,
        tracks_tnumber INT(4) NOT NULL,
        artists_aid INTEGER NOT NULL,
        CONSTRAINT pk PRIMARY KEY(discs_did, tracks_tnumber, artists_aid),
        CONSTRAINT fk1 FOREIGN KEY(discs_did, tracks_tnumber) REFERENCES tracks(discs_did, tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT,
        CONSTRAINT fk2 FOREIGN KEY(artists_aid) REFERENCES artists(aid) ON DELETE RESTRICT ON UPDATE RESTRICT
    );
    

    This way you don't need a separate foreign key definition for discs_did.