minidb-sql 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- minidb_sql/__init__.py +168 -0
- minidb_sql/column.py +170 -0
- minidb_sql/database.py +258 -0
- minidb_sql/errors.py +176 -0
- minidb_sql/index.py +222 -0
- minidb_sql/parser.py +1278 -0
- minidb_sql/persistence.py +72 -0
- minidb_sql/planner.py +167 -0
- minidb_sql/query.py +1077 -0
- minidb_sql/table.py +274 -0
- minidb_sql/types.py +189 -0
- minidb_sql-1.3.0.dist-info/METADATA +379 -0
- minidb_sql-1.3.0.dist-info/RECORD +16 -0
- minidb_sql-1.3.0.dist-info/WHEEL +5 -0
- minidb_sql-1.3.0.dist-info/licenses/LICENSE +21 -0
- minidb_sql-1.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minidb-sql
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: A miniature in-memory database with SQL-like query support
|
|
5
|
+
Author: MiniDB Contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sebtardif/MiniDB
|
|
8
|
+
Project-URL: Repository, https://github.com/sebtardif/MiniDB
|
|
9
|
+
Project-URL: Issues, https://github.com/sebtardif/MiniDB/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/sebtardif/MiniDB/blob/main/CHANGELOG.md
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Database
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# MiniDB
|
|
26
|
+
|
|
27
|
+
[](https://github.com/SebTardif/MiniDB/actions/workflows/ci.yml?query=branch%3Amain)
|
|
28
|
+
[](https://github.com/SebTardif/MiniDB/actions/workflows/security.yml)
|
|
29
|
+
[](https://github.com/SebTardif/MiniDB/releases/latest)
|
|
30
|
+
[](https://www.python.org/downloads/)
|
|
31
|
+
[](./LICENSE)
|
|
32
|
+
|
|
33
|
+
[](https://github.com/SebTardif/MiniDB/tree/main/tests)
|
|
34
|
+
[](https://github.com/SebTardif/MiniDB/actions/workflows/scorecard.yml)
|
|
35
|
+
|
|
36
|
+
A miniature in-memory database with SQL-like query support, built from scratch using only Python 3.11+ standard library.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Typed Columns**: INTEGER, STRING, FLOAT, BOOLEAN
|
|
41
|
+
- **CRUD Operations**: INSERT, SELECT, UPDATE, DELETE
|
|
42
|
+
- **Parameters**: `?` placeholders bound after parse (`execute(sql, params)`)
|
|
43
|
+
- **Transactions**: in-memory `BEGIN` / `COMMIT` / `ROLLBACK` snapshots
|
|
44
|
+
- **SQL-like Query Language**:
|
|
45
|
+
- WHERE with AND/OR, NOT, parentheses, comparisons (=, >, <, >=, <=, !=, LIKE, IN), and IS NULL / IS NOT NULL
|
|
46
|
+
- SELECT column aliases (`col AS alias`)
|
|
47
|
+
- SELECT DISTINCT
|
|
48
|
+
- ORDER BY (ASC/DESC)
|
|
49
|
+
- GROUP BY with aggregations
|
|
50
|
+
- HAVING after GROUP BY
|
|
51
|
+
- LIMIT clause
|
|
52
|
+
- Searched `CASE` in the select list (`CASE WHEN ... THEN ... ELSE ... END`)
|
|
53
|
+
- **Aggregations**: COUNT, SUM, AVG, MIN, MAX
|
|
54
|
+
- **JOINs**: INNER JOIN and LEFT JOIN between tables
|
|
55
|
+
- **Indexing**: Automatic hash-based indexing on primary keys
|
|
56
|
+
- **Query Planner**: Chooses between index scans and table scans (`db.explain(sql)`)
|
|
57
|
+
- **Persistence**: Save/load database to JSON files with versioning
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install minidb-sql
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The library uses only the Python standard library. `pip install minidb` is a different project.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from minidb_sql import MiniDB
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
`execute()` runs any statement and returns a row id, an affected-row count, or None.
|
|
74
|
+
`query()` is SELECT-only and returns rows.
|
|
75
|
+
Both accept an optional `params` sequence for `?` placeholders. Values are bound
|
|
76
|
+
after parse, not interpolated into the SQL string.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from minidb_sql import MiniDB, Column, ColumnType
|
|
80
|
+
|
|
81
|
+
# Create database
|
|
82
|
+
db = MiniDB()
|
|
83
|
+
|
|
84
|
+
# Create table using SQL
|
|
85
|
+
db.execute("""
|
|
86
|
+
CREATE TABLE users (
|
|
87
|
+
id INTEGER PRIMARY KEY,
|
|
88
|
+
name STRING NOT NULL,
|
|
89
|
+
age INTEGER,
|
|
90
|
+
salary FLOAT,
|
|
91
|
+
active BOOLEAN
|
|
92
|
+
)
|
|
93
|
+
""")
|
|
94
|
+
|
|
95
|
+
# Or create table programmatically
|
|
96
|
+
db.create_table(
|
|
97
|
+
'orders',
|
|
98
|
+
[
|
|
99
|
+
Column('id', ColumnType.INTEGER, primary_key=True),
|
|
100
|
+
Column('user_id', ColumnType.INTEGER),
|
|
101
|
+
Column('product', ColumnType.STRING),
|
|
102
|
+
Column('total', ColumnType.FLOAT),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Insert data
|
|
107
|
+
db.execute("INSERT INTO users (id, name, age, salary, active) VALUES (1, 'Alice', 30, 75000.0, true)")
|
|
108
|
+
db.execute(
|
|
109
|
+
'INSERT INTO users (id, name, age, salary, active) VALUES (?, ?, ?, ?, ?)',
|
|
110
|
+
[2, 'Bob', 25, 55000.0, False],
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Query data
|
|
114
|
+
results = db.query('SELECT * FROM users WHERE age > ?', [28])
|
|
115
|
+
|
|
116
|
+
# Complex queries
|
|
117
|
+
results = db.query("""
|
|
118
|
+
SELECT name, salary FROM users
|
|
119
|
+
WHERE active = true AND salary > 60000
|
|
120
|
+
ORDER BY salary DESC
|
|
121
|
+
""")
|
|
122
|
+
|
|
123
|
+
# Inspect the SELECT plan (index_scan vs table_scan)
|
|
124
|
+
print(db.explain('SELECT * FROM users WHERE id = 1'))
|
|
125
|
+
|
|
126
|
+
# Aggregations
|
|
127
|
+
results = db.query('SELECT COUNT(*), AVG(salary) FROM users')
|
|
128
|
+
|
|
129
|
+
# GROUP BY with HAVING
|
|
130
|
+
results = db.query("""
|
|
131
|
+
SELECT active, COUNT(*), AVG(salary)
|
|
132
|
+
FROM users
|
|
133
|
+
GROUP BY active
|
|
134
|
+
HAVING COUNT(*) > 1
|
|
135
|
+
""")
|
|
136
|
+
|
|
137
|
+
# JOINs
|
|
138
|
+
results = db.query("""
|
|
139
|
+
SELECT users.name, orders.product, orders.total
|
|
140
|
+
FROM users
|
|
141
|
+
JOIN orders ON users.id = orders.user_id
|
|
142
|
+
WHERE orders.total > 50
|
|
143
|
+
""")
|
|
144
|
+
|
|
145
|
+
# UPDATE
|
|
146
|
+
affected = db.execute('UPDATE users SET salary = 80000.0 WHERE id = 1')
|
|
147
|
+
|
|
148
|
+
# DELETE
|
|
149
|
+
affected = db.execute('DELETE FROM users WHERE active = false')
|
|
150
|
+
|
|
151
|
+
# Snapshot transaction (in-memory only)
|
|
152
|
+
db.execute('BEGIN')
|
|
153
|
+
db.execute("INSERT INTO users (id, name, age, salary, active) VALUES (3, 'Cara', 40, 90000.0, true)")
|
|
154
|
+
db.execute('ROLLBACK')
|
|
155
|
+
|
|
156
|
+
# DROP TABLE (SQL or Python API)
|
|
157
|
+
db.execute('CREATE TABLE scratch (id INTEGER PRIMARY KEY)')
|
|
158
|
+
db.execute('DROP TABLE scratch')
|
|
159
|
+
db.create_table('temp', [Column('id', ColumnType.INTEGER, primary_key=True)])
|
|
160
|
+
db.drop_table('temp')
|
|
161
|
+
|
|
162
|
+
# Persistence
|
|
163
|
+
db.save('my_database.json')
|
|
164
|
+
db = MiniDB.load('my_database.json')
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## SQL Syntax Reference
|
|
168
|
+
|
|
169
|
+
### CREATE TABLE
|
|
170
|
+
|
|
171
|
+
```sql
|
|
172
|
+
CREATE TABLE table_name (
|
|
173
|
+
column1 INTEGER PRIMARY KEY,
|
|
174
|
+
column2 STRING NOT NULL,
|
|
175
|
+
column3 FLOAT,
|
|
176
|
+
column4 BOOLEAN
|
|
177
|
+
)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### INSERT
|
|
181
|
+
|
|
182
|
+
```sql
|
|
183
|
+
INSERT INTO table_name (col1, col2, col3) VALUES (1, 'value', 3.14)
|
|
184
|
+
INSERT INTO table_name (col1, col2, col3) VALUES (?, ?, ?)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Use `db.execute(sql, [1, 'value', 3.14])` or `db.query(sql, params)` to bind `?`
|
|
188
|
+
left-to-right. Too few or too many params raise `InvalidQueryError`.
|
|
189
|
+
|
|
190
|
+
### SELECT
|
|
191
|
+
|
|
192
|
+
```sql
|
|
193
|
+
SELECT * FROM table_name
|
|
194
|
+
SELECT col1, col2 FROM table_name
|
|
195
|
+
SELECT col1 AS alias FROM table_name
|
|
196
|
+
SELECT DISTINCT col1 FROM table_name
|
|
197
|
+
SELECT DISTINCT col1, col2 FROM table_name
|
|
198
|
+
SELECT col1, COUNT(*), AVG(col2) FROM table_name GROUP BY col1
|
|
199
|
+
SELECT col1, COUNT(*) FROM table_name GROUP BY col1 HAVING COUNT(*) > 1
|
|
200
|
+
SELECT col1, COUNT(*) AS n FROM table_name GROUP BY col1 HAVING n > 1
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### CASE
|
|
204
|
+
|
|
205
|
+
```sql
|
|
206
|
+
SELECT CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END FROM users
|
|
207
|
+
SELECT CASE WHEN age >= 18 THEN 'adult' WHEN age IS NULL THEN 'unknown' ELSE 'minor' END AS kind
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`CASE` is searched form in the select list. The first true `WHEN` wins. A comparison that is unknown, including `= NULL`, is not true, so a later `WHEN` or `ELSE` runs. No `ELSE` returns NULL. `WHEN` uses the same comparison rules as `WHERE`. The result column is `CASE` unless you give it an alias. `CASE` next to `GROUP BY` may only read grouped columns.
|
|
211
|
+
|
|
212
|
+
### HAVING
|
|
213
|
+
|
|
214
|
+
```sql
|
|
215
|
+
HAVING COUNT(*) > 1
|
|
216
|
+
HAVING SUM(quantity) > 30
|
|
217
|
+
HAVING n > 2
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
HAVING filters grouped rows after GROUP BY (or the single row from an ungrouped aggregate). It is rejected when the SELECT has neither GROUP BY nor aggregate functions.
|
|
221
|
+
|
|
222
|
+
### WHERE Clause
|
|
223
|
+
|
|
224
|
+
```sql
|
|
225
|
+
WHERE col = value
|
|
226
|
+
WHERE col > value
|
|
227
|
+
WHERE col >= value
|
|
228
|
+
WHERE col < value
|
|
229
|
+
WHERE col <= value
|
|
230
|
+
WHERE col != value
|
|
231
|
+
WHERE col LIKE 'pattern%' -- % matches any sequence
|
|
232
|
+
WHERE col IN (1, 2, 3)
|
|
233
|
+
WHERE col = ?
|
|
234
|
+
WHERE col IN (?, ?)
|
|
235
|
+
WHERE col IS NULL
|
|
236
|
+
WHERE col IS NOT NULL
|
|
237
|
+
WHERE cond1 AND cond2
|
|
238
|
+
WHERE cond1 OR cond2
|
|
239
|
+
WHERE NOT col = value
|
|
240
|
+
WHERE (cond1 OR cond2) AND cond3
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### ORDER BY
|
|
244
|
+
|
|
245
|
+
```sql
|
|
246
|
+
ORDER BY col ASC
|
|
247
|
+
ORDER BY col DESC
|
|
248
|
+
ORDER BY col1 ASC, col2 DESC
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### LIMIT
|
|
252
|
+
|
|
253
|
+
```sql
|
|
254
|
+
LIMIT 10
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### JOIN
|
|
258
|
+
|
|
259
|
+
```sql
|
|
260
|
+
SELECT t1.col, t2.col
|
|
261
|
+
FROM t1
|
|
262
|
+
JOIN t2 ON t1.id = t2.t1_id
|
|
263
|
+
|
|
264
|
+
-- LEFT JOIN (includes unmatched left rows with NULLs)
|
|
265
|
+
SELECT t1.col, t2.col
|
|
266
|
+
FROM t1
|
|
267
|
+
LEFT JOIN t2 ON t1.id = t2.t1_id
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### UPDATE
|
|
271
|
+
|
|
272
|
+
```sql
|
|
273
|
+
UPDATE table_name SET col1 = value1, col2 = value2 WHERE condition
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### DELETE
|
|
277
|
+
|
|
278
|
+
```sql
|
|
279
|
+
DELETE FROM table_name WHERE condition
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### BEGIN / COMMIT / ROLLBACK
|
|
283
|
+
|
|
284
|
+
```sql
|
|
285
|
+
BEGIN
|
|
286
|
+
COMMIT
|
|
287
|
+
ROLLBACK
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
These copy the in-memory tables at `BEGIN` and restore that copy on `ROLLBACK`.
|
|
291
|
+
They are not durable and not concurrent. MiniDB is single-threaded.
|
|
292
|
+
|
|
293
|
+
### DROP TABLE
|
|
294
|
+
|
|
295
|
+
```sql
|
|
296
|
+
DROP TABLE table_name
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
db.execute('DROP TABLE table_name')
|
|
301
|
+
db.drop_table('table_name')
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Architecture
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
minidb_sql/
|
|
308
|
+
├── __init__.py # Public API exports
|
|
309
|
+
├── database.py # MiniDB main class
|
|
310
|
+
├── table.py # Table storage
|
|
311
|
+
├── column.py # Column and Schema definitions
|
|
312
|
+
├── index.py # Hash-based indexing
|
|
313
|
+
├── parser.py # SQL lexer and parser
|
|
314
|
+
├── query.py # Query execution engine
|
|
315
|
+
├── planner.py # Query planner
|
|
316
|
+
├── persistence.py # JSON serialization
|
|
317
|
+
├── types.py # Type definitions
|
|
318
|
+
└── errors.py # Custom exceptions
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Test Suite
|
|
322
|
+
|
|
323
|
+
MiniDB includes a comprehensive test suite with 95+ test cases:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Run all tests
|
|
327
|
+
python -m pytest tests/ -v
|
|
328
|
+
|
|
329
|
+
# Run specific test file
|
|
330
|
+
python -m pytest tests/test_queries.py -v
|
|
331
|
+
|
|
332
|
+
# Run with coverage
|
|
333
|
+
python -m pytest tests/ -v --cov=minidb_sql
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Test Categories
|
|
337
|
+
|
|
338
|
+
- **test_database.py**: Database lifecycle, table management, `?` parameters, and snapshot transactions
|
|
339
|
+
- **test_crud.py**: INSERT, SELECT, UPDATE, DELETE operations
|
|
340
|
+
- **test_queries.py**: WHERE, ORDER BY, LIMIT, parser errors, type validation
|
|
341
|
+
- **test_aggregations.py**: COUNT, SUM, AVG, MIN, MAX, GROUP BY, HAVING
|
|
342
|
+
- **test_joins.py**: JOIN operations
|
|
343
|
+
- **test_index.py**: Indexing and query planning
|
|
344
|
+
- **test_persistence.py**: Save/load functionality
|
|
345
|
+
- **test_performance.py**: Large dataset tests (10,000+ rows)
|
|
346
|
+
|
|
347
|
+
## Performance
|
|
348
|
+
|
|
349
|
+
MiniDB is designed for small to medium datasets:
|
|
350
|
+
|
|
351
|
+
- **10,000 rows**: All operations complete in under 30 seconds
|
|
352
|
+
- **Indexed lookups**: O(1) for primary key equality
|
|
353
|
+
- **Range queries**: O(n) with index optimization
|
|
354
|
+
- **Table scans**: O(n) for non-indexed columns
|
|
355
|
+
|
|
356
|
+
## Limitations
|
|
357
|
+
|
|
358
|
+
- In-memory only (no disk-based storage during operation)
|
|
359
|
+
- Single-threaded
|
|
360
|
+
- Transactions are in-memory snapshots only (not durable, not concurrent)
|
|
361
|
+
- No foreign key constraints
|
|
362
|
+
- Limited JOIN support (INNER and LEFT JOIN only, no RIGHT JOIN execution)
|
|
363
|
+
|
|
364
|
+
## License
|
|
365
|
+
|
|
366
|
+
MIT License - Use freely for any purpose.
|
|
367
|
+
|
|
368
|
+
## Contributing
|
|
369
|
+
|
|
370
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
371
|
+
|
|
372
|
+
Areas for improvement:
|
|
373
|
+
|
|
374
|
+
- B-tree indexes for range queries
|
|
375
|
+
- RIGHT JOIN
|
|
376
|
+
- Subqueries
|
|
377
|
+
- More aggregate functions
|
|
378
|
+
- Query optimization
|
|
379
|
+
- Concurrent access
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
minidb_sql/__init__.py,sha256=GuP1vlebl86YAseINj-zOe8OV2C3lWKemLZtq_9VOsk,3780
|
|
2
|
+
minidb_sql/column.py,sha256=IaFunqAf4t9VuBeUyqg7Ga42gVP_X8scMldwagkg-Qw,5892
|
|
3
|
+
minidb_sql/database.py,sha256=6FJaCNumKwhpvdH3d4W1xH6VJTuYqDiH52aATA_CVIE,7868
|
|
4
|
+
minidb_sql/errors.py,sha256=T7yOhaQlAqPNLrIYKzZaZgOusGIBqy-T0oXc7iZstm4,4817
|
|
5
|
+
minidb_sql/index.py,sha256=K6xNGyszpNdEaC7JBmsJwfG1-HrYhsZgQtHiFiivQJs,7597
|
|
6
|
+
minidb_sql/parser.py,sha256=PyxR9idz1mJLK6S8b-kX_xMvbZ4Z1KgQiDmB82cj7EA,41193
|
|
7
|
+
minidb_sql/persistence.py,sha256=duwO83p5BNtWTVkk9-RP65O52hFVECH71Rqz1EZlLbI,2159
|
|
8
|
+
minidb_sql/planner.py,sha256=Lzhd_y5dnRyrKVLrGfQR527PRCUKewnixPRBM4gZc6k,5469
|
|
9
|
+
minidb_sql/query.py,sha256=rsMksnFwCftx9xn3b6msyKjSxLOegDsUFsJY9VYdkZ8,46554
|
|
10
|
+
minidb_sql/table.py,sha256=Q1g_aFIaWKQhwId1OctWGMEtRaYQI4aXrxV3KTnZDPA,9294
|
|
11
|
+
minidb_sql/types.py,sha256=oQAgyQX_uGnTvsZqUgAh-PURBLPeOJIRgeKSx4x8eLM,5099
|
|
12
|
+
minidb_sql-1.3.0.dist-info/licenses/LICENSE,sha256=457P-Q6b8q8CuJhk5SkOeqPvwlzrHYXA4lePbJeOktE,1073
|
|
13
|
+
minidb_sql-1.3.0.dist-info/METADATA,sha256=bIYzmYPlyqJJvT9IFLCJuwW4_304QOpPVmowLKd_E6U,10873
|
|
14
|
+
minidb_sql-1.3.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
15
|
+
minidb_sql-1.3.0.dist-info/top_level.txt,sha256=Ti3AuUr86Ce5BHEh7KCs9Cx313q1oJ4u4HkzicXmiPc,11
|
|
16
|
+
minidb_sql-1.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sebastien Tardif
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
minidb_sql
|