|
| 1 | +# MiniDB |
| 2 | + |
| 3 | +A miniature in-memory database with SQL-like query support, built from scratch using only Python 3.11+ standard library. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Typed Columns**: INTEGER, STRING, FLOAT, BOOLEAN |
| 8 | +- **CRUD Operations**: INSERT, SELECT, UPDATE, DELETE |
| 9 | +- **SQL-like Query Language**: |
| 10 | + - WHERE with AND/OR comparisons (=, >, <, >=, <=, !=, LIKE, IN) |
| 11 | + - ORDER BY (ASC/DESC) |
| 12 | + - GROUP BY with aggregations |
| 13 | + - LIMIT clause |
| 14 | +- **Aggregations**: COUNT, SUM, AVG, MIN, MAX |
| 15 | +- **JOINs**: INNER JOIN between tables |
| 16 | +- **Indexing**: Automatic hash-based indexing on primary keys |
| 17 | +- **Query Planner**: Chooses between index scans and table scans |
| 18 | +- **Persistence**: Save/load database to JSON files with versioning |
| 19 | + |
| 20 | +## Installation |
| 21 | + |
| 22 | +No installation required! MiniDB uses only Python standard library. |
| 23 | + |
| 24 | +```bash |
| 25 | +# Just clone and use |
| 26 | +git clone <repo-url> |
| 27 | +cd MiniDB |
| 28 | +python -m pytest tests/ -v # Run tests |
| 29 | +python main.py # Run demo |
| 30 | +``` |
| 31 | + |
| 32 | +## Quick Start |
| 33 | + |
| 34 | +```python |
| 35 | +from minidb import MiniDB, Column, ColumnType |
| 36 | + |
| 37 | +# Create database |
| 38 | +db = MiniDB() |
| 39 | + |
| 40 | +# Create table using SQL |
| 41 | +db.execute(''' |
| 42 | + CREATE TABLE users ( |
| 43 | + id INTEGER PRIMARY KEY, |
| 44 | + name STRING, |
| 45 | + age INTEGER, |
| 46 | + salary FLOAT, |
| 47 | + active BOOLEAN |
| 48 | + ) |
| 49 | +''') |
| 50 | + |
| 51 | +# Or create table programmatically |
| 52 | +db.create_table('orders', [ |
| 53 | + Column('id', ColumnType.INTEGER, primary_key=True), |
| 54 | + Column('user_id', ColumnType.INTEGER), |
| 55 | + Column('product', ColumnType.STRING), |
| 56 | + Column('total', ColumnType.FLOAT), |
| 57 | +]) |
| 58 | + |
| 59 | +# Insert data |
| 60 | +db.execute("INSERT INTO users (id, name, age, salary, active) VALUES (1, 'Alice', 30, 75000.0, true)") |
| 61 | +db.execute("INSERT INTO users (id, name, age, salary, active) VALUES (2, 'Bob', 25, 55000.0, false)") |
| 62 | + |
| 63 | +# Query data |
| 64 | +results = db.query("SELECT * FROM users WHERE age > 28") |
| 65 | + |
| 66 | +# Complex queries |
| 67 | +results = db.query(''' |
| 68 | + SELECT name, salary FROM users |
| 69 | + WHERE active = true AND salary > 60000 |
| 70 | + ORDER BY salary DESC |
| 71 | +''') |
| 72 | + |
| 73 | +# Aggregations |
| 74 | +results = db.query("SELECT COUNT(*), AVG(salary) FROM users") |
| 75 | + |
| 76 | +# GROUP BY |
| 77 | +results = db.query(''' |
| 78 | + SELECT active, COUNT(*), AVG(salary) |
| 79 | + FROM users |
| 80 | + GROUP BY active |
| 81 | +''') |
| 82 | + |
| 83 | +# JOINs |
| 84 | +results = db.query(''' |
| 85 | + SELECT users.name, orders.product, orders.total |
| 86 | + FROM users |
| 87 | + JOIN orders ON users.id = orders.user_id |
| 88 | + WHERE orders.total > 50 |
| 89 | +''') |
| 90 | + |
| 91 | +# UPDATE |
| 92 | +affected = db.execute("UPDATE users SET salary = 80000.0 WHERE id = 1") |
| 93 | + |
| 94 | +# DELETE |
| 95 | +affected = db.execute("DELETE FROM users WHERE active = false") |
| 96 | + |
| 97 | +# Persistence |
| 98 | +db.save('my_database.json') |
| 99 | +db = MiniDB.load('my_database.json') |
| 100 | +``` |
| 101 | + |
| 102 | +## SQL Syntax Reference |
| 103 | + |
| 104 | +### CREATE TABLE |
| 105 | + |
| 106 | +```sql |
| 107 | +CREATE TABLE table_name ( |
| 108 | + column1 INTEGER PRIMARY KEY, |
| 109 | + column2 STRING, |
| 110 | + column3 FLOAT, |
| 111 | + column4 BOOLEAN |
| 112 | +) |
| 113 | +``` |
| 114 | + |
| 115 | +### INSERT |
| 116 | + |
| 117 | +```sql |
| 118 | +INSERT INTO table_name (col1, col2, col3) VALUES (1, 'value', 3.14) |
| 119 | +``` |
| 120 | + |
| 121 | +### SELECT |
| 122 | + |
| 123 | +```sql |
| 124 | +SELECT * FROM table_name |
| 125 | +SELECT col1, col2 FROM table_name |
| 126 | +SELECT col1, COUNT(*), AVG(col2) FROM table_name GROUP BY col1 |
| 127 | +``` |
| 128 | + |
| 129 | +### WHERE Clause |
| 130 | + |
| 131 | +```sql |
| 132 | +WHERE col = value |
| 133 | +WHERE col > value |
| 134 | +WHERE col >= value |
| 135 | +WHERE col < value |
| 136 | +WHERE col <= value |
| 137 | +WHERE col != value |
| 138 | +WHERE col LIKE 'pattern%' -- % matches any sequence |
| 139 | +WHERE col IN (1, 2, 3) |
| 140 | +WHERE cond1 AND cond2 |
| 141 | +WHERE cond1 OR cond2 |
| 142 | +``` |
| 143 | + |
| 144 | +### ORDER BY |
| 145 | + |
| 146 | +```sql |
| 147 | +ORDER BY col ASC |
| 148 | +ORDER BY col DESC |
| 149 | +ORDER BY col1 ASC, col2 DESC |
| 150 | +``` |
| 151 | + |
| 152 | +### LIMIT |
| 153 | + |
| 154 | +```sql |
| 155 | +LIMIT 10 |
| 156 | +``` |
| 157 | + |
| 158 | +### JOIN |
| 159 | + |
| 160 | +```sql |
| 161 | +SELECT t1.col, t2.col |
| 162 | +FROM t1 |
| 163 | +JOIN t2 ON t1.id = t2.t1_id |
| 164 | +``` |
| 165 | + |
| 166 | +### UPDATE |
| 167 | + |
| 168 | +```sql |
| 169 | +UPDATE table_name SET col1 = value1, col2 = value2 WHERE condition |
| 170 | +``` |
| 171 | + |
| 172 | +### DELETE |
| 173 | + |
| 174 | +```sql |
| 175 | +DELETE FROM table_name WHERE condition |
| 176 | +``` |
| 177 | + |
| 178 | +## Architecture |
| 179 | + |
| 180 | +``` |
| 181 | +minidb/ |
| 182 | +├── __init__.py # Public API exports |
| 183 | +├── database.py # MiniDB main class |
| 184 | +├── table.py # Table storage |
| 185 | +├── column.py # Column and Schema definitions |
| 186 | +├── index.py # Hash-based indexing |
| 187 | +├── parser.py # SQL lexer and parser |
| 188 | +├── query.py # Query execution engine |
| 189 | +├── planner.py # Query planner |
| 190 | +├── persistence.py # JSON serialization |
| 191 | +├── types.py # Type definitions |
| 192 | +└── errors.py # Custom exceptions |
| 193 | +``` |
| 194 | + |
| 195 | +## Test Suite |
| 196 | + |
| 197 | +MiniDB includes a comprehensive test suite with 29+ test cases: |
| 198 | + |
| 199 | +```bash |
| 200 | +# Run all tests |
| 201 | +python -m pytest tests/ -v |
| 202 | + |
| 203 | +# Run specific test file |
| 204 | +python -m pytest tests/test_queries.py -v |
| 205 | + |
| 206 | +# Run with coverage |
| 207 | +python -m pytest tests/ -v --cov=minidb |
| 208 | +``` |
| 209 | + |
| 210 | +### Test Categories |
| 211 | + |
| 212 | +- **test_database.py**: Database lifecycle and table management |
| 213 | +- **test_crud.py**: INSERT, SELECT, UPDATE, DELETE operations |
| 214 | +- **test_queries.py**: WHERE, ORDER BY, LIMIT |
| 215 | +- **test_aggregations.py**: COUNT, SUM, AVG, MIN, MAX, GROUP BY |
| 216 | +- **test_joins.py**: JOIN operations |
| 217 | +- **test_index.py**: Indexing and query planning |
| 218 | +- **test_persistence.py**: Save/load functionality |
| 219 | +- **test_performance.py**: Large dataset tests (10,000+ rows) |
| 220 | + |
| 221 | +## Performance |
| 222 | + |
| 223 | +MiniDB is designed for small to medium datasets: |
| 224 | + |
| 225 | +- **10,000 rows**: All operations complete in under 30 seconds |
| 226 | +- **Indexed lookups**: O(1) for primary key equality |
| 227 | +- **Range queries**: O(n) with index optimization |
| 228 | +- **Table scans**: O(n) for non-indexed columns |
| 229 | + |
| 230 | +## Limitations |
| 231 | + |
| 232 | +- In-memory only (no disk-based storage during operation) |
| 233 | +- Single-threaded |
| 234 | +- No transactions |
| 235 | +- No foreign key constraints |
| 236 | +- No NULL handling in some edge cases |
| 237 | +- Limited JOIN support (INNER JOIN only) |
| 238 | + |
| 239 | +## License |
| 240 | + |
| 241 | +MIT License - Use freely for any purpose. |
| 242 | + |
| 243 | +## Contributing |
| 244 | + |
| 245 | +Contributions welcome! Areas for improvement: |
| 246 | + |
| 247 | +- B-tree indexes for range queries |
| 248 | +- LEFT/RIGHT OUTER JOIN |
| 249 | +- Subqueries |
| 250 | +- HAVING clause |
| 251 | +- DISTINCT |
| 252 | +- More aggregate functions |
| 253 | +- Query optimization |
| 254 | +- Concurrent access |
0 commit comments