Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ General ➜ SQLite3 foreign key support now available in version 4.45 of MUSHclient

SQLite3 foreign key support now available in version 4.45 of MUSHclient

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Nick Gammon   Australia  (23,121 posts)  Bio   Forum Administrator
Date Sat 12 Dec 2009 03:18 AM (UTC)

Amended on Tue 03 Jun 2014 11:48 PM (UTC) by Nick Gammon

Message
Version 4.45 of MUSHclient upgrades the SQLite3 database version from 3.6.16 to 3.6.21.

Amongst other things, this adds direct support of foreign key constraints. The importance of these constraints is that they ensure that the database has integrity, that is, you don't have rows in one table which are supposed to refer to rows in another table, which don't exist.

For more details, see:

http://www.sqlite.org/foreignkeys.html

An example of using foreign keys appears below, adapted from some code in this thread:

http://www.gammon.com.au/forum/?id=9650

The changes to support foreign keys are in bold. Note the pragma directive to turn foreign key support on.




print "commencing"

databasename = GetInfo (67) .. GetPluginID () .. "_mud.db"
db = "db"

local MUSHclient_Database_Errors = {
  [-1] = "Database id not found",
  [-2] = "Database not open",
  [-3] = "Already have prepared statement",
  [-4] = "Do not have prepared statement",
  [-5] = "Do not have a valid row",
  [-6] = "Database already exists under a different disk name",
  [-7] = "Column number out of range",
  } -- end of MUSHclient_Database_Errors

-- check for errors on a DatabaseXXXXX call
function dbcheck (code)

 if code == sqlite3.OK or       -- no error
    code == sqlite3.ROW or      -- completed OK with another row of data
    code == sqlite3.DONE then   -- completed OK, no more rows
    return code
  end -- if code OK

  -- DatabaseError won't return the negative errors  
  local err = MUSHclient_Database_Errors [code] or DatabaseError(db)
  DatabaseExec (db, "ROLLBACK")  -- rollback any transaction to unlock the database
  error (err, 2)                 -- show error in caller's context

end -- dbcheck 

DatabaseOpen (db, databasename, 6)  -- open database for read/write (+ create if necessary)

