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 |
|