Skip to content

Commit e2243c4

Browse files
authored
Better demo database schema for testing (#196)
* better schema * typo * Refactor readme * update * fix mysql migration * mysql 5.6 * fix tests * fix * wip: figuring out why JSONB return * fix tests * fmt * fixed all tests * fixing clippy * fix * fix * revert version * fix * fix demo
1 parent 55fa9dc commit e2243c4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+919
-563
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright <2024> <Jason Shin>
1+
Copyright <2025> <Jason Shin>
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44

playpen/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# World of Warcraft Inspired SQL Schema
2+
3+
This repository contains a comprehensive World of Warcraft-themed SQL schema designed to demonstrate the capabilities of [sqlx-ts](https://github.com/JasonShin/sqlx-ts). The schema models core game mechanics such as characters, races, classes, guilds, items, and quests, while incorporating MySQL and PostgreSQL's advanced features like JSONB, ENUM types, and relationships.
4+
5+
## Overview
6+
The schema covers:
7+
- **Factions**: Represents the two primary factions - Alliance and Horde.
8+
- **Races**: Lists all playable races, associated with their faction.
9+
- **Classes**: Defines class specializations, including roles, weapons, and abilities stored as JSONB.
10+
- **Characters**: Central entity representing player characters, linked to races and classes.
11+
- **Guilds**: Guilds that characters can join, supporting a many-to-many relationship.
12+
- **Items**: Collectible items with dynamic stats stored as JSONB.
13+
- **Inventory**: Tracks character inventory, supporting multiple item quantities.
14+
- **Quests**: Represents character progression, including quest rewards and status.
15+
- **Random Data**: Demonstrates MySQL/PostgreSQL's wide range of data types for testing and exploration.
16+
17+
## ERD Diagram
18+
19+
<img src="./assets/schema.png" width="65%">
20+
21+
## Installation and Setup
22+
23+
1. Clone this repository:
24+
```bash
25+
git clone https://github.com/jasonshin/sqlx-ts
26+
```
27+
28+
2. Ensure that you have [docker](https://www.docker.com/) and [docker-compose](https://docs.docker.com/compose/) installed
29+
30+
3. Navigate to root directory of the project:
31+
```bash
32+
docker compose up -d
33+
```
34+
35+
4. Run the SQL schema on your MySQL/PostgreSQL instance:
36+
```bash
37+
mysql -h 127.0.0.1 -P 33306 -u root sqlx-ts
38+
psql -h 127.0.0.1 -p 54321 -U postgres # and then enter `postgres` as password
39+
```
40+
41+
## Key Features
42+
- **Enum Types**: Faction, Race, and Class columns leverage MySQL/PostgreSQL ENUMs for better data consistency.
43+
- **JSONB Fields**: Flexible and scalable fields for class specializations, item stats, and quest rewards.
44+
- **Referential Integrity**: Foreign key relationships ensure data consistency across races, classes, and factions.
45+
- **Various types**: The schema utilises a wide range of string/numeric/date types to ensure every types are supported by sqlx-ts
46+
- **Complex Constraints**: Advanced CHECK constraints validate JSONB fields to ensure proper specialization structures.
47+
48+
## Example Queries
49+
Fetch all characters with their race and class:
50+
```sql
51+
SELECT c.name AS character_name, r.name AS race, cl.name AS class
52+
FROM characters c
53+
JOIN races r ON c.race_id = r.id
54+
JOIN classes cl ON c.class_id = cl.id;
55+
```
56+
57+
Retrieve all items in a character's inventory:
58+
```sql
59+
SELECT ch.name AS character_name, i.name AS item, inv.quantity
60+
FROM inventory inv
61+
JOIN characters ch ON inv.character_id = ch.id
62+
JOIN items i ON inv.item_id = i.id;
63+
```
64+
65+
List all active quests for a character:
66+
```sql
67+
SELECT q.name AS quest_name, cq.status
68+
FROM character_quests cq
69+
JOIN quests q ON cq.quest_id = q.id
70+
WHERE cq.character_id = 1;
71+
```
72+
73+
## License
74+
Please refer to [LICENSE](../LICENSE)
75+
76+

playpen/db/mysql_migration.sql

Lines changed: 130 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,141 @@
1-
CREATE TABLE tables (
2-
id INTEGER NOT NULL AUTO_INCREMENT,
3-
number INTEGER NOT NULL,
4-
occupied BOOL NOT NULL DEFAULT FALSE,
5-
PRIMARY KEY (id)
1+
CREATE TABLE faction_enum (
2+
name ENUM('alliance', 'horde') NOT NULL PRIMARY KEY
63
);
74

5+
CREATE TABLE race_enum (
6+
name ENUM('human', 'night elf', 'dwarf', 'gnome', 'orc', 'troll', 'tauren', 'undead') NOT NULL PRIMARY KEY
7+
);
8+
9+
CREATE TABLE class_enum (
10+
name ENUM('warrior', 'hunter', 'priest', 'paladin', 'druid', 'mage', 'warlock') NOT NULL PRIMARY KEY
11+
);
12+
13+
-- Factions Table
14+
CREATE TABLE factions (
15+
id INT AUTO_INCREMENT PRIMARY KEY,
16+
name ENUM('alliance', 'horde') UNIQUE NOT NULL,
17+
description TEXT
18+
);
19+
20+
-- Races Table
21+
CREATE TABLE races (
22+
id INT AUTO_INCREMENT PRIMARY KEY,
23+
name ENUM('human', 'night elf', 'dwarf', 'gnome', 'orc', 'troll', 'tauren', 'undead') UNIQUE NOT NULL,
24+
faction_id INT,
25+
FOREIGN KEY (faction_id) REFERENCES factions(id) ON DELETE CASCADE
26+
);
27+
28+
-- Classes Table
29+
CREATE TABLE classes (
30+
id INT AUTO_INCREMENT PRIMARY KEY,
31+
name ENUM('warrior', 'hunter', 'priest', 'paladin', 'druid', 'mage', 'warlock') UNIQUE NOT NULL,
32+
specialization JSON,
33+
CHECK (
34+
JSON_VALID(specialization) AND
35+
JSON_UNQUOTE(JSON_EXTRACT(specialization, '$.role')) IN ('tank', 'healer', 'ranged', 'melee', 'hybrid') AND
36+
JSON_LENGTH(JSON_EXTRACT(specialization, '$.abilities')) > 0 AND
37+
(
38+
JSON_EXTRACT(specialization, '$.tier') IS NULL OR
39+
CAST(JSON_UNQUOTE(JSON_EXTRACT(specialization, '$.tier')) AS UNSIGNED) BETWEEN 1 AND 5
40+
)
41+
)
42+
);
43+
44+
-- Characters Table
45+
CREATE TABLE characters (
46+
id INT AUTO_INCREMENT PRIMARY KEY,
47+
name VARCHAR(100) NOT NULL,
48+
race_id INT,
49+
class_id INT,
50+
level SMALLINT DEFAULT 1,
51+
experience BIGINT DEFAULT 0,
52+
gold DOUBLE DEFAULT 0,
53+
last_chat_time TIME,
54+
login_time DATETIME,
55+
logout_time DATETIME,
56+
last_trade_time TIME,
57+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
58+
FOREIGN KEY (race_id) REFERENCES races(id),
59+
FOREIGN KEY (class_id) REFERENCES classes(id)
60+
);
61+
62+
-- Guilds Table
63+
CREATE TABLE guilds (
64+
id INT AUTO_INCREMENT PRIMARY KEY,
65+
name VARCHAR(100) UNIQUE NOT NULL,
66+
description TEXT,
67+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
68+
);
69+
70+
-- Guild Members Table
71+
CREATE TABLE guild_members (
72+
guild_id INT,
73+
character_id INT,
74+
guild_rank VARCHAR(50),
75+
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
76+
PRIMARY KEY (guild_id, character_id),
77+
FOREIGN KEY (guild_id) REFERENCES guilds(id) ON DELETE CASCADE,
78+
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE
79+
);
80+
81+
-- Inventory Table
82+
CREATE TABLE inventory (
83+
id INT AUTO_INCREMENT PRIMARY KEY,
84+
quantity INT DEFAULT 1,
85+
character_id INT,
86+
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE
87+
);
88+
89+
-- Items Table
890
CREATE TABLE items (
9-
id INTEGER NOT NULL,
10-
food_type VARCHAR(30) NOT NULL,
11-
time_takes_to_cook INTEGER NOT NULL,
12-
table_id INTEGER NOT NULL,
13-
points SMALLINT NOT NULL,
14-
description VARCHAR(255) NULL,
15-
FOREIGN KEY (table_id) REFERENCES tables (id),
16-
PRIMARY KEY (id)
91+
id INT AUTO_INCREMENT PRIMARY KEY,
92+
name VARCHAR(100) NOT NULL,
93+
rarity VARCHAR(50),
94+
inventory_id INT,
95+
flavor_text TEXT,
96+
FOREIGN KEY (inventory_id) REFERENCES inventory(id) ON DELETE CASCADE
97+
);
98+
99+
-- Quests Table
100+
CREATE TABLE quests (
101+
id INT AUTO_INCREMENT PRIMARY KEY,
102+
name VARCHAR(100) NOT NULL,
103+
description TEXT,
104+
rewards JSON,
105+
completed BOOL DEFAULT false,
106+
required_level INT DEFAULT 1
17107
);
18108

19-
INSERT INTO tables (number)
20-
VALUES
21-
(1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
109+
-- Character Quests Table
110+
CREATE TABLE character_quests (
111+
character_id INT,
112+
quest_id INT,
113+
status VARCHAR(50) DEFAULT 'In Progress',
114+
PRIMARY KEY (character_id, quest_id),
115+
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE,
116+
FOREIGN KEY (quest_id) REFERENCES quests(id) ON DELETE CASCADE
117+
);
22118

23-
INSERT INTO items (id, food_type, time_takes_to_cook, table_id, points)
24-
VALUES
25-
(1, 'korean', 10, 1, 2),
26-
(2, 'chinese', 10, 1, 2),
27-
(3, 'japanese', 10, 1, 2),
28-
(4, 'italian', 10, 1, 2),
29-
(5, 'french', 10, 1, 2);
119+
-- Seed Data
120+
INSERT INTO factions (name, description) VALUES
121+
('alliance', 'The noble and righteous faction'),
122+
('horde', 'The fierce and battle-hardened faction');
30123

124+
INSERT INTO races (name, faction_id) VALUES
125+
('human', 1),
126+
('night elf', 1),
127+
('dwarf', 1),
128+
('gnome', 1),
129+
('orc', 2),
130+
('troll', 2),
131+
('tauren', 2),
132+
('undead', 2);
31133

32-
-- We can primarily use this table to check how a column in MySQL can be converted to a TsFieldType
134+
INSERT INTO classes (name, specialization) VALUES
135+
('warrior', '{"role": "tank", "weapon": "sword", "abilities": ["charge", "slam", "shield block"]}'),
136+
('hunter', '{"role": "ranged", "weapon": "bow", "abilities": ["aimed shot", "multi-shot", "trap"]}');
33137

138+
-- We can primarily use this table to check how a column in MySQL can be converted to a TsFieldType
34139
CREATE TABLE random (
35140
-- numeric types
36141
intz INT,
@@ -61,11 +166,9 @@ CREATE TABLE random (
61166
blob1 BLOB,
62167
text1 TEXT,
63168
-- ideally this one should be generated as a legit enum type
64-
enum1 ENUM('x-small', 'small', 'medium', 'large', 'x-large'),
169+
enum1 ENUM('alliance', 'horde'),
65170
set1 SET('one', 'two'),
66171

67172
-- JSON types
68173
json1 JSON
69-
70174
);
71-

0 commit comments

Comments
 (0)