dbcheck (DatabaseExec (db, [[

  -- no foreign key checks whilst deleting tables

  PRAGMA foreign_keys = OFF;

  DROP TABLE IF EXISTS classes;
  DROP TABLE IF EXISTS players;
  DROP TABLE IF EXISTS item_types;
  DROP TABLE IF EXISTS inventory;
  DROP TABLE IF EXISTS skills;
  DROP TABLE IF EXISTS player_skills;

  -- now check them
  PRAGMA foreign_keys = ON;

  BEGIN;
  
  -- player classes
  CREATE TABLE classes (
          class_id    INTEGER PRIMARY KEY AUTOINCREMENT,
          name        TEXT NOT NULL,
          initial_hp  INTEGER NOT NULL
        );


 -- players
  CREATE TABLE players (
          player_id INTEGER PRIMARY KEY AUTOINCREMENT,
          name      TEXT NOT NULL,
          level     INTEGER,
          class_id  INTEGER,  -- foreign key
          FOREIGN KEY(class_id) REFERENCES classes(class_id)

        );
    

  -- inventory item types (eg. weapon, bag)
  CREATE TABLE item_types (
          type_id     INTEGER PRIMARY KEY AUTOINCREMENT,
          name        TEXT NOT NULL
        );




  -- one entry for each inventory item per player
  CREATE TABLE inventory (
          inv_id      INTEGER PRIMARY KEY AUTOINCREMENT,
          player_id   INTEGER NOT NULL,   -- foreign key
          type_id     INTEGER NOT NULL,   -- foreign key
          name        TEXT NOT NULL,
          FOREIGN KEY(player_id) REFERENCES players(player_id),
          FOREIGN KEY(type_id) REFERENCES item_types(type_id)
        );


  -- improve efficiency of looking up inventory for particular players            
  CREATE INDEX IF NOT EXISTS player_id_index ON inventory (player_id);

  -- improve efficiency of looking up inventory items of a certain type
  CREATE INDEX IF NOT EXISTS type_id_index ON inventory(type_id);

  -- skill names
  CREATE TABLE skills (
          skill_id    INTEGER PRIMARY KEY AUTOINCREMENT,
          name        TEXT NOT NULL
        );
            
  -- this table has an entry for each player who has each skill
  CREATE TABLE player_skills (
          ps_id     INTEGER PRIMARY KEY AUTOINCREMENT,
          player_id INTEGER NOT NULL,   -- foreign key
          skill_id  INTEGER NOT NULL,   -- foreign key
          FOREIGN KEY(player_id) REFERENCES players (player_id),
          FOREIGN KEY(skill_id) REFERENCES skills (skill_id),
          
          UNIQUE (player_id, skill_id)  -- make sure we don't duplicate them
        );

  -- improve efficiency of looking up skills for particular players            
  CREATE INDEX IF NOT EXISTS player_id_index ON player_skills (player_id);

  -- improve efficiency of looking up who has a particular skill            
  CREATE INDEX IF NOT EXISTS skill_id_index ON player_skills (skill_id);
        
  -- some classes
  INSERT INTO classes VALUES (1, 'mage', 100);
  INSERT INTO classes VALUES (2, 'warrior', 200);
  INSERT INTO classes VALUES (3, 'thief', 150);
  
  -- some players
  INSERT INTO players (name, level, class_id) VALUES ('Nick', 1, 1);
  INSERT INTO players (name, level, class_id) VALUES ('David', 1, 2);
  INSERT INTO players (name, level, class_id) VALUES ('Twisol', 5, 3);
  INSERT INTO players (name, level, class_id) VALUES ('Zeno', 10, 1);

  -- types of inventory
  INSERT INTO item_types (type_id, name) VALUES (1, 'weapon');
  INSERT INTO item_types (type_id, name) VALUES (2, 'armour');
  INSERT INTO item_types (type_id, name) VALUES (3, 'bag');
  INSERT INTO item_types (type_id, name) VALUES (4, 'shield');
  
  -- inventory - Nick
  INSERT INTO inventory (player_id, type_id, name) VALUES (1, 1, 'sword of misgiving');
  INSERT INTO inventory (player_id, type_id, name) VALUES (1, 2, 'cloak of hiding');
  INSERT INTO inventory (player_id, type_id, name) VALUES (1, 3, 'leather bag');
  INSERT INTO inventory (player_id, type_id, name) VALUES (1, 4, 'wooden shield');
  -- David
  INSERT INTO inventory (player_id, type_id, name) VALUES (2, 1, 'sword of attacking');
  INSERT INTO inventory (player_id, type_id, name) VALUES (2, 3, 'leather bag');
  INSERT INTO inventory (player_id, type_id, name) VALUES (2, 4, 'iron shield');
    
  -- skills
  INSERT INTO skills (skill_id, name) VALUES (1, 'fireball');
  INSERT INTO skills (skill_id, name) VALUES (2, 'frostshock');
  INSERT INTO skills (skill_id, name) VALUES (3, 'freeze');
  INSERT INTO skills (skill_id, name) VALUES (4, 'cure');
  
  -- player/skill combinations
  
  -- Nick
  INSERT INTO player_skills (player_id, skill_id) VALUES (1, 1);  -- Nick / fireball
  INSERT INTO player_skills (player_id, skill_id) VALUES (1, 2);  -- Nick / frostshock
  
  -- David
  INSERT INTO player_skills (player_id, skill_id) VALUES (2, 3);  -- David / freeze
  INSERT INTO player_skills (player_id, skill_id) VALUES (2, 4);  -- David / cure
  INSERT INTO player_skills (player_id, skill_id) VALUES (2, 2);  -- David / frostshock
  

  COMMIT;
  
          ]]))
        
DatabaseClose (db)  -- close it        
print "done"


This code runs without errors (in the Immediate window with Lua as the scripting language). However if you force a foreign key violation, eg, changing one of lines near the end from:


INSERT INTO player_skills (player_id, skill_id) VALUES (2, 3);  -- David / freeze


to:


INSERT INTO player_skills (player_id, skill_id) VALUES (999, 3);  -- David / freeze


... then you get an error message: "foreign key constraint failed".

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by WillFa   USA  (525 posts)  Bio
Date Reply #1 on Sat 12 Dec 2009 05:12 AM (UTC)
Message
Awesome! :) Thanks for all the work, Nick!
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


14,512 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.