hanifx-db 28.0.0__tar.gz
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.
- hanifx_db-28.0.0/LICENSE +21 -0
- hanifx_db-28.0.0/PKG-INFO +317 -0
- hanifx_db-28.0.0/README.md +278 -0
- hanifx_db-28.0.0/hanifx_db.egg-info/PKG-INFO +317 -0
- hanifx_db-28.0.0/hanifx_db.egg-info/SOURCES.txt +9 -0
- hanifx_db-28.0.0/hanifx_db.egg-info/dependency_links.txt +1 -0
- hanifx_db-28.0.0/hanifx_db.egg-info/top_level.txt +1 -0
- hanifx_db-28.0.0/setup.cfg +4 -0
- hanifx_db-28.0.0/setup.py +49 -0
- hanifx_db-28.0.0/tests/__init__.py +0 -0
- hanifx_db-28.0.0/tests/test_all.py +340 -0
hanifx_db-28.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hanif (HanifX)
|
|
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,317 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hanifx-db
|
|
3
|
+
Version: 28.0.0
|
|
4
|
+
Summary: HanifX Database Engine ā Custom binary database with built-in HanifX security encoding
|
|
5
|
+
Home-page: https://pypi.org/project/hanifx-db
|
|
6
|
+
Author: Hanif
|
|
7
|
+
Author-email: sajim4653@gmail.com
|
|
8
|
+
Project-URL: Source, https://pypi.org/project/hanifx-db
|
|
9
|
+
Project-URL: Tracker, https://pypi.org/project/hanifx-db
|
|
10
|
+
Keywords: database,db,hanifx,security,encoding,encryption,custom-database,binary-format,hxdb,termux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Topic :: Database
|
|
23
|
+
Classifier: Topic :: Security :: Cryptography
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.6
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: project-url
|
|
37
|
+
Dynamic: requires-python
|
|
38
|
+
Dynamic: summary
|
|
39
|
+
|
|
40
|
+
# HanifX DB šļø
|
|
41
|
+
|
|
42
|
+
**HanifX Database Engine** ā A fully custom binary database built from scratch in pure Python with built-in HanifX security encoding.
|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/hanifx-db)
|
|
45
|
+
[](https://pypi.org/project/hanifx-db)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://pypi.org/project/hanifx-db)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ⨠Features
|
|
52
|
+
|
|
53
|
+
- š **Built-in HanifX Security** ā Irreversible encoding, no external dependency
|
|
54
|
+
- šļø **Multi-Table Support** ā Multiple tables in a single `.hxdb` file
|
|
55
|
+
- ā” **O(1) Index System** ā Custom hash table for fast lookups
|
|
56
|
+
- š **Transactions + WAL** ā Commit, rollback, crash recovery
|
|
57
|
+
- šļø **Custom Compression** ā Own RLE + LZ algorithm
|
|
58
|
+
- š **Relations** ā Foreign keys, ONE_TO_ONE, ONE_TO_MANY, MANY_TO_MANY
|
|
59
|
+
- š **Query Builder** ā Fluent query API with 11 operators
|
|
60
|
+
- š¾ **LRU Cache** ā Fast repeated reads
|
|
61
|
+
- š **Operation Logging** ā Every operation logged to `.hxlog`
|
|
62
|
+
- š¦ **Pure Python** ā No external dependencies, works on Termux
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## š¦ Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install hanifx-db
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## š Quick Start
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from hanifxdb import HanifXDB
|
|
78
|
+
|
|
79
|
+
# Open / Create database
|
|
80
|
+
db = HanifXDB("mydata.hxdb")
|
|
81
|
+
|
|
82
|
+
# Create table
|
|
83
|
+
db.create_table("users")
|
|
84
|
+
|
|
85
|
+
# Set plain data
|
|
86
|
+
db.set("users", "name", "Sazzad")
|
|
87
|
+
db.set("users", "age", 20)
|
|
88
|
+
db.set("users", "is_admin", True)
|
|
89
|
+
|
|
90
|
+
# Set secret data (irreversible HanifX encoding)
|
|
91
|
+
db.set_secret("users", "password", "mypass123")
|
|
92
|
+
|
|
93
|
+
# Get data
|
|
94
|
+
print(db.get("users", "name")) # Sazzad
|
|
95
|
+
print(db.get("users", "age")) # 20
|
|
96
|
+
|
|
97
|
+
# Verify secret
|
|
98
|
+
if db.verify("users", "password", "mypass123"):
|
|
99
|
+
print("Login success!")
|
|
100
|
+
|
|
101
|
+
# Close
|
|
102
|
+
db.close()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## š Security
|
|
108
|
+
|
|
109
|
+
HanifX DB uses the built-in **HanifX 24.0.0** encoding algorithm:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# Store secret (irreversible)
|
|
113
|
+
db.set_secret("users", "token", "mytoken123")
|
|
114
|
+
|
|
115
|
+
# Verify (checksum-based)
|
|
116
|
+
db.verify("users", "token", "mytoken123") # True
|
|
117
|
+
db.verify("users", "token", "wrongtoken") # HanifXVerifyError
|
|
118
|
+
|
|
119
|
+
# Encode file
|
|
120
|
+
db.set_file("users", "photo", "profile.jpg")
|
|
121
|
+
|
|
122
|
+
# Generate secure token
|
|
123
|
+
token = db.generate_token(32)
|
|
124
|
+
uid = db.generate_id()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## šļø Multi-Table
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
db = HanifXDB("shop.hxdb")
|
|
133
|
+
|
|
134
|
+
db.create_table("users")
|
|
135
|
+
db.create_table("orders")
|
|
136
|
+
db.create_table("products")
|
|
137
|
+
|
|
138
|
+
db.set("users", "user_1", {"name": "Sazzad", "age": 20})
|
|
139
|
+
db.set("products", "prod_1", {"name": "Phone", "price": 500})
|
|
140
|
+
db.set("orders", "ord_1", {"user": "user_1", "product": "prod_1"})
|
|
141
|
+
|
|
142
|
+
print(db.list_tables()) # ['users', 'orders', 'products']
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## š Query Builder
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from hanifxdb import OP_GTE, OP_LIKE
|
|
151
|
+
|
|
152
|
+
# Fluent query
|
|
153
|
+
results = (
|
|
154
|
+
db.query("users")
|
|
155
|
+
.where("age", OP_GTE, 18)
|
|
156
|
+
.where("name", OP_LIKE, "saz")
|
|
157
|
+
.order_by("age")
|
|
158
|
+
.limit(10)
|
|
159
|
+
.execute()
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Shortcuts
|
|
163
|
+
db.find_all("users")
|
|
164
|
+
db.find_one("users", "name", "Sazzad")
|
|
165
|
+
db.find_like("users", "name", "saz")
|
|
166
|
+
db.count("users")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## š Transactions
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
db.begin()
|
|
175
|
+
try:
|
|
176
|
+
db.set("users", "name", "Sazzad")
|
|
177
|
+
db.set("users", "age", 20)
|
|
178
|
+
db.set_secret("users", "password", "pass123")
|
|
179
|
+
db.commit()
|
|
180
|
+
except Exception:
|
|
181
|
+
db.rollback()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## š Relations
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# Add relation
|
|
190
|
+
db.add_relation(
|
|
191
|
+
name = "user_orders",
|
|
192
|
+
from_table = "orders",
|
|
193
|
+
from_key = "user_id",
|
|
194
|
+
to_table = "users",
|
|
195
|
+
to_key = "id"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Get related records
|
|
199
|
+
related = db.get_related("user_orders", "user_1")
|
|
200
|
+
|
|
201
|
+
# Validate foreign key
|
|
202
|
+
db.validate_fk("user_orders", "user_1")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## š¾ Backup & Restore
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Backup
|
|
211
|
+
db.backup("backup.hxbak")
|
|
212
|
+
|
|
213
|
+
# Restore
|
|
214
|
+
db.restore("backup.hxbak")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## š Info & Logs
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# Database info
|
|
223
|
+
print(db.info())
|
|
224
|
+
|
|
225
|
+
# Cache stats
|
|
226
|
+
print(db.cache_stats())
|
|
227
|
+
|
|
228
|
+
# Read logs
|
|
229
|
+
logs = db.read_logs()
|
|
230
|
+
for log in logs:
|
|
231
|
+
print(log)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## š„ļø Context Manager
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
with HanifXDB("mydata.hxdb") as db:
|
|
240
|
+
db.create_table("users")
|
|
241
|
+
db.set("users", "name", "Sazzad")
|
|
242
|
+
# Auto close on exit
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## š File Format
|
|
248
|
+
|
|
249
|
+
HanifX DB uses a custom binary `.hxdb` format:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
HANIFXDB ā Magic header (8 bytes)
|
|
253
|
+
Version ā DB version (4 bytes)
|
|
254
|
+
Timestamps ā Created / Modified
|
|
255
|
+
Table sections ā HX_TBL__ + records
|
|
256
|
+
Records ā HX_REC__ + key + value + checksum
|
|
257
|
+
HX_END__ ā EOF signature
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## š Supported Data Types
|
|
263
|
+
|
|
264
|
+
| Type | Python | Example |
|
|
265
|
+
|----------|---------------|----------------------------|
|
|
266
|
+
| text | str | `"Sazzad"` |
|
|
267
|
+
| number | int / float | `20`, `3.14` |
|
|
268
|
+
| bool | bool | `True`, `False` |
|
|
269
|
+
| secret | str (encoded) | `"password123"` ā encoded |
|
|
270
|
+
| file | str (path) | `"photo.jpg"` ā encoded |
|
|
271
|
+
| list | list | `[1, 2, 3]` |
|
|
272
|
+
| dict | dict | `{"name": "Sazzad"}` |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## ā” Query Operators
|
|
277
|
+
|
|
278
|
+
| Operator | Meaning |
|
|
279
|
+
|------------|----------------------|
|
|
280
|
+
| `OP_EQ` | Equal |
|
|
281
|
+
| `OP_NEQ` | Not equal |
|
|
282
|
+
| `OP_GT` | Greater than |
|
|
283
|
+
| `OP_GTE` | Greater than or equal|
|
|
284
|
+
| `OP_LT` | Less than |
|
|
285
|
+
| `OP_LTE` | Less than or equal |
|
|
286
|
+
| `OP_IN` | Value in list |
|
|
287
|
+
| `OP_NOT_IN`| Value not in list |
|
|
288
|
+
| `OP_LIKE` | String contains |
|
|
289
|
+
| `OP_STARTS`| String starts with |
|
|
290
|
+
| `OP_ENDS` | String ends with |
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## š± Termux Support
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Install on Termux (Android)
|
|
298
|
+
pip install hanifx-db
|
|
299
|
+
|
|
300
|
+
# Use immediately - no extra setup needed
|
|
301
|
+
python3 -c "from hanifxdb import HanifXDB; print('HanifX DB ready!')"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## šØāš» Author
|
|
307
|
+
|
|
308
|
+
**Hanif (HanifX)**
|
|
309
|
+
- PyPI: [habib_bi](https://pypi.org/user/habib_bi)
|
|
310
|
+
- Email: sajim4653@gmail.com
|
|
311
|
+
- Package: [hanifx-db](https://pypi.org/project/hanifx-db)
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## š License
|
|
316
|
+
|
|
317
|
+
MIT License ā Free to use, modify, and distribute.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# HanifX DB šļø
|
|
2
|
+
|
|
3
|
+
**HanifX Database Engine** ā A fully custom binary database built from scratch in pure Python with built-in HanifX security encoding.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/hanifx-db)
|
|
6
|
+
[](https://pypi.org/project/hanifx-db)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://pypi.org/project/hanifx-db)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ⨠Features
|
|
13
|
+
|
|
14
|
+
- š **Built-in HanifX Security** ā Irreversible encoding, no external dependency
|
|
15
|
+
- šļø **Multi-Table Support** ā Multiple tables in a single `.hxdb` file
|
|
16
|
+
- ā” **O(1) Index System** ā Custom hash table for fast lookups
|
|
17
|
+
- š **Transactions + WAL** ā Commit, rollback, crash recovery
|
|
18
|
+
- šļø **Custom Compression** ā Own RLE + LZ algorithm
|
|
19
|
+
- š **Relations** ā Foreign keys, ONE_TO_ONE, ONE_TO_MANY, MANY_TO_MANY
|
|
20
|
+
- š **Query Builder** ā Fluent query API with 11 operators
|
|
21
|
+
- š¾ **LRU Cache** ā Fast repeated reads
|
|
22
|
+
- š **Operation Logging** ā Every operation logged to `.hxlog`
|
|
23
|
+
- š¦ **Pure Python** ā No external dependencies, works on Termux
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## š¦ Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install hanifx-db
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## š Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from hanifxdb import HanifXDB
|
|
39
|
+
|
|
40
|
+
# Open / Create database
|
|
41
|
+
db = HanifXDB("mydata.hxdb")
|
|
42
|
+
|
|
43
|
+
# Create table
|
|
44
|
+
db.create_table("users")
|
|
45
|
+
|
|
46
|
+
# Set plain data
|
|
47
|
+
db.set("users", "name", "Sazzad")
|
|
48
|
+
db.set("users", "age", 20)
|
|
49
|
+
db.set("users", "is_admin", True)
|
|
50
|
+
|
|
51
|
+
# Set secret data (irreversible HanifX encoding)
|
|
52
|
+
db.set_secret("users", "password", "mypass123")
|
|
53
|
+
|
|
54
|
+
# Get data
|
|
55
|
+
print(db.get("users", "name")) # Sazzad
|
|
56
|
+
print(db.get("users", "age")) # 20
|
|
57
|
+
|
|
58
|
+
# Verify secret
|
|
59
|
+
if db.verify("users", "password", "mypass123"):
|
|
60
|
+
print("Login success!")
|
|
61
|
+
|
|
62
|
+
# Close
|
|
63
|
+
db.close()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## š Security
|
|
69
|
+
|
|
70
|
+
HanifX DB uses the built-in **HanifX 24.0.0** encoding algorithm:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# Store secret (irreversible)
|
|
74
|
+
db.set_secret("users", "token", "mytoken123")
|
|
75
|
+
|
|
76
|
+
# Verify (checksum-based)
|
|
77
|
+
db.verify("users", "token", "mytoken123") # True
|
|
78
|
+
db.verify("users", "token", "wrongtoken") # HanifXVerifyError
|
|
79
|
+
|
|
80
|
+
# Encode file
|
|
81
|
+
db.set_file("users", "photo", "profile.jpg")
|
|
82
|
+
|
|
83
|
+
# Generate secure token
|
|
84
|
+
token = db.generate_token(32)
|
|
85
|
+
uid = db.generate_id()
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## šļø Multi-Table
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
db = HanifXDB("shop.hxdb")
|
|
94
|
+
|
|
95
|
+
db.create_table("users")
|
|
96
|
+
db.create_table("orders")
|
|
97
|
+
db.create_table("products")
|
|
98
|
+
|
|
99
|
+
db.set("users", "user_1", {"name": "Sazzad", "age": 20})
|
|
100
|
+
db.set("products", "prod_1", {"name": "Phone", "price": 500})
|
|
101
|
+
db.set("orders", "ord_1", {"user": "user_1", "product": "prod_1"})
|
|
102
|
+
|
|
103
|
+
print(db.list_tables()) # ['users', 'orders', 'products']
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## š Query Builder
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from hanifxdb import OP_GTE, OP_LIKE
|
|
112
|
+
|
|
113
|
+
# Fluent query
|
|
114
|
+
results = (
|
|
115
|
+
db.query("users")
|
|
116
|
+
.where("age", OP_GTE, 18)
|
|
117
|
+
.where("name", OP_LIKE, "saz")
|
|
118
|
+
.order_by("age")
|
|
119
|
+
.limit(10)
|
|
120
|
+
.execute()
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Shortcuts
|
|
124
|
+
db.find_all("users")
|
|
125
|
+
db.find_one("users", "name", "Sazzad")
|
|
126
|
+
db.find_like("users", "name", "saz")
|
|
127
|
+
db.count("users")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## š Transactions
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
db.begin()
|
|
136
|
+
try:
|
|
137
|
+
db.set("users", "name", "Sazzad")
|
|
138
|
+
db.set("users", "age", 20)
|
|
139
|
+
db.set_secret("users", "password", "pass123")
|
|
140
|
+
db.commit()
|
|
141
|
+
except Exception:
|
|
142
|
+
db.rollback()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## š Relations
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# Add relation
|
|
151
|
+
db.add_relation(
|
|
152
|
+
name = "user_orders",
|
|
153
|
+
from_table = "orders",
|
|
154
|
+
from_key = "user_id",
|
|
155
|
+
to_table = "users",
|
|
156
|
+
to_key = "id"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Get related records
|
|
160
|
+
related = db.get_related("user_orders", "user_1")
|
|
161
|
+
|
|
162
|
+
# Validate foreign key
|
|
163
|
+
db.validate_fk("user_orders", "user_1")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## š¾ Backup & Restore
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# Backup
|
|
172
|
+
db.backup("backup.hxbak")
|
|
173
|
+
|
|
174
|
+
# Restore
|
|
175
|
+
db.restore("backup.hxbak")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## š Info & Logs
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
# Database info
|
|
184
|
+
print(db.info())
|
|
185
|
+
|
|
186
|
+
# Cache stats
|
|
187
|
+
print(db.cache_stats())
|
|
188
|
+
|
|
189
|
+
# Read logs
|
|
190
|
+
logs = db.read_logs()
|
|
191
|
+
for log in logs:
|
|
192
|
+
print(log)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## š„ļø Context Manager
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
with HanifXDB("mydata.hxdb") as db:
|
|
201
|
+
db.create_table("users")
|
|
202
|
+
db.set("users", "name", "Sazzad")
|
|
203
|
+
# Auto close on exit
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## š File Format
|
|
209
|
+
|
|
210
|
+
HanifX DB uses a custom binary `.hxdb` format:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
HANIFXDB ā Magic header (8 bytes)
|
|
214
|
+
Version ā DB version (4 bytes)
|
|
215
|
+
Timestamps ā Created / Modified
|
|
216
|
+
Table sections ā HX_TBL__ + records
|
|
217
|
+
Records ā HX_REC__ + key + value + checksum
|
|
218
|
+
HX_END__ ā EOF signature
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## š Supported Data Types
|
|
224
|
+
|
|
225
|
+
| Type | Python | Example |
|
|
226
|
+
|----------|---------------|----------------------------|
|
|
227
|
+
| text | str | `"Sazzad"` |
|
|
228
|
+
| number | int / float | `20`, `3.14` |
|
|
229
|
+
| bool | bool | `True`, `False` |
|
|
230
|
+
| secret | str (encoded) | `"password123"` ā encoded |
|
|
231
|
+
| file | str (path) | `"photo.jpg"` ā encoded |
|
|
232
|
+
| list | list | `[1, 2, 3]` |
|
|
233
|
+
| dict | dict | `{"name": "Sazzad"}` |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## ā” Query Operators
|
|
238
|
+
|
|
239
|
+
| Operator | Meaning |
|
|
240
|
+
|------------|----------------------|
|
|
241
|
+
| `OP_EQ` | Equal |
|
|
242
|
+
| `OP_NEQ` | Not equal |
|
|
243
|
+
| `OP_GT` | Greater than |
|
|
244
|
+
| `OP_GTE` | Greater than or equal|
|
|
245
|
+
| `OP_LT` | Less than |
|
|
246
|
+
| `OP_LTE` | Less than or equal |
|
|
247
|
+
| `OP_IN` | Value in list |
|
|
248
|
+
| `OP_NOT_IN`| Value not in list |
|
|
249
|
+
| `OP_LIKE` | String contains |
|
|
250
|
+
| `OP_STARTS`| String starts with |
|
|
251
|
+
| `OP_ENDS` | String ends with |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## š± Termux Support
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Install on Termux (Android)
|
|
259
|
+
pip install hanifx-db
|
|
260
|
+
|
|
261
|
+
# Use immediately - no extra setup needed
|
|
262
|
+
python3 -c "from hanifxdb import HanifXDB; print('HanifX DB ready!')"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## šØāš» Author
|
|
268
|
+
|
|
269
|
+
**Hanif (HanifX)**
|
|
270
|
+
- PyPI: [habib_bi](https://pypi.org/user/habib_bi)
|
|
271
|
+
- Email: sajim4653@gmail.com
|
|
272
|
+
- Package: [hanifx-db](https://pypi.org/project/hanifx-db)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## š License
|
|
277
|
+
|
|
278
|
+
MIT License ā Free to use, modify, and distribute.
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hanifx-db
|
|
3
|
+
Version: 28.0.0
|
|
4
|
+
Summary: HanifX Database Engine ā Custom binary database with built-in HanifX security encoding
|
|
5
|
+
Home-page: https://pypi.org/project/hanifx-db
|
|
6
|
+
Author: Hanif
|
|
7
|
+
Author-email: sajim4653@gmail.com
|
|
8
|
+
Project-URL: Source, https://pypi.org/project/hanifx-db
|
|
9
|
+
Project-URL: Tracker, https://pypi.org/project/hanifx-db
|
|
10
|
+
Keywords: database,db,hanifx,security,encoding,encryption,custom-database,binary-format,hxdb,termux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Topic :: Database
|
|
23
|
+
Classifier: Topic :: Security :: Cryptography
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.6
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: project-url
|
|
37
|
+
Dynamic: requires-python
|
|
38
|
+
Dynamic: summary
|
|
39
|
+
|
|
40
|
+
# HanifX DB šļø
|
|
41
|
+
|
|
42
|
+
**HanifX Database Engine** ā A fully custom binary database built from scratch in pure Python with built-in HanifX security encoding.
|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/hanifx-db)
|
|
45
|
+
[](https://pypi.org/project/hanifx-db)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://pypi.org/project/hanifx-db)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ⨠Features
|
|
52
|
+
|
|
53
|
+
- š **Built-in HanifX Security** ā Irreversible encoding, no external dependency
|
|
54
|
+
- šļø **Multi-Table Support** ā Multiple tables in a single `.hxdb` file
|
|
55
|
+
- ā” **O(1) Index System** ā Custom hash table for fast lookups
|
|
56
|
+
- š **Transactions + WAL** ā Commit, rollback, crash recovery
|
|
57
|
+
- šļø **Custom Compression** ā Own RLE + LZ algorithm
|
|
58
|
+
- š **Relations** ā Foreign keys, ONE_TO_ONE, ONE_TO_MANY, MANY_TO_MANY
|
|
59
|
+
- š **Query Builder** ā Fluent query API with 11 operators
|
|
60
|
+
- š¾ **LRU Cache** ā Fast repeated reads
|
|
61
|
+
- š **Operation Logging** ā Every operation logged to `.hxlog`
|
|
62
|
+
- š¦ **Pure Python** ā No external dependencies, works on Termux
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## š¦ Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install hanifx-db
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## š Quick Start
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from hanifxdb import HanifXDB
|
|
78
|
+
|
|
79
|
+
# Open / Create database
|
|
80
|
+
db = HanifXDB("mydata.hxdb")
|
|
81
|
+
|
|
82
|
+
# Create table
|
|
83
|
+
db.create_table("users")
|
|
84
|
+
|
|
85
|
+
# Set plain data
|
|
86
|
+
db.set("users", "name", "Sazzad")
|
|
87
|
+
db.set("users", "age", 20)
|
|
88
|
+
db.set("users", "is_admin", True)
|
|
89
|
+
|
|
90
|
+
# Set secret data (irreversible HanifX encoding)
|
|
91
|
+
db.set_secret("users", "password", "mypass123")
|
|
92
|
+
|
|
93
|
+
# Get data
|
|
94
|
+
print(db.get("users", "name")) # Sazzad
|
|
95
|
+
print(db.get("users", "age")) # 20
|
|
96
|
+
|
|
97
|
+
# Verify secret
|
|
98
|
+
if db.verify("users", "password", "mypass123"):
|
|
99
|
+
print("Login success!")
|
|
100
|
+
|
|
101
|
+
# Close
|
|
102
|
+
db.close()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## š Security
|
|
108
|
+
|
|
109
|
+
HanifX DB uses the built-in **HanifX 24.0.0** encoding algorithm:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# Store secret (irreversible)
|
|
113
|
+
db.set_secret("users", "token", "mytoken123")
|
|
114
|
+
|
|
115
|
+
# Verify (checksum-based)
|
|
116
|
+
db.verify("users", "token", "mytoken123") # True
|
|
117
|
+
db.verify("users", "token", "wrongtoken") # HanifXVerifyError
|
|
118
|
+
|
|
119
|
+
# Encode file
|
|
120
|
+
db.set_file("users", "photo", "profile.jpg")
|
|
121
|
+
|
|
122
|
+
# Generate secure token
|
|
123
|
+
token = db.generate_token(32)
|
|
124
|
+
uid = db.generate_id()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## šļø Multi-Table
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
db = HanifXDB("shop.hxdb")
|
|
133
|
+
|
|
134
|
+
db.create_table("users")
|
|
135
|
+
db.create_table("orders")
|
|
136
|
+
db.create_table("products")
|
|
137
|
+
|
|
138
|
+
db.set("users", "user_1", {"name": "Sazzad", "age": 20})
|
|
139
|
+
db.set("products", "prod_1", {"name": "Phone", "price": 500})
|
|
140
|
+
db.set("orders", "ord_1", {"user": "user_1", "product": "prod_1"})
|
|
141
|
+
|
|
142
|
+
print(db.list_tables()) # ['users', 'orders', 'products']
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## š Query Builder
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from hanifxdb import OP_GTE, OP_LIKE
|
|
151
|
+
|
|
152
|
+
# Fluent query
|
|
153
|
+
results = (
|
|
154
|
+
db.query("users")
|
|
155
|
+
.where("age", OP_GTE, 18)
|
|
156
|
+
.where("name", OP_LIKE, "saz")
|
|
157
|
+
.order_by("age")
|
|
158
|
+
.limit(10)
|
|
159
|
+
.execute()
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Shortcuts
|
|
163
|
+
db.find_all("users")
|
|
164
|
+
db.find_one("users", "name", "Sazzad")
|
|
165
|
+
db.find_like("users", "name", "saz")
|
|
166
|
+
db.count("users")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## š Transactions
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
db.begin()
|
|
175
|
+
try:
|
|
176
|
+
db.set("users", "name", "Sazzad")
|
|
177
|
+
db.set("users", "age", 20)
|
|
178
|
+
db.set_secret("users", "password", "pass123")
|
|
179
|
+
db.commit()
|
|
180
|
+
except Exception:
|
|
181
|
+
db.rollback()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## š Relations
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# Add relation
|
|
190
|
+
db.add_relation(
|
|
191
|
+
name = "user_orders",
|
|
192
|
+
from_table = "orders",
|
|
193
|
+
from_key = "user_id",
|
|
194
|
+
to_table = "users",
|
|
195
|
+
to_key = "id"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Get related records
|
|
199
|
+
related = db.get_related("user_orders", "user_1")
|
|
200
|
+
|
|
201
|
+
# Validate foreign key
|
|
202
|
+
db.validate_fk("user_orders", "user_1")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## š¾ Backup & Restore
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Backup
|
|
211
|
+
db.backup("backup.hxbak")
|
|
212
|
+
|
|
213
|
+
# Restore
|
|
214
|
+
db.restore("backup.hxbak")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## š Info & Logs
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# Database info
|
|
223
|
+
print(db.info())
|
|
224
|
+
|
|
225
|
+
# Cache stats
|
|
226
|
+
print(db.cache_stats())
|
|
227
|
+
|
|
228
|
+
# Read logs
|
|
229
|
+
logs = db.read_logs()
|
|
230
|
+
for log in logs:
|
|
231
|
+
print(log)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## š„ļø Context Manager
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
with HanifXDB("mydata.hxdb") as db:
|
|
240
|
+
db.create_table("users")
|
|
241
|
+
db.set("users", "name", "Sazzad")
|
|
242
|
+
# Auto close on exit
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## š File Format
|
|
248
|
+
|
|
249
|
+
HanifX DB uses a custom binary `.hxdb` format:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
HANIFXDB ā Magic header (8 bytes)
|
|
253
|
+
Version ā DB version (4 bytes)
|
|
254
|
+
Timestamps ā Created / Modified
|
|
255
|
+
Table sections ā HX_TBL__ + records
|
|
256
|
+
Records ā HX_REC__ + key + value + checksum
|
|
257
|
+
HX_END__ ā EOF signature
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## š Supported Data Types
|
|
263
|
+
|
|
264
|
+
| Type | Python | Example |
|
|
265
|
+
|----------|---------------|----------------------------|
|
|
266
|
+
| text | str | `"Sazzad"` |
|
|
267
|
+
| number | int / float | `20`, `3.14` |
|
|
268
|
+
| bool | bool | `True`, `False` |
|
|
269
|
+
| secret | str (encoded) | `"password123"` ā encoded |
|
|
270
|
+
| file | str (path) | `"photo.jpg"` ā encoded |
|
|
271
|
+
| list | list | `[1, 2, 3]` |
|
|
272
|
+
| dict | dict | `{"name": "Sazzad"}` |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## ā” Query Operators
|
|
277
|
+
|
|
278
|
+
| Operator | Meaning |
|
|
279
|
+
|------------|----------------------|
|
|
280
|
+
| `OP_EQ` | Equal |
|
|
281
|
+
| `OP_NEQ` | Not equal |
|
|
282
|
+
| `OP_GT` | Greater than |
|
|
283
|
+
| `OP_GTE` | Greater than or equal|
|
|
284
|
+
| `OP_LT` | Less than |
|
|
285
|
+
| `OP_LTE` | Less than or equal |
|
|
286
|
+
| `OP_IN` | Value in list |
|
|
287
|
+
| `OP_NOT_IN`| Value not in list |
|
|
288
|
+
| `OP_LIKE` | String contains |
|
|
289
|
+
| `OP_STARTS`| String starts with |
|
|
290
|
+
| `OP_ENDS` | String ends with |
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## š± Termux Support
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Install on Termux (Android)
|
|
298
|
+
pip install hanifx-db
|
|
299
|
+
|
|
300
|
+
# Use immediately - no extra setup needed
|
|
301
|
+
python3 -c "from hanifxdb import HanifXDB; print('HanifX DB ready!')"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## šØāš» Author
|
|
307
|
+
|
|
308
|
+
**Hanif (HanifX)**
|
|
309
|
+
- PyPI: [habib_bi](https://pypi.org/user/habib_bi)
|
|
310
|
+
- Email: sajim4653@gmail.com
|
|
311
|
+
- Package: [hanifx-db](https://pypi.org/project/hanifx-db)
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## š License
|
|
316
|
+
|
|
317
|
+
MIT License ā Free to use, modify, and distribute.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tests
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# HanifX DB ā setup.py
|
|
3
|
+
# Author : Hanif (HanifX)
|
|
4
|
+
# PyPI : hanifx-db
|
|
5
|
+
# ============================================================
|
|
6
|
+
|
|
7
|
+
from setuptools import setup, find_packages
|
|
8
|
+
|
|
9
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
10
|
+
long_description = f.read()
|
|
11
|
+
|
|
12
|
+
setup(
|
|
13
|
+
name = "hanifx-db",
|
|
14
|
+
version = "28.0.0",
|
|
15
|
+
author = "Hanif",
|
|
16
|
+
author_email = "sajim4653@gmail.com",
|
|
17
|
+
description = "HanifX Database Engine ā Custom binary database with built-in HanifX security encoding",
|
|
18
|
+
long_description = long_description,
|
|
19
|
+
long_description_content_type = "text/markdown",
|
|
20
|
+
url = "https://pypi.org/project/hanifx-db",
|
|
21
|
+
packages = find_packages(),
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.6",
|
|
25
|
+
"Programming Language :: Python :: 3.7",
|
|
26
|
+
"Programming Language :: Python :: 3.8",
|
|
27
|
+
"Programming Language :: Python :: 3.9",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"License :: OSI Approved :: MIT License",
|
|
33
|
+
"Operating System :: OS Independent",
|
|
34
|
+
"Topic :: Database",
|
|
35
|
+
"Topic :: Security :: Cryptography",
|
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
+
],
|
|
38
|
+
python_requires = ">=3.6",
|
|
39
|
+
install_requires = [], # No external dependencies!
|
|
40
|
+
keywords = [
|
|
41
|
+
"database", "db", "hanifx", "security",
|
|
42
|
+
"encoding", "encryption", "custom-database",
|
|
43
|
+
"binary-format", "hxdb", "termux"
|
|
44
|
+
],
|
|
45
|
+
project_urls = {
|
|
46
|
+
"Source" : "https://pypi.org/project/hanifx-db",
|
|
47
|
+
"Tracker" : "https://pypi.org/project/hanifx-db",
|
|
48
|
+
},
|
|
49
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# HanifX DB ā Full Test Suite
|
|
3
|
+
# Author : Hanif (HanifX)
|
|
4
|
+
# Version : 27.0.0
|
|
5
|
+
# ============================================================
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
11
|
+
|
|
12
|
+
from hanifxdb import (
|
|
13
|
+
HanifXDB,
|
|
14
|
+
HanifXKeyError,
|
|
15
|
+
HanifXTableExistsError,
|
|
16
|
+
HanifXTableNotFoundError,
|
|
17
|
+
HanifXVerifyError,
|
|
18
|
+
HanifXTransactionError,
|
|
19
|
+
OP_EQ, OP_GT, OP_LIKE, OP_IN, OP_GTE,
|
|
20
|
+
REL_ONE_TO_MANY,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# āā Test Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
24
|
+
|
|
25
|
+
PASS = "ā
PASS"
|
|
26
|
+
FAIL = "ā FAIL"
|
|
27
|
+
DB_PATH = "test_hanifx.hxdb"
|
|
28
|
+
|
|
29
|
+
total = 0
|
|
30
|
+
passed = 0
|
|
31
|
+
failed = 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test(name: str, fn):
|
|
35
|
+
global total, passed, failed
|
|
36
|
+
total += 1
|
|
37
|
+
try:
|
|
38
|
+
fn()
|
|
39
|
+
print(f" {PASS} ā {name}")
|
|
40
|
+
passed += 1
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(f" {FAIL} ā {name}")
|
|
43
|
+
print(f" Error: {e}")
|
|
44
|
+
failed += 1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def cleanup():
|
|
48
|
+
for ext in (".hxdb", ".hxwal", ".hxlog", ".hxbak"):
|
|
49
|
+
path = DB_PATH.replace(".hxdb", ext)
|
|
50
|
+
if os.path.exists(path):
|
|
51
|
+
os.remove(path)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# āā Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
55
|
+
|
|
56
|
+
def run_tests():
|
|
57
|
+
global passed, failed, total
|
|
58
|
+
cleanup()
|
|
59
|
+
db = HanifXDB(DB_PATH, logging=True)
|
|
60
|
+
|
|
61
|
+
print("\n" + "=" * 50)
|
|
62
|
+
print(" HanifX DB ā Test Suite v27.0.0")
|
|
63
|
+
print("=" * 50)
|
|
64
|
+
|
|
65
|
+
# āā 1. Table Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
66
|
+
print("\nš Table Tests")
|
|
67
|
+
|
|
68
|
+
test("Create table", lambda: db.create_table("users"))
|
|
69
|
+
test("Create second table", lambda: db.create_table("orders"))
|
|
70
|
+
|
|
71
|
+
def test_duplicate_table():
|
|
72
|
+
try:
|
|
73
|
+
db.create_table("users")
|
|
74
|
+
assert False, "Should have raised"
|
|
75
|
+
except HanifXTableExistsError:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
test("Duplicate table raises error", test_duplicate_table)
|
|
79
|
+
test("List tables", lambda: assert_eq(sorted(db.list_tables()), ["orders", "users"]))
|
|
80
|
+
test("Table exists", lambda: assert_true(db.table_exists("users")))
|
|
81
|
+
test("Table not exists", lambda: assert_false(db.table_exists("ghost")))
|
|
82
|
+
|
|
83
|
+
# āā 2. CRUD Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
84
|
+
print("\nš CRUD Tests")
|
|
85
|
+
|
|
86
|
+
test("Set string", lambda: db.set("users", "name", "Sazzad"))
|
|
87
|
+
test("Set integer", lambda: db.set("users", "age", 20))
|
|
88
|
+
test("Set float", lambda: db.set("users", "score", 9.5))
|
|
89
|
+
test("Set bool", lambda: db.set("users", "is_admin", True))
|
|
90
|
+
test("Set list", lambda: db.set("users", "tags", ["python", "dev"]))
|
|
91
|
+
test("Set dict", lambda: db.set("users", "info", {"city": "Chittagong", "country": "BD"}))
|
|
92
|
+
|
|
93
|
+
test("Get string", lambda: assert_eq(db.get("users", "name"), "Sazzad"))
|
|
94
|
+
test("Get integer", lambda: assert_eq(db.get("users", "age"), 20))
|
|
95
|
+
test("Get float", lambda: assert_eq(db.get("users", "score"), 9.5))
|
|
96
|
+
test("Get bool", lambda: assert_true(db.get("users", "is_admin")))
|
|
97
|
+
test("Get list", lambda: assert_eq(db.get("users", "tags"), ["python", "dev"]))
|
|
98
|
+
test("Get dict", lambda: assert_eq(db.get("users", "info")["city"], "Chittagong"))
|
|
99
|
+
|
|
100
|
+
def test_key_not_found():
|
|
101
|
+
try:
|
|
102
|
+
db.get("users", "ghost_key")
|
|
103
|
+
assert False, "Should have raised"
|
|
104
|
+
except HanifXKeyError:
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
test("Get missing key raises error", test_key_not_found)
|
|
108
|
+
|
|
109
|
+
test("Exists true", lambda: assert_true(db.exists("users", "name")))
|
|
110
|
+
test("Exists false", lambda: assert_false(db.exists("users", "ghost")))
|
|
111
|
+
|
|
112
|
+
test("Update value", lambda: (db.update("users", "age", 21), assert_eq(db.get("users", "age"), 21)))
|
|
113
|
+
|
|
114
|
+
test("List keys", lambda: assert_true("name" in db.list_keys("users")))
|
|
115
|
+
|
|
116
|
+
test("Delete key", lambda: db.delete("users", "tags"))
|
|
117
|
+
|
|
118
|
+
def test_deleted_key():
|
|
119
|
+
try:
|
|
120
|
+
db.get("users", "tags")
|
|
121
|
+
assert False, "Should have raised"
|
|
122
|
+
except HanifXKeyError:
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
test("Get deleted key raises error", test_deleted_key)
|
|
126
|
+
|
|
127
|
+
# āā 3. Secret Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
128
|
+
print("\nš Secret Tests")
|
|
129
|
+
|
|
130
|
+
test("Set secret", lambda: db.set_secret("users", "password", "mypass123"))
|
|
131
|
+
test("Secret stored encoded", lambda: assert_true(
|
|
132
|
+
isinstance(db.get("users", "password"), dict)
|
|
133
|
+
))
|
|
134
|
+
test("Verify correct password", lambda: assert_true(
|
|
135
|
+
db.verify("users", "password", "mypass123")
|
|
136
|
+
))
|
|
137
|
+
|
|
138
|
+
def test_wrong_password():
|
|
139
|
+
try:
|
|
140
|
+
db.verify("users", "password", "wrongpass")
|
|
141
|
+
assert False, "Should have raised"
|
|
142
|
+
except HanifXVerifyError:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
test("Verify wrong password raises error", test_wrong_password)
|
|
146
|
+
test("List secrets", lambda: assert_true("password" in db.list_secrets("users")))
|
|
147
|
+
|
|
148
|
+
# āā 4. Security Utility Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
149
|
+
print("\nš”ļø Security Utility Tests")
|
|
150
|
+
|
|
151
|
+
test("Generate token", lambda: assert_true(len(db.generate_token()) == 64))
|
|
152
|
+
test("Generate ID", lambda: assert_true(len(db.generate_id()) == 32))
|
|
153
|
+
|
|
154
|
+
# āā 5. Query Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
155
|
+
print("\nš Query Tests")
|
|
156
|
+
|
|
157
|
+
db.set("users", "user2", {"name": "Hanif", "age": 25, "city": "Dhaka"})
|
|
158
|
+
db.set("users", "user3", {"name": "Samia", "age": 18, "city": "Chittagong"})
|
|
159
|
+
|
|
160
|
+
test("Find all", lambda: assert_true(len(db.find_all("users")) > 0))
|
|
161
|
+
|
|
162
|
+
test("Query with OP_GT", lambda: assert_true(
|
|
163
|
+
len(db.query("users").where("age", OP_GT, 18).execute()) > 0
|
|
164
|
+
))
|
|
165
|
+
|
|
166
|
+
test("Query with OP_LIKE", lambda: assert_true(
|
|
167
|
+
len(db.query("users").where("name", OP_LIKE, "han").execute()) > 0
|
|
168
|
+
))
|
|
169
|
+
|
|
170
|
+
test("Query with OP_EQ", lambda: assert_true(
|
|
171
|
+
len(db.query("users").where("city", OP_EQ, "Dhaka").execute()) > 0
|
|
172
|
+
))
|
|
173
|
+
|
|
174
|
+
test("Query limit", lambda: assert_true(
|
|
175
|
+
len(db.query("users").limit(1).execute()) == 1
|
|
176
|
+
))
|
|
177
|
+
|
|
178
|
+
test("Query count", lambda: assert_true(
|
|
179
|
+
db.query("users").count() > 0
|
|
180
|
+
))
|
|
181
|
+
|
|
182
|
+
test("Query first", lambda: assert_true(
|
|
183
|
+
db.query("users").first() is not None
|
|
184
|
+
))
|
|
185
|
+
|
|
186
|
+
test("find_one", lambda: assert_true(
|
|
187
|
+
db.find_one("users", "name", "Sazzad") is not None
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
test("find_like", lambda: assert_true(
|
|
191
|
+
len(db.find_like("users", "city", "chit")) > 0
|
|
192
|
+
))
|
|
193
|
+
|
|
194
|
+
test("count", lambda: assert_true(db.count("users") > 0))
|
|
195
|
+
|
|
196
|
+
# āā 6. Transaction Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
197
|
+
print("\nš Transaction Tests")
|
|
198
|
+
|
|
199
|
+
def test_commit():
|
|
200
|
+
db.begin()
|
|
201
|
+
db.set("orders", "order_1", {"item": "Phone", "price": 500})
|
|
202
|
+
db.set("orders", "order_2", {"item": "Laptop", "price": 1000})
|
|
203
|
+
db.commit()
|
|
204
|
+
assert db.exists("orders", "order_1")
|
|
205
|
+
assert db.exists("orders", "order_2")
|
|
206
|
+
|
|
207
|
+
test("Transaction commit", test_commit)
|
|
208
|
+
|
|
209
|
+
def test_rollback():
|
|
210
|
+
db.begin()
|
|
211
|
+
db.set("orders", "order_temp", {"item": "Tablet", "price": 300})
|
|
212
|
+
db.rollback()
|
|
213
|
+
assert not db.exists("orders", "order_temp")
|
|
214
|
+
|
|
215
|
+
test("Transaction rollback", test_rollback)
|
|
216
|
+
|
|
217
|
+
def test_no_tx():
|
|
218
|
+
try:
|
|
219
|
+
db.commit()
|
|
220
|
+
assert False, "Should have raised"
|
|
221
|
+
except HanifXTransactionError:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
test("Commit without begin raises error", test_no_tx)
|
|
225
|
+
|
|
226
|
+
# āā 7. Relation Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
227
|
+
print("\nš Relation Tests")
|
|
228
|
+
|
|
229
|
+
def test_add_relation():
|
|
230
|
+
db.add_relation(
|
|
231
|
+
name = "user_orders",
|
|
232
|
+
from_table = "orders",
|
|
233
|
+
from_key = "user_id",
|
|
234
|
+
to_table = "users",
|
|
235
|
+
to_key = "id"
|
|
236
|
+
)
|
|
237
|
+
assert "user_orders" in db.list_relations()
|
|
238
|
+
|
|
239
|
+
test("Add relation", test_add_relation)
|
|
240
|
+
test("List relations", lambda: assert_true(len(db.list_relations()) > 0))
|
|
241
|
+
|
|
242
|
+
# āā 8. Index Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
243
|
+
print("\nā” Index Tests")
|
|
244
|
+
|
|
245
|
+
test("Create index", lambda: db.create_index("users", "name"))
|
|
246
|
+
test("Drop index", lambda: db.drop_index("users", "name"))
|
|
247
|
+
|
|
248
|
+
# āā 9. Cache Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
249
|
+
print("\nš¾ Cache Tests")
|
|
250
|
+
|
|
251
|
+
test("Cache stats", lambda: assert_true(
|
|
252
|
+
isinstance(db.cache_stats(), dict)
|
|
253
|
+
))
|
|
254
|
+
test("Clear cache", lambda: db.clear_cache())
|
|
255
|
+
|
|
256
|
+
# āā 10. Table Info Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
257
|
+
print("\nš Info Tests")
|
|
258
|
+
|
|
259
|
+
test("Table info", lambda: assert_true(
|
|
260
|
+
isinstance(db.table_info("users"), dict)
|
|
261
|
+
))
|
|
262
|
+
test("DB info", lambda: assert_true(
|
|
263
|
+
isinstance(db.info(), dict)
|
|
264
|
+
))
|
|
265
|
+
test("DB size", lambda: assert_true(
|
|
266
|
+
db.info()["size_bytes"] > 0
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
# āā 11. Backup Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
270
|
+
print("\nšæ Backup Tests")
|
|
271
|
+
|
|
272
|
+
test("Backup DB", lambda: db.backup("test_backup.hxbak"))
|
|
273
|
+
test("Backup file exists", lambda: assert_true(
|
|
274
|
+
os.path.exists("test_backup.hxbak")
|
|
275
|
+
))
|
|
276
|
+
|
|
277
|
+
# āā 12. Log Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
278
|
+
print("\nš Log Tests")
|
|
279
|
+
|
|
280
|
+
test("Read logs", lambda: assert_true(
|
|
281
|
+
isinstance(db.read_logs(), list)
|
|
282
|
+
))
|
|
283
|
+
|
|
284
|
+
# āā 13. Drop Table Tests āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
285
|
+
print("\nšļø Drop Table Tests")
|
|
286
|
+
|
|
287
|
+
db.create_table("temp_table")
|
|
288
|
+
test("Drop table", lambda: db.drop_table("temp_table"))
|
|
289
|
+
test("Dropped table not exists", lambda: assert_false(
|
|
290
|
+
db.table_exists("temp_table")
|
|
291
|
+
))
|
|
292
|
+
|
|
293
|
+
def test_drop_missing():
|
|
294
|
+
try:
|
|
295
|
+
db.drop_table("ghost_table")
|
|
296
|
+
assert False, "Should have raised"
|
|
297
|
+
except HanifXTableNotFoundError:
|
|
298
|
+
pass
|
|
299
|
+
|
|
300
|
+
test("Drop missing table raises error", test_drop_missing)
|
|
301
|
+
|
|
302
|
+
# āā Close āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
303
|
+
db.close()
|
|
304
|
+
test("Close DB", lambda: assert_true(db._closed))
|
|
305
|
+
|
|
306
|
+
# āā Summary āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
307
|
+
print("\n" + "=" * 50)
|
|
308
|
+
print(f" Total : {total}")
|
|
309
|
+
print(f" Passed : {passed} ā
")
|
|
310
|
+
print(f" Failed : {failed} ā")
|
|
311
|
+
print("=" * 50)
|
|
312
|
+
|
|
313
|
+
if failed == 0:
|
|
314
|
+
print("\nš All tests passed! HanifX DB is ready!")
|
|
315
|
+
else:
|
|
316
|
+
print(f"\nā ļø {failed} test(s) failed. Please check above.")
|
|
317
|
+
|
|
318
|
+
# Cleanup
|
|
319
|
+
cleanup()
|
|
320
|
+
for f in ("test_backup.hxbak",):
|
|
321
|
+
if os.path.exists(f):
|
|
322
|
+
os.remove(f)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# āā Assert Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
326
|
+
|
|
327
|
+
def assert_eq(a, b):
|
|
328
|
+
assert a == b, f"Expected {b!r}, got {a!r}"
|
|
329
|
+
|
|
330
|
+
def assert_true(val):
|
|
331
|
+
assert val, f"Expected True, got {val!r}"
|
|
332
|
+
|
|
333
|
+
def assert_false(val):
|
|
334
|
+
assert not val, f"Expected False, got {val!r}"
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# āā Run āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
338
|
+
|
|
339
|
+
if __name__ == "__main__":
|
|
340
|
+
run_tests()
|