@wowoengine/sawitdb 2.4.0
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.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/bin/sawit-server.js +68 -0
- package/cli/local.js +49 -0
- package/cli/remote.js +165 -0
- package/docs/index.html +727 -0
- package/docs/sawitdb.jpg +0 -0
- package/package.json +63 -0
- package/src/SawitClient.js +265 -0
- package/src/SawitServer.js +602 -0
- package/src/WowoEngine.js +539 -0
- package/src/modules/BTreeIndex.js +282 -0
- package/src/modules/Pager.js +70 -0
- package/src/modules/QueryParser.js +569 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 SawitDB Community
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# SawitDB
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
|
|
7
|
+
[](https://wowoengine.github.io/SawitDB/)
|
|
8
|
+
[](https://github.com/WowoEngine/SawitDB-Go)
|
|
9
|
+
[](CHANGELOG.md)
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
**SawitDB** is a unique database solution stored in `.sawit` binary files.
|
|
15
|
+
|
|
16
|
+
The system features a custom **Paged Heap File** architecture similar to SQLite, using fixed-size 4KB pages to ensure efficient memory usage. What differentiates SawitDB is its unique **Agricultural Query Language (AQL)**, which replaces standard SQL keywords with Indonesian farming terminology.
|
|
17
|
+
|
|
18
|
+
**Now with Network Edition!** Connect via TCP using `sawitdb://` protocol similar to MongoDB.
|
|
19
|
+
|
|
20
|
+
**🚨 Emergency: Aceh Flood Relief**
|
|
21
|
+
Please support our brothers and sisters in Aceh.
|
|
22
|
+
|
|
23
|
+
[](https://kitabisa.com/campaign/donasipedulibanjiraceh)
|
|
24
|
+
|
|
25
|
+
*Organized by Human Initiative Aceh*
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- **Paged Architecture**: Data is stored in 4096-byte binary pages. The engine does not load the entire database into memory.
|
|
30
|
+
- **Single File Storage**: All data, schema, and indexes are stored in a single `.sawit` file.
|
|
31
|
+
- **High Stability**: Uses 4KB atomic pages. More stable than a coalition government.
|
|
32
|
+
- **Data Integrity (Anti-Korupsi)**: Implements strict `fsync` protocols. Data cannot be "corrupted" or "disappear" mysteriously like social aid funds (Bansos). No "Sunat Massal" here.
|
|
33
|
+
- **Zero Bureaucracy (Zero Deps)**: Built entirely with standard Node.js. No unnecessary "Vendor Pengadaan" or "Mark-up Anggaran".
|
|
34
|
+
- **Transparansi**: Query language is clear. No "Pasal Karet" (Ambiguous Laws) or "Rapat Tertutup" in 5-star hotels.
|
|
35
|
+
- **Speed**: Faster than printing an e-KTP at the Kelurahan.
|
|
36
|
+
- **Network Support (NEW)**: Client-Server architecture with Multi-database support and Authentication.
|
|
37
|
+
|
|
38
|
+
## Filosofi
|
|
39
|
+
|
|
40
|
+
### Filosofi (ID)
|
|
41
|
+
SawitDB dibangun dengan semangat "Kemandirian Data". Kami percaya database yang handal tidak butuh **Infrastruktur Langit** yang harganya triliunan tapi sering *down*. Berbeda dengan proyek negara yang mahal di *budget* tapi murah di kualitas, SawitDB menggunakan arsitektur **Single File** (`.sawit`) yang hemat biaya. Backup cukup *copy-paste*, tidak perlu sewa vendor konsultan asing. Fitur **`fsync`** kami menjamin data tertulis di *disk*, karena bagi kami, integritas data adalah harga mati, bukan sekadar bahan konferensi pers untuk minta maaf.
|
|
42
|
+
|
|
43
|
+
### Philosophy (EN)
|
|
44
|
+
SawitDB is built with the spirit of "Data Sovereignty". We believe a reliable database doesn't need **"Sky Infrastructure"** that costs trillions yet goes *down* often. Unlike state projects that are expensive in budget but cheap in quality, SawitDB uses a cost-effective **Single File** (`.sawit`) architecture. Backup is just *copy-paste*, no need to hire expensive foreign consultants. Our **`fsync`** feature guarantees data is written to *disk*, because for us, data integrity is non-negotiable, not just material for a press conference to apologize.
|
|
45
|
+
|
|
46
|
+
## File List
|
|
47
|
+
|
|
48
|
+
- `src/WowoEngine.js`: Core Database Engine (Class: `SawitDB`).
|
|
49
|
+
- `bin/sawit-server.js`: Server executable.
|
|
50
|
+
- `cli/local.js`: Interactive CLI tool (Local).
|
|
51
|
+
- `cli/remote.js`: Interactive CLI tool (Network).
|
|
52
|
+
- [CHANGELOG.md](CHANGELOG.md): Version history and release notes.
|
|
53
|
+
- `examples/`: Sample scripts.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
Ensure you have Node.js installed. Clone the repository.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Clone
|
|
61
|
+
git clone https://github.com/WowoEngine/SawitDB.git
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start (Network Edition)
|
|
65
|
+
|
|
66
|
+
### 1. Start the Server
|
|
67
|
+
```bash
|
|
68
|
+
node src/SawitServer.js
|
|
69
|
+
```
|
|
70
|
+
The server will start on `0.0.0.0:7878` by default.
|
|
71
|
+
|
|
72
|
+
### 2. Connect with Client
|
|
73
|
+
Use [SawitClient](#client-api) or any interactive session.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Dual Syntax Support
|
|
78
|
+
|
|
79
|
+
SawitDB 2.3 introduces the **Generic Syntax** alongside the classic **Agricultural Query Language (AQL)**, making it easier for developers familiar with standard SQL to adopt.
|
|
80
|
+
|
|
81
|
+
| Operation | Agricultural Query Language (AQL) | Generic SQL (Standard) |
|
|
82
|
+
| :--- | :--- | :--- |
|
|
83
|
+
| **Create DB** | `BUKA WILAYAH sales_db` | `CREATE DATABASE sales_db` |
|
|
84
|
+
| **Use DB** | `MASUK WILAYAH sales_db` | `USE sales_db` |
|
|
85
|
+
| **Show DBs** | `LIHAT WILAYAH` | `SHOW DATABASES` |
|
|
86
|
+
| **Drop DB** | `BAKAR WILAYAH sales_db` | `DROP DATABASE sales_db` |
|
|
87
|
+
| **Create Table** | `LAHAN products` | `CREATE TABLE products` |
|
|
88
|
+
| **Insert** | `TANAM KE products (...) BIBIT (...)` | `INSERT INTO products (...) VALUES (...)` |
|
|
89
|
+
| **Select** | `PANEN * DARI products DIMANA ...` | `SELECT * FROM products WHERE ...` |
|
|
90
|
+
| **Update** | `PUPUK products DENGAN ...` | `UPDATE products SET ...` |
|
|
91
|
+
| **Delete** | `GUSUR DARI products DIMANA ...` | `DELETE FROM products WHERE ...` |
|
|
92
|
+
| **Indexing** | `INDEKS products PADA price` | `CREATE INDEX ON products (price)` |
|
|
93
|
+
| **Aggregation** | `HITUNG SUM(stock) DARI products` | *Same Syntax* |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Query Syntax (Detailed)
|
|
98
|
+
|
|
99
|
+
### 1. Management Commands
|
|
100
|
+
|
|
101
|
+
#### Create Table
|
|
102
|
+
```sql
|
|
103
|
+
-- Tani
|
|
104
|
+
LAHAN users
|
|
105
|
+
-- Generic
|
|
106
|
+
CREATE TABLE users
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Show Tables
|
|
110
|
+
```sql
|
|
111
|
+
-- Tani
|
|
112
|
+
LIHAT LAHAN
|
|
113
|
+
-- Generic
|
|
114
|
+
SHOW TABLES
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Drop Table
|
|
118
|
+
```sql
|
|
119
|
+
-- Tani
|
|
120
|
+
BAKAR LAHAN users
|
|
121
|
+
-- Generic
|
|
122
|
+
DROP TABLE users
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 2. Data Manipulation
|
|
126
|
+
|
|
127
|
+
#### Insert Data
|
|
128
|
+
```sql
|
|
129
|
+
-- Tani
|
|
130
|
+
TANAM KE users (name, role) BIBIT ('Alice', 'Admin')
|
|
131
|
+
-- Generic
|
|
132
|
+
INSERT INTO users (name, role) VALUES ('Alice', 'Admin')
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Select Data
|
|
136
|
+
```sql
|
|
137
|
+
-- Tani
|
|
138
|
+
PANEN name, role DARI users DIMANA role = 'Admin' ORDER BY name ASC LIMIT 10
|
|
139
|
+
-- Generic
|
|
140
|
+
SELECT name, role FROM users WHERE role = 'Admin' ORDER BY name ASC LIMIT 10
|
|
141
|
+
```
|
|
142
|
+
*Operators*: `=`, `!=`, `>`, `<`, `>=`, `<=`
|
|
143
|
+
*Advanced*: `IN ('a','b')`, `LIKE 'pat%'`, `BETWEEN 10 AND 20`, `IS NULL`, `IS NOT NULL`
|
|
144
|
+
|
|
145
|
+
#### Pagination & Sorting
|
|
146
|
+
```sql
|
|
147
|
+
SELECT * FROM users ORDER BY age DESC LIMIT 5 OFFSET 10
|
|
148
|
+
SELECT * FROM users WHERE age BETWEEN 18 AND 30 AND status IS NOT NULL
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Update Data
|
|
152
|
+
```sql
|
|
153
|
+
-- Tani
|
|
154
|
+
PUPUK users DENGAN role='SuperAdmin' DIMANA name='Alice'
|
|
155
|
+
-- Generic
|
|
156
|
+
UPDATE users SET role='SuperAdmin' WHERE name='Alice'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Delete Data
|
|
160
|
+
```sql
|
|
161
|
+
-- Tani
|
|
162
|
+
GUSUR DARI users DIMANA name='Bob'
|
|
163
|
+
-- Generic
|
|
164
|
+
DELETE FROM users WHERE name='Bob'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 3. Advanced Features
|
|
168
|
+
|
|
169
|
+
#### Indexing
|
|
170
|
+
```sql
|
|
171
|
+
INDEKS [table] PADA [field]
|
|
172
|
+
-- or
|
|
173
|
+
CREATE INDEX ON [table] ([field])
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Aggregation & Grouping
|
|
177
|
+
```sql
|
|
178
|
+
HITUNG COUNT(*) DARI [table]
|
|
179
|
+
HITUNG AVG(price) DARI [products] KELOMPOK [category]
|
|
180
|
+
-- Generic Keyword Alias
|
|
181
|
+
SELECT AVG(price) FROM [products] GROUP BY [category] (Coming Soon)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Architecture Details
|
|
185
|
+
|
|
186
|
+
- **Modular Codebase**: Engine logic separated into `src/modules/` (`Pager.js`, `QueryParser.js`, `BTreeIndex.js`) for better maintainability.
|
|
187
|
+
- **Page 0 (Master Page)**: Contains header and Table Directory.
|
|
188
|
+
- **Data & Indexes**: Stored in 4KB atomic pages.
|
|
189
|
+
|
|
190
|
+
## Benchmark Performance
|
|
191
|
+
Test Environment: Single Thread, Windows Node.js (Local NVMe)
|
|
192
|
+
|
|
193
|
+
| Operation | Ops/Sec | Latency (avg) |
|
|
194
|
+
|-----------|---------|---------------|
|
|
195
|
+
| **INSERT** | ~3,125 | 0.32 ms |
|
|
196
|
+
| **SELECT (PK Index)** | ~3,846 | 0.26 ms |
|
|
197
|
+
| **SELECT (Scan)** | ~4,762 | 0.21 ms |
|
|
198
|
+
| **UPDATE** | ~3,571 | 0.28 ms |
|
|
199
|
+
|
|
200
|
+
*Note: Hasil dapat bervariasi tergantung hardware.*
|
|
201
|
+
|
|
202
|
+
## Full Feature Comparison
|
|
203
|
+
|
|
204
|
+
| Feature | Tani Edition (AQL) | Generic SQL (Standard) | Notes |
|
|
205
|
+
|---------|-------------------|------------------------|-------|
|
|
206
|
+
| **Create DB** | `BUKA WILAYAH [db]` | `CREATE DATABASE [db]` | Creates `.sawit` in data/ |
|
|
207
|
+
| **Use DB** | `MASUK WILAYAH [db]` | `USE [db]` | Switch context |
|
|
208
|
+
| **Show DBs** | `LIHAT WILAYAH` | `SHOW DATABASES` | Lists available DBs |
|
|
209
|
+
| **Drop DB** | `BAKAR WILAYAH [db]` | `DROP DATABASE [db]` | **Irreversible!** |
|
|
210
|
+
| **Create Table** | `LAHAN [table]` | `CREATE TABLE [table]` | Schema-less creation |
|
|
211
|
+
| **Show Tables** | `LIHAT LAHAN` | `SHOW TABLES` | Lists tables in DB |
|
|
212
|
+
| **Drop Table** | `BAKAR LAHAN [table]` | `DROP TABLE [table]` | Deletes table & data |
|
|
213
|
+
| **Insert** | `TANAM KE [table] ... BIBIT (...)` | `INSERT INTO [table] (...) VALUES (...)` | Auto-ID if omitted |
|
|
214
|
+
| **Select** | `PANEN ... DARI [table] DIMANA ...` | `SELECT ... FROM [table] WHERE ...` | Supports Projection |
|
|
215
|
+
| **Update** | `PUPUK [table] DENGAN ... DIMANA ...` | `UPDATE [table] SET ... WHERE ...` | Atomic update |
|
|
216
|
+
| **Delete** | `GUSUR DARI [table] DIMANA ...` | `DELETE FROM [table] WHERE ...` | Row-level deletion |
|
|
217
|
+
| **Index** | `INDEKS [table] PADA [field]` | `CREATE INDEX ON [table] (field)` | B-Tree Indexing |
|
|
218
|
+
| **Count** | `HITUNG COUNT(*) DARI [table]` | `SELECT COUNT(*) FROM [table]` (via HITUNG) | Aggregation |
|
|
219
|
+
| **Sum** | `HITUNG SUM(col) DARI [table]` | `SELECT SUM(col) FROM [table]` (via HITUNG) | Aggregation |
|
|
220
|
+
| **Average** | `HITUNG AVG(col) DARI [table]` | `SELECT AVG(col) FROM [table]` (via HITUNG) | Aggregation |
|
|
221
|
+
|
|
222
|
+
### Supported Operators Table
|
|
223
|
+
|
|
224
|
+
| Operator | Syntax Example | Description |
|
|
225
|
+
|----------|----------------|-------------|
|
|
226
|
+
| **Comparison** | `=`, `!=`, `>`, `<`, `>=`, `<=` | Standard value comparison |
|
|
227
|
+
| **Logical** | `AND`, `OR` | Combine multiple conditions |
|
|
228
|
+
| **In List** | `IN ('coffee', 'tea')` | Matches any value in the list |
|
|
229
|
+
| **Not In** | `NOT IN ('water')` | Matches values NOT in list |
|
|
230
|
+
| **Pattern** | `LIKE 'Jwa%'` | Standard SQL wildcard matching |
|
|
231
|
+
| **Range** | `BETWEEN 1000 AND 5000` | Inclusive range check |
|
|
232
|
+
| **Null** | `IS NULL` | Check if field is empty/null |
|
|
233
|
+
| **Not Null** | `IS NOT NULL` | Check if field has value |
|
|
234
|
+
| **Limit** | `LIMIT 10` | Restrict number of rows |
|
|
235
|
+
| **Offset** | `OFFSET 5` | Skip first N rows (Pagination) |
|
|
236
|
+
| **Order** | `ORDER BY price DESC` | Sort by field (ASC/DESC) |
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT License
|
|
240
|
+
<!-- ## Support Developer
|
|
241
|
+
- [](https://saweria.co/patradev)
|
|
242
|
+
|
|
243
|
+
- **BTC**: `12EnneEriimQey3cqvxtv4ZUbvpmEbDinL`
|
|
244
|
+
- **BNB Smart Chain (BEP20)**: `0x471a58a2b5072cb50e3761dba3e15d19f080bdbc`
|
|
245
|
+
- **DOGE**: `DHrFZW6w9akaWuf8BCBGxxRLR3PegKTggF` -->
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Start SawitDB Server
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node start_server.js
|
|
8
|
+
*
|
|
9
|
+
* Environment Variables:
|
|
10
|
+
* SAWIT_PORT - Port to listen on (default: 7878)
|
|
11
|
+
* SAWIT_HOST - Host to bind to (default: 0.0.0.0)
|
|
12
|
+
* SAWIT_DATA_DIR - Directory for database files (default: ./data)
|
|
13
|
+
* SAWIT_AUTH - Enable authentication (format: username:password)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const SawitServer = require('../src/SawitServer');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
// Parse configuration
|
|
20
|
+
const config = {
|
|
21
|
+
port: process.env.SAWIT_PORT || 7878,
|
|
22
|
+
host: process.env.SAWIT_HOST || '0.0.0.0',
|
|
23
|
+
dataDir: process.env.SAWIT_DATA_DIR || path.join(__dirname, '../data')
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Parse authentication if provided
|
|
27
|
+
if (process.env.SAWIT_AUTH) {
|
|
28
|
+
const [username, password] = process.env.SAWIT_AUTH.split(':');
|
|
29
|
+
if (username && password) {
|
|
30
|
+
config.auth = { [username]: password };
|
|
31
|
+
console.log(`[Config] Authentication enabled for user: ${username}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('[Config] Server configuration:');
|
|
36
|
+
console.log(` - Port: ${config.port}`);
|
|
37
|
+
console.log(` - Host: ${config.host}`);
|
|
38
|
+
console.log(` - Data Directory: ${config.dataDir}`);
|
|
39
|
+
console.log(` - Auth: ${config.auth ? 'Enabled' : 'Disabled'}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
// Create and start server
|
|
43
|
+
const server = new SawitServer(config);
|
|
44
|
+
server.start();
|
|
45
|
+
|
|
46
|
+
// Graceful shutdown
|
|
47
|
+
process.on('SIGINT', () => {
|
|
48
|
+
console.log('\n[Server] Received SIGINT, shutting down gracefully...');
|
|
49
|
+
server.stop();
|
|
50
|
+
setTimeout(() => process.exit(0), 1000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
process.on('SIGTERM', () => {
|
|
54
|
+
console.log('\n[Server] Received SIGTERM, shutting down gracefully...');
|
|
55
|
+
server.stop();
|
|
56
|
+
setTimeout(() => process.exit(0), 1000);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Handle uncaught errors
|
|
60
|
+
process.on('uncaughtException', (err) => {
|
|
61
|
+
console.error('[Server] Uncaught Exception:', err);
|
|
62
|
+
server.stop();
|
|
63
|
+
process.exit(1);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
67
|
+
console.error('[Server] Unhandled Rejection at:', promise, 'reason:', reason);
|
|
68
|
+
});
|
package/cli/local.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const SawitDB = require('../src/WowoEngine');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const dbPath = path.join(__dirname, 'example.sawit');
|
|
6
|
+
const db = new SawitDB(dbPath);
|
|
7
|
+
|
|
8
|
+
const rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
console.log("--- WOWODB TANI EDITION V2 (SQL-Like) ---");
|
|
14
|
+
console.log("Perintah:");
|
|
15
|
+
console.log(" LAHAN [nama_kebun]");
|
|
16
|
+
console.log(" LIHAT LAHAN");
|
|
17
|
+
console.log(" TANAM KE [kebun] (col,...) BIBIT (val,...)");
|
|
18
|
+
console.log(" PANEN * DARI [kebun]");
|
|
19
|
+
console.log(" PANEN ... DIMANA col [=,>,<,!=] val");
|
|
20
|
+
console.log(" GUSUR DARI [kebun] DIMANA col = val");
|
|
21
|
+
console.log(" PUPUK [kebun] DENGAN col=val ... DIMANA col = val");
|
|
22
|
+
console.log(" BAKAR LAHAN [kebun]");
|
|
23
|
+
console.log("\nContoh:");
|
|
24
|
+
console.log(" TANAM KE sawit (id, bibit) BIBIT (1, 'Dura')");
|
|
25
|
+
console.log(" PANEN * DARI sawit DIMANA id > 0");
|
|
26
|
+
console.log(" BAKAR LAHAN karet");
|
|
27
|
+
console.log("Ketik 'EXIT' untuk pulang.");
|
|
28
|
+
|
|
29
|
+
function prompt() {
|
|
30
|
+
rl.question('petani> ', (line) => {
|
|
31
|
+
const cmd = line.trim();
|
|
32
|
+
if (cmd.toUpperCase() === 'EXIT') {
|
|
33
|
+
rl.close();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (cmd) {
|
|
38
|
+
const result = db.query(cmd);
|
|
39
|
+
if (typeof result === 'object') {
|
|
40
|
+
console.log(JSON.stringify(result, null, 2));
|
|
41
|
+
} else {
|
|
42
|
+
console.log(result);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
prompt();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
prompt();
|
package/cli/remote.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interactive CLI Client for SawitDB
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node cli_client.js [connection_string]
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
* node cli_client.js sawitdb://localhost:7878/mydb
|
|
11
|
+
* node cli_client.js sawitdb://user:pass@localhost:7878/mydb
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const readline = require('readline');
|
|
15
|
+
const SawitClient = require('../src/SawitClient');
|
|
16
|
+
|
|
17
|
+
const connStr = process.argv[2] || 'sawitdb://localhost:7878/default';
|
|
18
|
+
|
|
19
|
+
console.log('╔══════════════════════════════════════════════════╗');
|
|
20
|
+
console.log('║ 🌴 SawitDB Client - Interactive CLI ║');
|
|
21
|
+
console.log('╚══════════════════════════════════════════════════╝');
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(`Connecting to: ${connStr}`);
|
|
24
|
+
console.log('');
|
|
25
|
+
|
|
26
|
+
const client = new SawitClient(connStr);
|
|
27
|
+
let rl;
|
|
28
|
+
|
|
29
|
+
async function init() {
|
|
30
|
+
try {
|
|
31
|
+
await client.connect();
|
|
32
|
+
console.log('✓ Connected to SawitDB server\n');
|
|
33
|
+
console.log('Commands:');
|
|
34
|
+
console.log(' LAHAN [nama] - Create table');
|
|
35
|
+
console.log(' LIHAT LAHAN - Show tables');
|
|
36
|
+
console.log(' LIHAT INDEKS [table] - Show indexes');
|
|
37
|
+
console.log(' TANAM KE ... - Insert data');
|
|
38
|
+
console.log(' PANEN ... DARI ... - Select data');
|
|
39
|
+
console.log(' PUPUK ... DENGAN ... - Update data');
|
|
40
|
+
console.log(' GUSUR DARI ... - Delete data');
|
|
41
|
+
console.log(' BAKAR LAHAN [nama] - Drop table');
|
|
42
|
+
console.log(' INDEKS [table] PADA [field] - Create index');
|
|
43
|
+
console.log(' HITUNG FUNC(...) DARI ... - Aggregate');
|
|
44
|
+
console.log(' .databases - List all databases');
|
|
45
|
+
console.log(' .use [db] - Switch database');
|
|
46
|
+
console.log(' .ping - Ping server');
|
|
47
|
+
console.log(' .help - Show this help');
|
|
48
|
+
console.log(' EXIT - Disconnect and exit');
|
|
49
|
+
console.log('');
|
|
50
|
+
|
|
51
|
+
rl = readline.createInterface({
|
|
52
|
+
input: process.stdin,
|
|
53
|
+
output: process.stdout
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
prompt();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('Failed to connect:', error.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function prompt() {
|
|
64
|
+
rl.question(`${client.currentDatabase || 'none'}> `, async (line) => {
|
|
65
|
+
const cmd = line.trim();
|
|
66
|
+
|
|
67
|
+
if (!cmd) {
|
|
68
|
+
return prompt();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (cmd.toUpperCase() === 'EXIT') {
|
|
72
|
+
console.log('\nDisconnecting...');
|
|
73
|
+
client.disconnect();
|
|
74
|
+
rl.close();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Special commands
|
|
80
|
+
if (cmd.startsWith('.')) {
|
|
81
|
+
await handleSpecialCommand(cmd);
|
|
82
|
+
return prompt();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Regular query
|
|
86
|
+
try {
|
|
87
|
+
const result = await client.query(cmd);
|
|
88
|
+
if (typeof result === 'object') {
|
|
89
|
+
console.log(JSON.stringify(result, null, 2));
|
|
90
|
+
} else {
|
|
91
|
+
console.log(result);
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error:', error.message);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
prompt();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function handleSpecialCommand(cmd) {
|
|
102
|
+
const parts = cmd.split(' ');
|
|
103
|
+
const command = parts[0];
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
switch (command) {
|
|
107
|
+
case '.databases':
|
|
108
|
+
const dbs = await client.listDatabases();
|
|
109
|
+
console.log('Databases:', dbs.join(', '));
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case '.use':
|
|
113
|
+
if (parts.length < 2) {
|
|
114
|
+
console.log('Usage: .use [database]');
|
|
115
|
+
} else {
|
|
116
|
+
await client.use(parts[1]);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
case '.ping':
|
|
121
|
+
const ping = await client.ping();
|
|
122
|
+
console.log(`Pong! Latency: ${ping.latency}ms`);
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case '.stats':
|
|
126
|
+
const stats = await client.stats();
|
|
127
|
+
console.log('\nServer Statistics:');
|
|
128
|
+
console.log(` Uptime: ${stats.uptimeFormatted}`);
|
|
129
|
+
console.log(` Total Connections: ${stats.totalConnections}`);
|
|
130
|
+
console.log(` Active Connections: ${stats.activeConnections}`);
|
|
131
|
+
console.log(` Total Queries: ${stats.totalQueries}`);
|
|
132
|
+
console.log(` Errors: ${stats.errors}`);
|
|
133
|
+
console.log(` Databases: ${stats.databases}`);
|
|
134
|
+
console.log(` Memory: ${Math.round(stats.memoryUsage.heapUsed / 1024 / 1024)}MB / ${Math.round(stats.memoryUsage.heapTotal / 1024 / 1024)}MB`);
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case '.help':
|
|
138
|
+
console.log('\nAvailable commands:');
|
|
139
|
+
console.log(' .databases - List all databases');
|
|
140
|
+
console.log(' .use [db] - Switch to database');
|
|
141
|
+
console.log(' .ping - Test connection');
|
|
142
|
+
console.log(' .stats - Show server statistics');
|
|
143
|
+
console.log(' .help - Show this help');
|
|
144
|
+
console.log('');
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
default:
|
|
148
|
+
console.log(`Unknown command: ${command}`);
|
|
149
|
+
console.log('Type .help for available commands');
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Error:', error.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Start
|
|
157
|
+
init();
|
|
158
|
+
|
|
159
|
+
// Handle exit
|
|
160
|
+
process.on('SIGINT', () => {
|
|
161
|
+
console.log('\n\nDisconnecting...');
|
|
162
|
+
client.disconnect();
|
|
163
|
+
if (rl) rl.close();
|
|
164
|
+
process.exit(0);
|
|
165
|
+
});
|