jsql-neo 4.0.0 → 4.0.2
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/README.md
CHANGED
|
@@ -1,78 +1,101 @@
|
|
|
1
|
-
# JSQL-NEO
|
|
1
|
+
# JSQL-NEO v4.0.1
|
|
2
2
|
|
|
3
|
-
Rust-powered embedded database with **three engines** in one npm package.
|
|
3
|
+
Rust-powered embedded database with **three engines + SQL + Redis-style storage** in one npm package.
|
|
4
4
|
|
|
5
5
|
## Engines
|
|
6
6
|
|
|
7
|
-
| Engine | Entry | Use Case |
|
|
8
|
-
|
|
9
|
-
| **
|
|
10
|
-
| **
|
|
11
|
-
| **Pure JS** (
|
|
7
|
+
| Engine | Entry | Use Case |
|
|
8
|
+
|--------|-------|----------|
|
|
9
|
+
| **Native** (Rust → N-API, fastest) | `NativeJSQL` | Node.js, best performance |
|
|
10
|
+
| **WASM** (Rust → wasm-pack, zero native deps) | `JSQL` | Node.js / browsers, no native addon |
|
|
11
|
+
| **Pure JS** (local JSON file, SQLite-like) | `Database` | Local file persistence, legacy API |
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
All engines share the same API (`createTable` / `insert` / `findById` / `find` / `updateById` / `removeById` / `dropTable`) plus `executeSQL()`.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## SQL
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
|
-
const
|
|
18
|
+
const jsql = require('jsql-neo');
|
|
19
|
+
const db = new jsql.NativeJSQL();
|
|
20
|
+
await db.start();
|
|
21
|
+
|
|
22
|
+
await jsql.executeSQL(db, 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name STRING, age INTEGER)');
|
|
23
|
+
await jsql.executeSQL(db, "INSERT INTO users (name, age) VALUES ('Alice', 30), ('Bob', 25)");
|
|
24
|
+
await jsql.executeSQL(db, "INSERT INTO users VALUES (5, 'Carol', 35) ON DUPLICATE KEY UPDATE age = 30");
|
|
25
|
+
const r = await jsql.executeSQL(db, 'SELECT name, age FROM users WHERE age > 26 ORDER BY age DESC');
|
|
26
|
+
// → rows: [["Carol",35],["Alice",30]]
|
|
27
|
+
|
|
28
|
+
await jsql.executeSQL(db, 'UPDATE users SET age = 31 WHERE id = 1');
|
|
29
|
+
await jsql.executeSQL(db, 'DELETE FROM users WHERE id = 2');
|
|
30
|
+
await db.stop();
|
|
31
|
+
```
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
Supported: `CREATE/DROP TABLE`, `INSERT` (multi-row, `ON DUPLICATE KEY UPDATE`), `SELECT` (`WHERE`/`ORDER BY`/`LIMIT`/`OFFSET`/`GROUP BY`/`HAVING`/aggregates), `UPDATE`, `DELETE`, prepared statements with `?` placeholders.
|
|
34
|
+
|
|
35
|
+
## Redis-Style Storage Modes
|
|
36
|
+
|
|
37
|
+
Native and Pure JS engines support three persistence modes (Redis-compatible model: memory-first, async flush, LRU eviction, lazy reload):
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const db = new jsql.NativeJSQL({
|
|
41
|
+
path: '/var/lib/jsql', // storage directory (required for hybrid/disk)
|
|
42
|
+
mode: 'hybrid', // 'memory' (default) | 'hybrid' | 'disk'
|
|
43
|
+
memReserveMB: 512, // keep 512MB RAM headroom before evicting
|
|
44
|
+
flushInterval: 200, // ms between async flushes (hybrid 200 / disk 50)
|
|
45
|
+
evictInterval: 1000, // ms between memory-pressure checks
|
|
46
|
+
});
|
|
47
|
+
await db.start();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- **`memory`** — pure in-memory (default, no path needed)
|
|
51
|
+
- **`hybrid`** — writes go to memory first, async incremental flush to disk; cold tables are LRU-evicted when memory pressure exceeds `total - memReserveMB`, and lazily reloaded on next access
|
|
52
|
+
- **`disk`** — fast flush (50ms), memory acts as read/write cache
|
|
53
|
+
|
|
54
|
+
Data is stored per-table as `<dir>/<table>.jsql.json` + `meta.json`; writes are atomic (tmp + rename).
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// Pure JS engine (same options)
|
|
58
|
+
const db2 = new jsql.Database({ path: '/var/lib/jsql', mode: 'hybrid' });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Native (fastest)
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
const { NativeJSQL } = require('jsql-neo');
|
|
67
|
+
const db = new NativeJSQL();
|
|
21
68
|
await db.start();
|
|
22
69
|
|
|
23
70
|
await db.createTable('users', {
|
|
24
71
|
name: { type: 'string' },
|
|
25
72
|
age: { type: 'integer' }
|
|
26
73
|
});
|
|
27
|
-
|
|
28
74
|
const [id] = await db.insert('users', { name: 'Alice', age: 30 });
|
|
29
75
|
const user = await db.findById('users', id);
|
|
30
76
|
// → { id: 1, fields: { name: 'Alice', age: 30 }, created_at: '...', updated_at: '...' }
|
|
31
|
-
|
|
32
77
|
await db.stop();
|
|
33
78
|
```
|
|
34
79
|
|
|
35
|
-
### Pure JS (local file
|
|
80
|
+
### Pure JS (local file)
|
|
36
81
|
|
|
37
82
|
```js
|
|
38
83
|
const { Database } = require('jsql-neo');
|
|
39
|
-
|
|
40
|
-
const db = new Database('/tmp/mydb.json');
|
|
84
|
+
const db = new Database('/tmp/mydb.json'); // or { path, mode } for hybrid/disk
|
|
41
85
|
const users = db.createTable('users', {
|
|
42
86
|
id: { type: 'integer', autoIncrement: true, primaryKey: true },
|
|
43
87
|
name: { type: 'string', length: 32 },
|
|
44
88
|
age: { type: 'integer' }
|
|
45
89
|
});
|
|
46
|
-
|
|
47
90
|
const ids = users.insertMany([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);
|
|
48
91
|
users.updateById(ids[0], { age: 31 });
|
|
49
|
-
const user = users.findById(ids[0]);
|
|
50
92
|
db.save();
|
|
51
93
|
```
|
|
52
94
|
|
|
53
|
-
### HTTP Server
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
JSQL_DATA_DIR=/tmp/jsql-neo npx jsql-neo
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
```js
|
|
60
|
-
const { HttpJSQL } = require('jsql-neo');
|
|
61
|
-
|
|
62
|
-
const db = new HttpJSQL({ host: '127.0.0.1', port: 6379 });
|
|
63
|
-
await db.start();
|
|
64
|
-
|
|
65
|
-
await db.createTable('users', { name: { type: 'string' }, age: { type: 'integer' } });
|
|
66
|
-
const [id] = await db.insert('users', { name: 'Alice', age: 30 });
|
|
67
|
-
const user = await db.findById('users', id);
|
|
68
|
-
|
|
69
|
-
await db.stop();
|
|
70
|
-
```
|
|
71
|
-
|
|
72
95
|
## API
|
|
73
96
|
|
|
74
|
-
| Method |
|
|
75
|
-
|
|
97
|
+
| Method | Native | WASM | Pure JS | Description |
|
|
98
|
+
|--------|--------|------|---------|-------------|
|
|
76
99
|
| `createTable(name, schema)` | ✅ | ✅ | ✅ | Define table with typed fields |
|
|
77
100
|
| `insert(table, data)` | ✅ | ✅ | ✅ | Insert row(s), returns IDs |
|
|
78
101
|
| `findById(table, id)` | ✅ | ✅ | ✅ | O(1) PK lookup |
|
|
@@ -81,19 +104,7 @@ await db.stop();
|
|
|
81
104
|
| `updateById(table, id, data)` | ✅ | ✅ | ✅ | O(1) PK update |
|
|
82
105
|
| `removeById(table, id)` | ✅ | ✅ | ✅ | O(1) PK delete |
|
|
83
106
|
| `dropTable(name)` | ✅ | ✅ | ✅ | Remove table |
|
|
84
|
-
|
|
85
|
-
All engines share the same async API for CRUD operations.
|
|
86
|
-
|
|
87
|
-
## Performance (Pure JS, 100K rows)
|
|
88
|
-
|
|
89
|
-
| Operation | Time | Rate |
|
|
90
|
-
|-----------|------|------|
|
|
91
|
-
| Insert 100K | 128 ms | 781K rows/sec |
|
|
92
|
-
| findById × 10,000 | 3 ms | 0.3 μs each |
|
|
93
|
-
| updateById × 10,000 | 82 ms | 8.2 μs each (O(1) hash index) |
|
|
94
|
-
| count × 100 | 0 ms | — |
|
|
95
|
-
| findAll × 5 | 16 ms | — |
|
|
96
|
-
| Filtered query × 100 | 407 ms | — |
|
|
107
|
+
| `executeSQL(db, sql, params?)` | ✅ | ✅ | ✅ | Run SQL statements |
|
|
97
108
|
|
|
98
109
|
## Schema Field Options
|
|
99
110
|
|
|
@@ -110,11 +121,12 @@ All engines share the same async API for CRUD operations.
|
|
|
110
121
|
|
|
111
122
|
## Features
|
|
112
123
|
|
|
113
|
-
- Three engines:
|
|
124
|
+
- Three engines: Native (N-API Rust), WASM (wasm-pack Rust), Pure JS (local JSON)
|
|
125
|
+
- SQL engine with prepared statements
|
|
126
|
+
- Redis-style hybrid/disk storage: memory-first + async flush + LRU eviction + lazy reload
|
|
114
127
|
- O(1) primary key hash index (`FxHashMap` / `Map`)
|
|
115
128
|
- B-Tree indexing for range queries
|
|
116
|
-
- WAL + snapshot crash recovery (
|
|
117
|
-
- Strings Pool for memory-efficient storage
|
|
129
|
+
- WAL + snapshot crash recovery (server engine)
|
|
118
130
|
- Batch insert / update / delete
|
|
119
131
|
- Cursor-based pagination
|
|
120
|
-
- Transaction support (
|
|
132
|
+
- Transaction support (server engine)
|
package/lib/native_client.js
CHANGED
|
@@ -11,9 +11,16 @@ function encodeBatch(rows) {
|
|
|
11
11
|
if (rows.length === 0) return new Uint8Array(0);
|
|
12
12
|
const fieldNames = Object.keys(rows[0]);
|
|
13
13
|
const nFields = fieldNames.length;
|
|
14
|
-
const est = 100 + rows.length *
|
|
15
|
-
|
|
14
|
+
const est = 100 + rows.length * 160;
|
|
15
|
+
let buf = Buffer.allocUnsafe(est);
|
|
16
16
|
let off = 0;
|
|
17
|
+
const ensure = (n) => {
|
|
18
|
+
if (off + n > buf.length) {
|
|
19
|
+
const nb = Buffer.allocUnsafe(Math.max(buf.length * 2, off + n));
|
|
20
|
+
buf.copy(nb, 0, 0, off);
|
|
21
|
+
buf = nb;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
17
24
|
|
|
18
25
|
off = buf.writeUInt8(nFields, off);
|
|
19
26
|
for (let fi = 0; fi < nFields; fi++) {
|
|
@@ -34,8 +41,9 @@ function encodeBatch(rows) {
|
|
|
34
41
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
35
42
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
36
43
|
} else if (typeof v === 'string') {
|
|
37
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
38
44
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
45
|
+
ensure(5 + sl);
|
|
46
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
39
47
|
off = buf.writeUInt32LE(sl, off);
|
|
40
48
|
off += buf.write(v, off, sl, 'utf8');
|
|
41
49
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -47,8 +55,9 @@ function encodeBatch(rows) {
|
|
|
47
55
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
48
56
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
49
57
|
} else if (typeof v === 'string') {
|
|
50
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
51
58
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
59
|
+
ensure(5 + sl);
|
|
60
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
52
61
|
off = buf.writeUInt32LE(sl, off);
|
|
53
62
|
off += buf.write(v, off, sl, 'utf8');
|
|
54
63
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -60,8 +69,9 @@ function encodeBatch(rows) {
|
|
|
60
69
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
61
70
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
62
71
|
} else if (typeof v === 'string') {
|
|
63
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
64
72
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
73
|
+
ensure(5 + sl);
|
|
74
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
65
75
|
off = buf.writeUInt32LE(sl, off);
|
|
66
76
|
off += buf.write(v, off, sl, 'utf8');
|
|
67
77
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -73,8 +83,9 @@ function encodeBatch(rows) {
|
|
|
73
83
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
74
84
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
75
85
|
} else if (typeof v === 'string') {
|
|
76
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
77
86
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
87
|
+
ensure(5 + sl);
|
|
88
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
78
89
|
off = buf.writeUInt32LE(sl, off);
|
|
79
90
|
off += buf.write(v, off, sl, 'utf8');
|
|
80
91
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -86,8 +97,9 @@ function encodeBatch(rows) {
|
|
|
86
97
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
87
98
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
88
99
|
} else if (typeof v === 'string') {
|
|
89
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
90
100
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
101
|
+
ensure(5 + sl);
|
|
102
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
91
103
|
off = buf.writeUInt32LE(sl, off);
|
|
92
104
|
off += buf.write(v, off, sl, 'utf8');
|
|
93
105
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -99,8 +111,9 @@ function encodeBatch(rows) {
|
|
|
99
111
|
if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
|
|
100
112
|
else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
|
|
101
113
|
} else if (typeof v === 'string') {
|
|
102
|
-
off = buf.writeUInt8(STR_TAG, off);
|
|
103
114
|
const sl = Buffer.byteLength(v, 'utf8');
|
|
115
|
+
ensure(5 + sl);
|
|
116
|
+
off = buf.writeUInt8(STR_TAG, off);
|
|
104
117
|
off = buf.writeUInt32LE(sl, off);
|
|
105
118
|
off += buf.write(v, off, sl, 'utf8');
|
|
106
119
|
} else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
|
|
@@ -328,16 +341,11 @@ class JSQL {
|
|
|
328
341
|
if (!this._runHooks('beforeInsert', [table, filtered])) return [];
|
|
329
342
|
if (arr.length > 1) {
|
|
330
343
|
await this._flush();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (!result) result = r;
|
|
337
|
-
}
|
|
338
|
-
this._emit('insert', { table, count: arr.length, ids: result });
|
|
339
|
-
this._runHooks('afterInsert', [table, filtered, result]);
|
|
340
|
-
return result;
|
|
344
|
+
const r = await this._insertBatch(table, arr);
|
|
345
|
+
if (r && r.error) throw new Error(r.error);
|
|
346
|
+
this._emit('insert', { table, count: arr.length, ids: r });
|
|
347
|
+
this._runHooks('afterInsert', [table, filtered, r]);
|
|
348
|
+
return r;
|
|
341
349
|
}
|
|
342
350
|
if (!this._buffer[table]) this._buffer[table] = [];
|
|
343
351
|
this._buffer[table].push(arr[0]);
|
package/lib/sql.js
CHANGED
|
@@ -678,7 +678,7 @@ function extractEqualPushdown(expr, schema) {
|
|
|
678
678
|
if (node.type === 'and') { walk(node.left); walk(node.right); return; }
|
|
679
679
|
if (node.type === 'compare' && node.op === '=') {
|
|
680
680
|
const col = node.left && node.left.type === 'column' ? node.left.name : null;
|
|
681
|
-
const val = node.right && node.right.type === 'literal' ? node.right.value : undefined;
|
|
681
|
+
const val = node.right && (node.right.type === 'literal' || node.right.type === 'value' || node.right.type === 'param') ? node.right.value : undefined;
|
|
682
682
|
if (col && val !== undefined && val !== null && schema[col] && !(schema[col].primaryKey && schema[col].autoIncrement === false)) {
|
|
683
683
|
filter[col] = val;
|
|
684
684
|
return;
|
|
@@ -728,9 +728,10 @@ function normalizeRow(row, schema) {
|
|
|
728
728
|
if (schema) {
|
|
729
729
|
const pkCols = Object.keys(schema).filter(k => schema[k].primaryKey);
|
|
730
730
|
for (const c of pkCols) {
|
|
731
|
-
|
|
731
|
+
const v = flat[c];
|
|
732
|
+
if (row.id !== undefined && (v === undefined || v === null || v === 0 || v === '')) flat[c] = row.id;
|
|
732
733
|
}
|
|
733
|
-
} else if (row.id !== undefined && flat.id === undefined) {
|
|
734
|
+
} else if (row.id !== undefined && (flat.id === undefined || flat.id === null || flat.id === 0 || flat.id === '')) {
|
|
734
735
|
flat.id = row.id;
|
|
735
736
|
}
|
|
736
737
|
flat._rid = row.id;
|
|
@@ -858,6 +859,11 @@ class SQLExecutor {
|
|
|
858
859
|
if (this.engine.hasTable && !this.engine.hasTable(statement.table)) {
|
|
859
860
|
throw new Error(`Table '${statement.table}' does not exist`);
|
|
860
861
|
}
|
|
862
|
+
if (statement.aggregate && statement.aggregate.type === 'COUNT' && !statement.where && !statement.distinct) {
|
|
863
|
+
const aggName = statement.aggregate.alias || 'COUNT(*)';
|
|
864
|
+
const n = this.engine.count ? await this.engine.count(statement.table) : 0;
|
|
865
|
+
return { ok: true, type: 'select', table: statement.table, columns: [aggName], rows: [[n]], aggregate: statement.aggregate };
|
|
866
|
+
}
|
|
861
867
|
schema = this.engine.getTableSchema
|
|
862
868
|
? await this.engine.getTableSchema(statement.table)
|
|
863
869
|
: (this.engine._schemas ? this.engine._schemas[statement.table] : null);
|
|
@@ -905,7 +911,13 @@ class SQLExecutor {
|
|
|
905
911
|
const rowsOnly = rows;
|
|
906
912
|
const aggName = agg.alias || (agg.type === 'COUNT' ? 'COUNT(*)' : agg.type + '(' + agg.column + ')');
|
|
907
913
|
if (agg.type === 'COUNT') {
|
|
908
|
-
|
|
914
|
+
let n;
|
|
915
|
+
if (!statement.where && !statement.distinct) {
|
|
916
|
+
n = this.engine.count ? await this.engine.count(statement.table) : rowsOnly.length;
|
|
917
|
+
} else {
|
|
918
|
+
n = rowsOnly.length;
|
|
919
|
+
}
|
|
920
|
+
return { ok: true, type: 'select', table: statement.table, columns: [aggName], rows: [[n]], aggregate: agg };
|
|
909
921
|
}
|
|
910
922
|
const values = rowsOnly.map(r => r[agg.column]).filter(v => v !== null && v !== undefined);
|
|
911
923
|
let value;
|
|
Binary file
|
|
@@ -235,7 +235,7 @@ impl Table {
|
|
|
235
235
|
|
|
236
236
|
if let Some(ref pk_key) = pk_auto_key {
|
|
237
237
|
match fields.get(pk_key) {
|
|
238
|
-
Some(v) if v.is_null() => {
|
|
238
|
+
Some(v) if v.is_null() || v.as_i64() == Some(0) => {
|
|
239
239
|
fields.insert(pk_key.clone(), serde_json::Value::Number(id.into()));
|
|
240
240
|
}
|
|
241
241
|
Some(v) => {
|
|
@@ -307,7 +307,7 @@ impl Table {
|
|
|
307
307
|
if let Some(pi) = pk_idx {
|
|
308
308
|
if pi < row_vals.len() {
|
|
309
309
|
match row_vals[pi] {
|
|
310
|
-
FieldValue::Null => row_vals[pi] = FieldValue::Int(id as i64),
|
|
310
|
+
FieldValue::Null | FieldValue::Int(0) => row_vals[pi] = FieldValue::Int(id as i64),
|
|
311
311
|
FieldValue::Int(e) if e > 0 => {
|
|
312
312
|
self.next_id = self.next_id.max((e + 1) as u64);
|
|
313
313
|
}
|
|
@@ -344,7 +344,13 @@ impl Table {
|
|
|
344
344
|
for (i, remapped_si) in remap.iter().enumerate() {
|
|
345
345
|
if let Some(si) = *remapped_si {
|
|
346
346
|
if si < row_vals.len() {
|
|
347
|
-
|
|
347
|
+
let v = std::mem::take(&mut row_vals[si]);
|
|
348
|
+
let is_auto_pk = pk_auto_key.is_some() && self.field_order[i] == *pk_auto_key.as_ref().unwrap();
|
|
349
|
+
if is_auto_pk && matches!(v, FieldValue::Null | FieldValue::Int(0)) {
|
|
350
|
+
self.values.push(FieldValue::Int(id as i64));
|
|
351
|
+
} else {
|
|
352
|
+
self.values.push(v);
|
|
353
|
+
}
|
|
348
354
|
continue;
|
|
349
355
|
}
|
|
350
356
|
}
|
|
Binary file
|