jsql-neo 5.5.0 → 6.0.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.
Files changed (81) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8155 -8097
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +412 -385
  7. package/index.js +128 -124
  8. package/lib/ast.js +214 -214
  9. package/lib/btree.js +478 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1741 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +722 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +259 -35
  26. package/lib/plugins/audit.js +37 -0
  27. package/lib/plugins/index.js +72 -0
  28. package/lib/plugins/logger.js +35 -0
  29. package/lib/plugins/metrics.js +38 -0
  30. package/lib/plugins/timestamps.js +57 -0
  31. package/lib/plugins/validation.js +128 -0
  32. package/lib/prepared.js +37 -0
  33. package/lib/query.js +834 -815
  34. package/lib/redis_server.js +571 -571
  35. package/lib/sql.js +4053 -3460
  36. package/lib/sqlite_compat.js +323 -323
  37. package/lib/sqlite_worker.js +264 -264
  38. package/lib/table.js +1351 -1304
  39. package/lib/tui.js +502 -502
  40. package/lib/wasm.js +1 -1
  41. package/lib/wasm_client.js +715 -718
  42. package/lib/web_ui.js +257 -257
  43. package/native/jsql-neo-native.node +0 -0
  44. package/nativesrc/jsql-neo-core/Cargo.lock +462 -455
  45. package/nativesrc/jsql-neo-core/Cargo.toml +26 -24
  46. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +492 -455
  47. package/nativesrc/jsql-neo-core/src/engine/memory.rs +191 -168
  48. package/nativesrc/jsql-neo-core/src/engine/mod.rs +74 -62
  49. package/nativesrc/jsql-neo-core/src/engine/table.rs +996 -700
  50. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  51. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  52. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  53. package/nativesrc/jsql-neo-core/src/storage/wal.rs +298 -85
  54. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  55. package/nativesrc/jsql-neo-native/Cargo.lock +613 -606
  56. package/nativesrc/jsql-neo-native/Cargo.toml +24 -16
  57. package/nativesrc/jsql-neo-native/build.rs +3 -3
  58. package/nativesrc/jsql-neo-native/package.json +7 -7
  59. package/nativesrc/jsql-neo-native/src/lib.rs +281 -269
  60. package/nativesrc/jsql-neo-wasm/Cargo.lock +472 -465
  61. package/nativesrc/jsql-neo-wasm/Cargo.toml +22 -15
  62. package/nativesrc/jsql-neo-wasm/src/lib.rs +463 -447
  63. package/package.json +87 -87
  64. package/postinstall.js +158 -158
  65. package/test/btree.test.js +144 -128
  66. package/test/coverage.js +187 -187
  67. package/test/join.test.js +110 -110
  68. package/test/native.test.js +158 -158
  69. package/test/query-opt.test.js +109 -0
  70. package/test/regress-5.1.0.js +271 -271
  71. package/test/smoke.js +58 -58
  72. package/test/sql-parser.test.js +450 -273
  73. package/test/wasm.test.js +110 -110
  74. package/wasm/browser.d.ts +89 -89
  75. package/wasm/browser.mjs +591 -569
  76. package/wasm/browser_bg.mjs +618 -597
  77. package/wasm/jsql_neo_wasm.d.ts +44 -38
  78. package/wasm/jsql_neo_wasm.js +641 -520
  79. package/wasm/jsql_neo_wasm_bg.wasm +0 -0
  80. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  81. package/wasm/package.json +11 -11
@@ -1,700 +1,996 @@
1
- use std::collections::HashMap;
2
- use rustc_hash::FxHashMap;
3
- use serde::Serialize;
4
- use crate::types::{Row, FieldSchema, FieldType};
5
- use std::fmt::Write;
6
-
7
- #[derive(Debug, Clone, PartialEq, Default)]
8
- pub enum FieldValue {
9
- #[default]
10
- Null,
11
- Bool(bool),
12
- Int(i64),
13
- Float(f64),
14
- String(String),
15
- }
16
-
17
- impl serde::Serialize for FieldValue {
18
- fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
19
- match self {
20
- FieldValue::Null => s.serialize_unit(),
21
- FieldValue::Bool(b) => s.serialize_bool(*b),
22
- FieldValue::Int(n) => s.serialize_i64(*n),
23
- FieldValue::Float(f) => s.serialize_f64(*f),
24
- FieldValue::String(st) => s.serialize_str(st),
25
- }
26
- }
27
- }
28
-
29
- impl FieldValue {
30
- pub fn from_json(jv: serde_json::Value) -> Self {
31
- match jv {
32
- serde_json::Value::Null => FieldValue::Null,
33
- serde_json::Value::Bool(b) => FieldValue::Bool(b),
34
- serde_json::Value::Number(n) => {
35
- n.as_i64().map(FieldValue::Int)
36
- .or_else(|| n.as_f64().map(FieldValue::Float))
37
- .unwrap_or(FieldValue::Null)
38
- }
39
- serde_json::Value::String(s) => FieldValue::String(s),
40
- _ => FieldValue::Null,
41
- }
42
- }
43
-
44
- fn to_json(&self) -> serde_json::Value {
45
- match self {
46
- FieldValue::Null => serde_json::Value::Null,
47
- FieldValue::Bool(b) => serde_json::Value::Bool(*b),
48
- FieldValue::Int(n) => serde_json::Value::Number((*n).into()),
49
- FieldValue::Float(f) => serde_json::Number::from_f64(*f)
50
- .map(serde_json::Value::Number)
51
- .unwrap_or(serde_json::Value::Null),
52
- FieldValue::String(s) => serde_json::Value::String(s.clone()),
53
- }
54
- }
55
-
56
- pub fn write_json(&self, out: &mut String) {
57
- match self {
58
- FieldValue::Null => out.push_str("null"),
59
- FieldValue::Bool(true) => out.push_str("true"),
60
- FieldValue::Bool(false) => out.push_str("false"),
61
- FieldValue::Int(n) => {
62
- out.push_str(itoa::Buffer::new().format(*n));
63
- }
64
- FieldValue::Float(f) => {
65
- out.push_str(ryu::Buffer::new().format_finite(*f));
66
- }
67
- FieldValue::String(s) => {
68
- write_json_string(out, s);
69
- }
70
- }
71
- }
72
-
73
- pub fn eq_json(&self, jv: &serde_json::Value) -> bool {
74
- match (self, jv) {
75
- (FieldValue::Null, serde_json::Value::Null) => true,
76
- (FieldValue::Bool(a), serde_json::Value::Bool(b)) => a == b,
77
- (FieldValue::Int(a), serde_json::Value::Number(n)) => n.as_i64() == Some(*a),
78
- (FieldValue::Float(a), serde_json::Value::Number(n)) => n.as_f64() == Some(*a),
79
- (FieldValue::String(a), serde_json::Value::String(b)) => a == b,
80
- _ => false,
81
- }
82
- }
83
- }
84
-
85
- #[derive(Debug, Clone)]
86
- pub struct RowStore {
87
- pub id: u64,
88
- pub values_start: usize,
89
- pub num_values: usize,
90
- pub created_at: u64,
91
- pub updated_at: u64,
92
- }
93
-
94
- #[derive(Debug, Clone)]
95
- pub struct Table {
96
- pub name: String,
97
- pub schema: HashMap<String, FieldSchema>,
98
- pub field_order: Vec<String>,
99
- pub field_index: HashMap<String, usize>,
100
- pub rows: Vec<RowStore>,
101
- pub values: Vec<FieldValue>,
102
- pub next_id: u64,
103
- pub pk_field: Option<String>,
104
- pub pk_index: FxHashMap<u64, usize>,
105
- }
106
-
107
- pub fn default_value(ft: &FieldType) -> FieldValue {
108
- match ft {
109
- FieldType::Integer => FieldValue::Int(0),
110
- FieldType::Float => FieldValue::Float(0.0),
111
- FieldType::String => FieldValue::String(String::new()),
112
- FieldType::Boolean => FieldValue::Bool(false),
113
- }
114
- }
115
-
116
- fn now_millis() -> u64 {
117
- #[cfg(feature = "wasm")]
118
- {
119
- js_sys::Date::now() as u64
120
- }
121
- #[cfg(not(feature = "wasm"))]
122
- {
123
- std::time::SystemTime::now()
124
- .duration_since(std::time::UNIX_EPOCH)
125
- .unwrap_or_default()
126
- .as_millis() as u64
127
- }
128
- }
129
-
130
- fn write_json_string(out: &mut String, s: &str) {
131
- out.push('"');
132
- for c in s.chars() {
133
- match c {
134
- '"' => out.push_str("\\\""),
135
- '\\' => out.push_str("\\\\"),
136
- '\n' => out.push_str("\\n"),
137
- '\r' => out.push_str("\\r"),
138
- '\t' => out.push_str("\\t"),
139
- c if c.is_control() => {
140
- write!(out, "\\u{:04x}", c as u32).unwrap();
141
- }
142
- c => out.push(c),
143
- }
144
- }
145
- out.push('"');
146
- }
147
-
148
- fn millis_to_string(ms: u64) -> String {
149
- #[cfg(feature = "wasm")]
150
- {
151
- let d = js_sys::Date::new(&wasm_bindgen::JsValue::from_f64(ms as f64));
152
- d.to_iso_string().as_string().unwrap_or_else(|| "2026-01-01T00:00:00Z".into())
153
- }
154
- #[cfg(not(feature = "wasm"))]
155
- {
156
- let secs = ms / 1000;
157
- let nanos = ((ms % 1000) * 1_000_000) as u32;
158
- let dt = chrono::DateTime::from_timestamp(secs as i64, nanos)
159
- .unwrap_or_default();
160
- dt.to_rfc3339()
161
- }
162
- }
163
-
164
- impl Table {
165
- pub fn new(name: String, schema: &[(String, FieldSchema)]) -> Self {
166
- let pk_field = schema.iter()
167
- .find(|(_, f)| f.primary_key)
168
- .map(|(name, _)| name.clone());
169
-
170
- let field_order: Vec<String> = schema.iter().map(|(n, _)| n.clone()).collect();
171
- let field_index: HashMap<String, usize> = field_order.iter()
172
- .enumerate().map(|(i, n)| (n.clone(), i)).collect();
173
- let schema: HashMap<String, FieldSchema> = schema.iter()
174
- .map(|(k, v)| (k.clone(), v.clone())).collect();
175
-
176
- Self {
177
- name, schema, field_order, field_index,
178
- rows: Vec::new(), values: Vec::new(), next_id: 1, pk_field,
179
- pk_index: FxHashMap::default(),
180
- }
181
- }
182
-
183
- pub fn to_row_map(&self, values: &[FieldValue]) -> HashMap<String, serde_json::Value> {
184
- let mut map = HashMap::with_capacity(self.field_order.len());
185
- for (i, name) in self.field_order.iter().enumerate() {
186
- if let Some(v) = values.get(i) {
187
- map.insert(name.clone(), v.to_json());
188
- }
189
- }
190
- map
191
- }
192
-
193
- pub fn push_row_values(&mut self, id: u64, values: Vec<FieldValue>, now: u64) {
194
- let num_values = values.len();
195
- let values_start = self.values.len();
196
- self.values.extend(values);
197
- self.rows.push(RowStore { id, values_start, num_values, created_at: now, updated_at: now });
198
- }
199
-
200
- pub fn get_row_values(&self, rs: &RowStore) -> &[FieldValue] {
201
- &self.values[rs.values_start..][..rs.num_values]
202
- }
203
-
204
- pub fn get_row_values_mut(&mut self, rs: &RowStore) -> &mut [FieldValue] {
205
- let start = rs.values_start;
206
- &mut self.values[start..][..rs.num_values]
207
- }
208
-
209
- fn to_row(&self, rs: &RowStore) -> Row {
210
- Row {
211
- id: rs.id,
212
- fields: self.to_row_map(self.get_row_values(rs)),
213
- created_at: Some(millis_to_string(rs.created_at)),
214
- updated_at: Some(millis_to_string(rs.updated_at)),
215
- }
216
- }
217
-
218
- pub fn add_rows(&mut self, fields_batch: Vec<HashMap<String, serde_json::Value>>) -> Vec<u64> {
219
- let now = now_millis();
220
- let batch_len = fields_batch.len();
221
- let mut ids = Vec::with_capacity(batch_len);
222
- self.pk_index.reserve(batch_len);
223
- self.rows.reserve(batch_len);
224
- self.values.reserve(batch_len * self.field_order.len());
225
-
226
- let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
227
- self.schema.get(pk).and_then(|fs| {
228
- if fs.auto_increment { Some(pk.to_string()) } else { None }
229
- })
230
- });
231
-
232
- for mut fields in fields_batch {
233
- let auto_id = self.next_id;
234
- self.next_id += 1;
235
- let mut id = auto_id;
236
-
237
- if let Some(ref pk_key) = pk_auto_key {
238
- match fields.get(pk_key) {
239
- Some(v) if v.is_null() => {
240
- fields.insert(pk_key.clone(), serde_json::Value::Number(auto_id.into()));
241
- }
242
- Some(v) => {
243
- if let Some(n) = v.as_i64() {
244
- if n > 0 {
245
- id = n as u64;
246
- self.next_id = self.next_id.max(id + 1);
247
- }
248
- }
249
- }
250
- None => {
251
- fields.insert(pk_key.clone(), serde_json::Value::Number(auto_id.into()));
252
- }
253
- }
254
- }
255
-
256
- let values_start = self.values.len();
257
- if fields.len() < self.schema.len() {
258
- for name in &self.field_order {
259
- if let Some(v) = fields.remove(name) {
260
- self.values.push(FieldValue::from_json(v));
261
- continue;
262
- }
263
- match self.schema.get(name).and_then(|fs| fs.default.clone()) {
264
- Some(dv) => self.values.push(FieldValue::from_json(dv)),
265
- None => {
266
- if let Some(fs) = self.schema.get(name) {
267
- if !fs.nullable {
268
- self.values.push(default_value(&fs.field_type));
269
- } else {
270
- self.values.push(FieldValue::Null);
271
- }
272
- } else {
273
- self.values.push(FieldValue::Null);
274
- }
275
- }
276
- }
277
- }
278
- } else {
279
- for name in &self.field_order {
280
- self.values.push(fields.remove(name).map(FieldValue::from_json).unwrap_or(FieldValue::Null));
281
- }
282
- }
283
-
284
- let idx = self.rows.len();
285
- self.pk_index.insert(id, idx);
286
- self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
287
- ids.push(id);
288
- }
289
- ids
290
- }
291
-
292
- pub fn add_rows_vec(&mut self, field_names: &[String], batch: Vec<Vec<serde_json::Value>>) -> Vec<u64> {
293
- let batch_fv: Vec<Vec<FieldValue>> = batch.into_iter()
294
- .map(|row| row.into_iter().map(FieldValue::from_json).collect())
295
- .collect();
296
- self.add_rows_vec_fv(field_names, batch_fv)
297
- }
298
-
299
- pub fn add_rows_vec_fv(&mut self, field_names: &[String], batch: Vec<Vec<FieldValue>>) -> Vec<u64> {
300
- let now = now_millis();
301
- let batch_len = batch.len();
302
- let mut ids = Vec::with_capacity(batch_len);
303
- self.pk_index.reserve(batch_len);
304
- self.rows.reserve(batch_len);
305
- self.values.reserve(batch_len * self.field_order.len());
306
-
307
- let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
308
- self.schema.get(pk).and_then(|fs| {
309
- if fs.auto_increment { Some(pk.to_string()) } else { None }
310
- })
311
- });
312
-
313
- // fast path: field_names match field_order
314
- if field_names.len() == self.field_order.len()
315
- && field_names.iter().zip(&self.field_order).all(|(a, b)| a == b)
316
- {
317
- let pk_idx = pk_auto_key.as_ref().and_then(|pk| self.field_index.get(pk.as_str()).copied());
318
- for mut row_vals in batch {
319
- let auto_id = self.next_id;
320
- self.next_id += 1;
321
- let mut id = auto_id;
322
- if row_vals.len() < self.field_order.len() {
323
- row_vals.resize(self.field_order.len(), FieldValue::Null);
324
- }
325
- if let Some(pi) = pk_idx {
326
- if pi < row_vals.len() {
327
- match row_vals[pi] {
328
- FieldValue::Null => row_vals[pi] = FieldValue::Int(auto_id as i64),
329
- FieldValue::Int(e) if e > 0 => {
330
- id = e as u64;
331
- self.next_id = self.next_id.max(id + 1);
332
- }
333
- _ => {}
334
- }
335
- }
336
- }
337
- let values_start = self.values.len();
338
- self.values.append(&mut row_vals);
339
- let idx = self.rows.len();
340
- self.pk_index.insert(id, idx);
341
- self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
342
- ids.push(id);
343
- }
344
- return ids;
345
- }
346
-
347
- // slow path: remap field names
348
- let remap: Vec<Option<usize>> = self.field_order.iter()
349
- .map(|name| field_names.iter().position(|fname| fname == name))
350
- .collect();
351
- let explicit_pk_si = pk_auto_key.as_ref()
352
- .and_then(|pk| field_names.iter().position(|n| n == pk));
353
- let pk_idx = pk_auto_key.as_ref().and_then(|pk| self.field_index.get(pk.as_str()).copied());
354
-
355
- for mut row_vals in batch {
356
- let auto_id = self.next_id;
357
- self.next_id += 1;
358
- let mut id = auto_id;
359
- if let Some(si) = explicit_pk_si {
360
- if let Some(FieldValue::Int(e)) = row_vals.get(si) {
361
- if *e > 0 {
362
- id = *e as u64;
363
- self.next_id = self.next_id.max(id + 1);
364
- }
365
- }
366
- }
367
- let values_start = self.values.len();
368
- for (i, remapped_si) in remap.iter().enumerate() {
369
- if let Some(si) = *remapped_si {
370
- if si < row_vals.len() {
371
- self.values.push(std::mem::take(&mut row_vals[si]));
372
- continue;
373
- }
374
- }
375
- if let Some(ref pk_key) = pk_auto_key {
376
- if self.field_order[i] == *pk_key {
377
- self.values.push(FieldValue::Int(id as i64));
378
- continue;
379
- }
380
- }
381
- if let Some(name) = self.field_order.get(i) {
382
- if let Some(fs) = self.schema.get(name) {
383
- if let Some(ref default) = fs.default {
384
- self.values.push(FieldValue::from_json(default.clone()));
385
- } else if !fs.nullable {
386
- self.values.push(default_value(&fs.field_type));
387
- } else {
388
- self.values.push(FieldValue::Null);
389
- }
390
- } else {
391
- self.values.push(FieldValue::Null);
392
- }
393
- }
394
- }
395
- // 显式传入 null 的主键:回填自动 id,保证 pk 值与行 id 一致
396
- if let Some(pi) = pk_idx {
397
- if let Some(v) = self.values.get_mut(values_start + pi) {
398
- if *v == FieldValue::Null {
399
- *v = FieldValue::Int(id as i64);
400
- }
401
- }
402
- }
403
- let idx = self.rows.len();
404
- self.pk_index.insert(id, idx);
405
- self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
406
- ids.push(id);
407
- }
408
- ids
409
- }
410
-
411
- pub fn row_matches(&self, r: &RowStore, filter: &HashMap<String, serde_json::Value>) -> bool {
412
- let vals = self.get_row_values(r);
413
- filter.iter().all(|(k, v)| {
414
- self.field_index.get(k)
415
- .and_then(|&pos| vals.get(pos))
416
- .map_or(false, |fv| fv.eq_json(v))
417
- })
418
- }
419
-
420
- pub fn get_row(&self, id: u64) -> Option<Row> {
421
- let idx = *self.pk_index.get(&id)?;
422
- self.rows.get(idx).map(|r| self.to_row(r))
423
- }
424
-
425
- pub fn get_row_json(&self, id: u64) -> Option<String> {
426
- let idx = *self.pk_index.get(&id)?;
427
- self.rows.get(idx).map(|r| self.to_row_json_string(r))
428
- }
429
-
430
- pub fn get_rows_json(&self, ids: &[u64]) -> String {
431
- let mut out = String::with_capacity(ids.len() * 128);
432
- out.push('[');
433
- let mut first = true;
434
- for &id in ids {
435
- if let Some(&idx) = self.pk_index.get(&id) {
436
- if let Some(rs) = self.rows.get(idx) {
437
- if !first { out.push(','); }
438
- first = false;
439
- out.push_str(&self.to_row_json_string(rs));
440
- }
441
- }
442
- }
443
- out.push(']');
444
- out
445
- }
446
-
447
- fn to_row_json_compact(&self, rs: &RowStore) -> String {
448
- let vals = self.get_row_values(rs);
449
- let mut out = String::with_capacity(64);
450
- out.push('[');
451
- out.push_str(itoa::Buffer::new().format(rs.id));
452
- for v in vals {
453
- out.push(',');
454
- v.write_json(&mut out);
455
- }
456
- out.push(']');
457
- out
458
- }
459
-
460
- fn to_row_json_string(&self, rs: &RowStore) -> String {
461
- let vals = self.get_row_values(rs);
462
- let mut out = String::with_capacity(160);
463
- out.push_str(r#"{"id":"#);
464
- out.push_str(itoa::Buffer::new().format(rs.id));
465
- out.push_str(r#","fields":{"#);
466
- for (i, name) in self.field_order.iter().enumerate() {
467
- if i > 0 { out.push(','); }
468
- write_json_string(&mut out, name);
469
- out.push(':');
470
- if let Some(v) = vals.get(i) {
471
- v.write_json(&mut out);
472
- } else {
473
- out.push_str("null");
474
- }
475
- }
476
- out.push_str(r#"},"created_at":"#);
477
- write_json_string(&mut out, &millis_to_string(rs.created_at));
478
- out.push_str(r#","updated_at":"#);
479
- write_json_string(&mut out, &millis_to_string(rs.updated_at));
480
- out.push_str("}");
481
- out
482
- }
483
-
484
- pub fn update_row(&mut self, id: u64, data: HashMap<String, serde_json::Value>) -> bool {
485
- let idx = match self.pk_index.get(&id) {
486
- Some(i) => *i,
487
- None => return false,
488
- };
489
- // Use raw index access to update values without borrow conflicts
490
- if idx < self.rows.len() {
491
- let row = &mut self.rows[idx];
492
- let nv = row.num_values;
493
- let s = row.values_start;
494
- for (k, v) in data {
495
- if let Some(&pos) = self.field_index.get(&k) {
496
- if pos < nv {
497
- self.values[s + pos] = FieldValue::from_json(v);
498
- }
499
- }
500
- }
501
- self.rows[idx].updated_at = now_millis();
502
- true
503
- } else {
504
- false
505
- }
506
- }
507
-
508
- pub fn update_rows(&mut self, batch: Vec<(u64, HashMap<String, serde_json::Value>)>) -> usize {
509
- let mut count = 0;
510
- let now = now_millis();
511
- for (id, data) in batch {
512
- if let Some(&idx) = self.pk_index.get(&id) {
513
- if idx < self.rows.len() {
514
- let row = &mut self.rows[idx];
515
- let nv = row.num_values;
516
- let s = row.values_start;
517
- for (k, v) in data {
518
- if let Some(&pos) = self.field_index.get(&k) {
519
- if pos < nv {
520
- self.values[s + pos] = FieldValue::from_json(v);
521
- }
522
- }
523
- }
524
- self.rows[idx].updated_at = now;
525
- count += 1;
526
- }
527
- }
528
- }
529
- count
530
- }
531
-
532
- pub fn remove_row(&mut self, id: u64) -> bool {
533
- let idx = match self.pk_index.get(&id) {
534
- Some(i) => *i,
535
- None => return false,
536
- };
537
- self.pk_index.remove(&id);
538
- let last = self.rows.len() - 1;
539
- if idx != last {
540
- self.pk_index.insert(self.rows[last].id, idx);
541
- }
542
- self.rows.swap_remove(idx);
543
- true
544
- }
545
-
546
- pub fn remove_rows(&mut self, ids: &[u64]) -> usize {
547
- let mut count = 0;
548
- let mut indices: Vec<(usize, u64)> = Vec::with_capacity(ids.len());
549
- for &id in ids {
550
- if let Some(&idx) = self.pk_index.get(&id) {
551
- indices.push((idx, id));
552
- count += 1;
553
- }
554
- }
555
- indices.sort_unstable_by(|a, b| b.0.cmp(&a.0));
556
- for (idx, id) in indices {
557
- self.pk_index.remove(&id);
558
- let last = self.rows.len() - 1;
559
- if idx != last {
560
- self.pk_index.insert(self.rows[last].id, idx);
561
- }
562
- self.rows.swap_remove(idx);
563
- }
564
- count
565
- }
566
-
567
- pub fn remove_row_by_idx(&mut self, idx: usize) {
568
- let last = self.rows.len() - 1;
569
- if idx < last {
570
- let swapped_id = self.rows[last].id;
571
- self.pk_index.insert(swapped_id, idx);
572
- }
573
- self.pk_index.remove(&self.rows[idx].id);
574
- self.rows.swap_remove(idx);
575
- }
576
-
577
- pub fn count(&self) -> usize {
578
- self.rows.len()
579
- }
580
-
581
- // ============================================================
582
- // 磁盘持久化导出 / 导入(供 HybridEngine 使用)
583
- // ============================================================
584
-
585
- pub fn export_all(&self) -> String {
586
- let mut out = String::with_capacity(64 + self.rows.len() * 96);
587
- out.push_str(r#"{"next_id":"#);
588
- out.push_str(itoa::Buffer::new().format(self.next_id));
589
- out.push_str(r#","rows":["#);
590
- for (i, rs) in self.rows.iter().enumerate() {
591
- if i > 0 { out.push(','); }
592
- out.push_str(r#"{"id":"#);
593
- out.push_str(itoa::Buffer::new().format(rs.id));
594
- out.push_str(r#","created_at":"#);
595
- out.push_str(itoa::Buffer::new().format(rs.created_at));
596
- out.push_str(r#","updated_at":"#);
597
- out.push_str(itoa::Buffer::new().format(rs.updated_at));
598
- out.push_str(r#","fields":{"#);
599
- let vals = self.get_row_values(rs);
600
- for (fi, name) in self.field_order.iter().enumerate() {
601
- if fi > 0 { out.push(','); }
602
- write_json_string(&mut out, name);
603
- out.push(':');
604
- if let Some(v) = vals.get(fi) {
605
- v.write_json(&mut out);
606
- } else {
607
- out.push_str("null");
608
- }
609
- }
610
- out.push_str("}}");
611
- }
612
- out.push_str("]}");
613
- out
614
- }
615
-
616
- pub fn import_from_json(&mut self, json: &str) -> Result<(), String> {
617
- let parsed: serde_json::Value = serde_json::from_str(json)
618
- .map_err(|e| format!("invalid table file: {}", e))?;
619
- let next_id = parsed.get("next_id").and_then(|v| v.as_u64()).unwrap_or(1);
620
- self.next_id = next_id;
621
- let rows = parsed.get("rows").and_then(|v| v.as_array()).ok_or("table file missing rows")?;
622
- let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
623
- self.schema.get(pk).and_then(|fs| if fs.auto_increment { Some(pk.to_string()) } else { None })
624
- });
625
- self.rows.reserve(rows.len());
626
- self.values.reserve(rows.len() * self.field_order.len());
627
- self.pk_index.reserve(rows.len());
628
- for rv in rows {
629
- let id = rv.get("id").and_then(|v| v.as_u64()).ok_or("row missing id")?;
630
- let created_at = rv.get("created_at").and_then(|v| v.as_u64()).unwrap_or_else(now_millis);
631
- let updated_at = rv.get("updated_at").and_then(|v| v.as_u64()).unwrap_or(created_at);
632
- let fields = rv.get("fields").and_then(|v| v.as_object()).ok_or("row missing fields")?;
633
- let values_start = self.values.len();
634
- for name in &self.field_order {
635
- if let Some(jv) = fields.get(name) {
636
- self.values.push(FieldValue::from_json(jv.clone()));
637
- } else if let Some(ref pk_key) = pk_auto_key {
638
- if *pk_key == *name {
639
- self.values.push(FieldValue::Int(id as i64));
640
- } else {
641
- self.values.push(FieldValue::Null);
642
- }
643
- } else {
644
- self.values.push(FieldValue::Null);
645
- }
646
- }
647
- let idx = self.rows.len();
648
- self.pk_index.insert(id, idx);
649
- self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at, updated_at });
650
- }
651
- Ok(())
652
- }
653
-
654
- pub fn find_rows(&self, filter: &Option<HashMap<String, serde_json::Value>>,
655
- limit: Option<usize>, offset: Option<usize>,
656
- order_by: &Option<String>, order: &Option<String>) -> Vec<Row> {
657
- let mut results: Vec<&RowStore> = self.rows.iter().collect();
658
-
659
- if let Some(ref f) = filter {
660
- let filter_indices: Vec<(usize, &serde_json::Value)> = f.iter()
661
- .filter_map(|(k, v)| self.field_index.get(k).map(|&pos| (pos, v)))
662
- .collect();
663
- results.retain(|r| {
664
- let vals = self.get_row_values(r);
665
- filter_indices.iter().all(|(pos, v)| {
666
- vals.get(*pos).map_or(false, |fv| fv.eq_json(v))
667
- })
668
- });
669
- }
670
-
671
- if let Some(ref field) = order_by {
672
- let desc = order.as_deref() == Some("desc");
673
- if let Some(&pos) = self.field_index.get(field) {
674
- results.sort_by(|a, b| {
675
- let vals_a = self.get_row_values(a);
676
- let vals_b = self.get_row_values(b);
677
- let cmp = cmp_field_values(vals_a.get(pos), vals_b.get(pos));
678
- if desc { cmp.reverse() } else { cmp }
679
- });
680
- }
681
- }
682
-
683
- let o = offset.unwrap_or(0);
684
- let l = limit.unwrap_or(usize::MAX);
685
- results.into_iter().skip(o).take(l).map(|r| self.to_row(r)).collect()
686
- }
687
- }
688
-
689
- fn cmp_field_values(a: Option<&FieldValue>, b: Option<&FieldValue>) -> std::cmp::Ordering {
690
- match (a, b) {
691
- (Some(FieldValue::Int(na)), Some(FieldValue::Int(nb))) => na.cmp(nb),
692
- (Some(FieldValue::Float(na)), Some(FieldValue::Float(nb))) => na.partial_cmp(nb).unwrap_or(std::cmp::Ordering::Equal),
693
- (Some(FieldValue::String(sa)), Some(FieldValue::String(sb))) => sa.cmp(sb),
694
- (Some(FieldValue::Bool(ba)), Some(FieldValue::Bool(bb))) => ba.cmp(bb),
695
- (Some(a), Some(b)) => format!("{:?}", a).cmp(&format!("{:?}", b)),
696
- (Some(_), None) => std::cmp::Ordering::Greater,
697
- (None, Some(_)) => std::cmp::Ordering::Less,
698
- (None, None) => std::cmp::Ordering::Equal,
699
- }
700
- }
1
+ use std::collections::HashMap;
2
+ use rustc_hash::FxHashMap;
3
+ use serde::Serialize;
4
+ use crate::types::{Row, FieldSchema, FieldType};
5
+ use std::fmt::Write;
6
+
7
+ #[derive(Debug, Clone, PartialEq, Default)]
8
+ pub enum FieldValue {
9
+ #[default]
10
+ Null,
11
+ Bool(bool),
12
+ Int(i64),
13
+ Float(f64),
14
+ String(String),
15
+ }
16
+
17
+ impl serde::Serialize for FieldValue {
18
+ fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
19
+ match self {
20
+ FieldValue::Null => s.serialize_unit(),
21
+ FieldValue::Bool(b) => s.serialize_bool(*b),
22
+ FieldValue::Int(n) => s.serialize_i64(*n),
23
+ FieldValue::Float(f) => s.serialize_f64(*f),
24
+ FieldValue::String(st) => s.serialize_str(st),
25
+ }
26
+ }
27
+ }
28
+
29
+ impl FieldValue {
30
+ pub fn from_json(jv: serde_json::Value) -> Self {
31
+ match jv {
32
+ serde_json::Value::Null => FieldValue::Null,
33
+ serde_json::Value::Bool(b) => FieldValue::Bool(b),
34
+ serde_json::Value::Number(n) => {
35
+ n.as_i64().map(FieldValue::Int)
36
+ .or_else(|| n.as_f64().map(FieldValue::Float))
37
+ .unwrap_or(FieldValue::Null)
38
+ }
39
+ serde_json::Value::String(s) => FieldValue::String(s),
40
+ _ => FieldValue::Null,
41
+ }
42
+ }
43
+
44
+ fn to_json(&self) -> serde_json::Value {
45
+ match self {
46
+ FieldValue::Null => serde_json::Value::Null,
47
+ FieldValue::Bool(b) => serde_json::Value::Bool(*b),
48
+ FieldValue::Int(n) => serde_json::Value::Number((*n).into()),
49
+ FieldValue::Float(f) => serde_json::Number::from_f64(*f)
50
+ .map(serde_json::Value::Number)
51
+ .unwrap_or(serde_json::Value::Null),
52
+ FieldValue::String(s) => serde_json::Value::String(s.clone()),
53
+ }
54
+ }
55
+
56
+ pub fn write_json(&self, out: &mut String) {
57
+ match self {
58
+ FieldValue::Null => out.push_str("null"),
59
+ FieldValue::Bool(true) => out.push_str("true"),
60
+ FieldValue::Bool(false) => out.push_str("false"),
61
+ FieldValue::Int(n) => {
62
+ out.push_str(itoa::Buffer::new().format(*n));
63
+ }
64
+ FieldValue::Float(f) => {
65
+ out.push_str(ryu::Buffer::new().format_finite(*f));
66
+ }
67
+ FieldValue::String(s) => {
68
+ write_json_string(out, s);
69
+ }
70
+ }
71
+ }
72
+
73
+ pub fn eq_json(&self, jv: &serde_json::Value) -> bool {
74
+ match (self, jv) {
75
+ (FieldValue::Null, serde_json::Value::Null) => true,
76
+ (FieldValue::Bool(a), serde_json::Value::Bool(b)) => a == b,
77
+ (FieldValue::Int(a), serde_json::Value::Number(n)) => n.as_i64() == Some(*a),
78
+ (FieldValue::Float(a), serde_json::Value::Number(n)) => n.as_f64() == Some(*a),
79
+ (FieldValue::String(a), serde_json::Value::String(b)) => a == b,
80
+ _ => false,
81
+ }
82
+ }
83
+ }
84
+
85
+ #[derive(Debug, Clone)]
86
+ pub struct RowStore {
87
+ pub id: u64,
88
+ pub values_start: usize,
89
+ pub num_values: usize,
90
+ pub created_at: u64,
91
+ pub updated_at: u64,
92
+ }
93
+
94
+ #[derive(Debug, Clone)]
95
+ pub struct Table {
96
+ pub name: String,
97
+ pub schema: HashMap<String, FieldSchema>,
98
+ pub field_order: Vec<String>,
99
+ pub field_index: HashMap<String, usize>,
100
+ pub rows: Vec<RowStore>,
101
+ pub values: Vec<FieldValue>,
102
+ pub next_id: u64,
103
+ pub pk_field: Option<String>,
104
+ pub pk_index: FxHashMap<u64, usize>,
105
+ }
106
+
107
+ pub fn default_value(ft: &FieldType) -> FieldValue {
108
+ match ft {
109
+ FieldType::Integer => FieldValue::Int(0),
110
+ FieldType::Float => FieldValue::Float(0.0),
111
+ FieldType::String => FieldValue::String(String::new()),
112
+ FieldType::Boolean => FieldValue::Bool(false),
113
+ }
114
+ }
115
+
116
+ fn now_millis() -> u64 {
117
+ #[cfg(feature = "wasm")]
118
+ {
119
+ js_sys::Date::now() as u64
120
+ }
121
+ #[cfg(not(feature = "wasm"))]
122
+ {
123
+ std::time::SystemTime::now()
124
+ .duration_since(std::time::UNIX_EPOCH)
125
+ .unwrap_or_default()
126
+ .as_millis() as u64
127
+ }
128
+ }
129
+
130
+ fn write_json_string(out: &mut String, s: &str) {
131
+ out.push('"');
132
+ for c in s.chars() {
133
+ match c {
134
+ '"' => out.push_str("\\\""),
135
+ '\\' => out.push_str("\\\\"),
136
+ '\n' => out.push_str("\\n"),
137
+ '\r' => out.push_str("\\r"),
138
+ '\t' => out.push_str("\\t"),
139
+ c if c.is_control() => {
140
+ write!(out, "\\u{:04x}", c as u32).unwrap();
141
+ }
142
+ c => out.push(c),
143
+ }
144
+ }
145
+ out.push('"');
146
+ }
147
+
148
+ fn millis_to_string(ms: u64) -> String {
149
+ #[cfg(feature = "wasm")]
150
+ {
151
+ let d = js_sys::Date::new(&wasm_bindgen::JsValue::from_f64(ms as f64));
152
+ d.to_iso_string().as_string().unwrap_or_else(|| "2026-01-01T00:00:00Z".into())
153
+ }
154
+ #[cfg(not(feature = "wasm"))]
155
+ {
156
+ let secs = ms / 1000;
157
+ let nanos = ((ms % 1000) * 1_000_000) as u32;
158
+ let dt = chrono::DateTime::from_timestamp(secs as i64, nanos)
159
+ .unwrap_or_default();
160
+ dt.to_rfc3339()
161
+ }
162
+ }
163
+
164
+ /// 行内时间戳格式化缓存:批量写入的行共享同一个毫秒时间戳,
165
+ /// 命中缓存可避免每行都做一次 chrono/JS Date 格式化与字符串分配。
166
+ #[derive(Default)]
167
+ pub struct TimeCache {
168
+ last_ms: u64,
169
+ last_str: String,
170
+ valid: bool,
171
+ }
172
+
173
+ impl TimeCache {
174
+ pub fn new() -> Self {
175
+ Self { last_ms: 0, last_str: String::new(), valid: false }
176
+ }
177
+
178
+ #[inline]
179
+ fn write(&mut self, out: &mut String, ms: u64) {
180
+ if !self.valid || self.last_ms != ms {
181
+ self.last_str.clear();
182
+ self.last_str.push_str(&millis_to_string(ms));
183
+ self.last_ms = ms;
184
+ self.valid = true;
185
+ }
186
+ out.push_str(&self.last_str);
187
+ }
188
+ }
189
+
190
+ impl Table {
191
+ pub fn new(name: String, schema: &[(String, FieldSchema)]) -> Self {
192
+ let pk_field = schema.iter()
193
+ .find(|(_, f)| f.primary_key)
194
+ .map(|(name, _)| name.clone());
195
+
196
+ let field_order: Vec<String> = schema.iter().map(|(n, _)| n.clone()).collect();
197
+ let field_index: HashMap<String, usize> = field_order.iter()
198
+ .enumerate().map(|(i, n)| (n.clone(), i)).collect();
199
+ let schema: HashMap<String, FieldSchema> = schema.iter()
200
+ .map(|(k, v)| (k.clone(), v.clone())).collect();
201
+
202
+ Self {
203
+ name, schema, field_order, field_index,
204
+ rows: Vec::new(), values: Vec::new(), next_id: 1, pk_field,
205
+ pk_index: FxHashMap::default(),
206
+ }
207
+ }
208
+
209
+ pub fn to_row_map(&self, values: &[FieldValue]) -> HashMap<String, serde_json::Value> {
210
+ let mut map = HashMap::with_capacity(self.field_order.len());
211
+ for (i, name) in self.field_order.iter().enumerate() {
212
+ if let Some(v) = values.get(i) {
213
+ map.insert(name.clone(), v.to_json());
214
+ }
215
+ }
216
+ map
217
+ }
218
+
219
+ pub fn push_row_values(&mut self, id: u64, values: Vec<FieldValue>, now: u64) {
220
+ let num_values = values.len();
221
+ let values_start = self.values.len();
222
+ self.values.extend(values);
223
+ self.rows.push(RowStore { id, values_start, num_values, created_at: now, updated_at: now });
224
+ }
225
+
226
+ pub fn get_row_values(&self, rs: &RowStore) -> &[FieldValue] {
227
+ &self.values[rs.values_start..][..rs.num_values]
228
+ }
229
+
230
+ pub fn get_row_values_mut(&mut self, rs: &RowStore) -> &mut [FieldValue] {
231
+ let start = rs.values_start;
232
+ &mut self.values[start..][..rs.num_values]
233
+ }
234
+
235
+ fn to_row(&self, rs: &RowStore) -> Row {
236
+ Row {
237
+ id: rs.id,
238
+ fields: self.to_row_map(self.get_row_values(rs)),
239
+ created_at: Some(millis_to_string(rs.created_at)),
240
+ updated_at: Some(millis_to_string(rs.updated_at)),
241
+ }
242
+ }
243
+
244
+ /// 运行期加列(供 `db.addColumn` 使用):扩展 schema / field_order / field_index,
245
+ /// 并为已有行按「default → 非空类型默认值 → null」的既有语义回填新列的值。
246
+ /// 返回 true 表示新增成功;false 表示该列已存在(no-op)。
247
+ pub fn add_column(&mut self, name: &str, fs: &FieldSchema) -> bool {
248
+ if self.field_index.contains_key(name) {
249
+ return false;
250
+ }
251
+ let fill = match &fs.default {
252
+ Some(dv) => FieldValue::from_json(dv.clone()),
253
+ None => if !fs.nullable { default_value(&fs.field_type) } else { FieldValue::Null },
254
+ };
255
+ let pos = self.field_order.len();
256
+ self.schema.insert(name.to_string(), fs.clone());
257
+ self.field_order.push(name.to_string());
258
+ self.field_index.insert(name.to_string(), pos);
259
+ for i in 0..self.rows.len() {
260
+ let start = self.rows[i].values_start + self.rows[i].num_values;
261
+ self.values.insert(start, fill.clone());
262
+ self.rows[i].num_values += 1;
263
+ }
264
+ true
265
+ }
266
+
267
+ pub fn add_rows(&mut self, fields_batch: Vec<HashMap<String, serde_json::Value>>) -> Vec<u64> {
268
+ let now = now_millis();
269
+ let batch_len = fields_batch.len();
270
+ let mut ids = Vec::with_capacity(batch_len);
271
+ self.pk_index.reserve(batch_len);
272
+ self.rows.reserve(batch_len);
273
+ self.values.reserve(batch_len * self.field_order.len());
274
+
275
+ let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
276
+ self.schema.get(pk).and_then(|fs| {
277
+ if fs.auto_increment { Some(pk.to_string()) } else { None }
278
+ })
279
+ });
280
+
281
+ for mut fields in fields_batch {
282
+ let auto_id = self.next_id;
283
+ self.next_id += 1;
284
+ let mut id = auto_id;
285
+
286
+ if let Some(ref pk_key) = pk_auto_key {
287
+ match fields.get(pk_key) {
288
+ Some(v) if v.is_null() => {
289
+ fields.insert(pk_key.clone(), serde_json::Value::Number(auto_id.into()));
290
+ }
291
+ Some(v) => {
292
+ if let Some(n) = v.as_i64() {
293
+ if n > 0 {
294
+ id = n as u64;
295
+ self.next_id = self.next_id.max(id + 1);
296
+ }
297
+ }
298
+ }
299
+ None => {
300
+ fields.insert(pk_key.clone(), serde_json::Value::Number(auto_id.into()));
301
+ }
302
+ }
303
+ }
304
+
305
+ let values_start = self.values.len();
306
+ if fields.len() < self.schema.len() {
307
+ for name in &self.field_order {
308
+ if let Some(v) = fields.remove(name) {
309
+ self.values.push(FieldValue::from_json(v));
310
+ continue;
311
+ }
312
+ match self.schema.get(name).and_then(|fs| fs.default.clone()) {
313
+ Some(dv) => self.values.push(FieldValue::from_json(dv)),
314
+ None => {
315
+ if let Some(fs) = self.schema.get(name) {
316
+ if !fs.nullable {
317
+ self.values.push(default_value(&fs.field_type));
318
+ } else {
319
+ self.values.push(FieldValue::Null);
320
+ }
321
+ } else {
322
+ self.values.push(FieldValue::Null);
323
+ }
324
+ }
325
+ }
326
+ }
327
+ } else {
328
+ for name in &self.field_order {
329
+ self.values.push(fields.remove(name).map(FieldValue::from_json).unwrap_or(FieldValue::Null));
330
+ }
331
+ }
332
+
333
+ let idx = self.rows.len();
334
+ self.pk_index.insert(id, idx);
335
+ self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
336
+ ids.push(id);
337
+ }
338
+ ids
339
+ }
340
+
341
+ pub fn add_rows_vec(&mut self, field_names: &[String], batch: Vec<Vec<serde_json::Value>>) -> Vec<u64> {
342
+ let batch_fv: Vec<Vec<FieldValue>> = batch.into_iter()
343
+ .map(|row| row.into_iter().map(FieldValue::from_json).collect())
344
+ .collect();
345
+ self.add_rows_vec_fv(field_names, batch_fv)
346
+ }
347
+
348
+ pub fn add_rows_vec_fv(&mut self, field_names: &[String], batch: Vec<Vec<FieldValue>>) -> Vec<u64> {
349
+ let now = now_millis();
350
+ let batch_len = batch.len();
351
+ let mut ids = Vec::with_capacity(batch_len);
352
+ self.pk_index.reserve(batch_len);
353
+ self.rows.reserve(batch_len);
354
+ self.values.reserve(batch_len * self.field_order.len());
355
+
356
+ let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
357
+ self.schema.get(pk).and_then(|fs| {
358
+ if fs.auto_increment { Some(pk.to_string()) } else { None }
359
+ })
360
+ });
361
+
362
+ // fast path: field_names match field_order
363
+ if field_names.len() == self.field_order.len()
364
+ && field_names.iter().zip(&self.field_order).all(|(a, b)| a == b)
365
+ {
366
+ let pk_idx = pk_auto_key.as_ref().and_then(|pk| self.field_index.get(pk.as_str()).copied());
367
+ for mut row_vals in batch {
368
+ let auto_id = self.next_id;
369
+ self.next_id += 1;
370
+ let mut id = auto_id;
371
+ if row_vals.len() < self.field_order.len() {
372
+ row_vals.resize(self.field_order.len(), FieldValue::Null);
373
+ }
374
+ if let Some(pi) = pk_idx {
375
+ if pi < row_vals.len() {
376
+ match row_vals[pi] {
377
+ FieldValue::Null => row_vals[pi] = FieldValue::Int(auto_id as i64),
378
+ FieldValue::Int(e) if e > 0 => {
379
+ id = e as u64;
380
+ self.next_id = self.next_id.max(id + 1);
381
+ }
382
+ _ => {}
383
+ }
384
+ }
385
+ }
386
+ let values_start = self.values.len();
387
+ self.values.append(&mut row_vals);
388
+ let idx = self.rows.len();
389
+ self.pk_index.insert(id, idx);
390
+ self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
391
+ ids.push(id);
392
+ }
393
+ return ids;
394
+ }
395
+
396
+ // slow path: remap field names
397
+ let remap: Vec<Option<usize>> = self.field_order.iter()
398
+ .map(|name| field_names.iter().position(|fname| fname == name))
399
+ .collect();
400
+ let explicit_pk_si = pk_auto_key.as_ref()
401
+ .and_then(|pk| field_names.iter().position(|n| n == pk));
402
+ let pk_idx = pk_auto_key.as_ref().and_then(|pk| self.field_index.get(pk.as_str()).copied());
403
+
404
+ for mut row_vals in batch {
405
+ let auto_id = self.next_id;
406
+ self.next_id += 1;
407
+ let mut id = auto_id;
408
+ if let Some(si) = explicit_pk_si {
409
+ if let Some(FieldValue::Int(e)) = row_vals.get(si) {
410
+ if *e > 0 {
411
+ id = *e as u64;
412
+ self.next_id = self.next_id.max(id + 1);
413
+ }
414
+ }
415
+ }
416
+ let values_start = self.values.len();
417
+ for (i, remapped_si) in remap.iter().enumerate() {
418
+ if let Some(si) = *remapped_si {
419
+ if si < row_vals.len() {
420
+ self.values.push(std::mem::take(&mut row_vals[si]));
421
+ continue;
422
+ }
423
+ }
424
+ if let Some(ref pk_key) = pk_auto_key {
425
+ if self.field_order[i] == *pk_key {
426
+ self.values.push(FieldValue::Int(id as i64));
427
+ continue;
428
+ }
429
+ }
430
+ if let Some(name) = self.field_order.get(i) {
431
+ if let Some(fs) = self.schema.get(name) {
432
+ if let Some(ref default) = fs.default {
433
+ self.values.push(FieldValue::from_json(default.clone()));
434
+ } else if !fs.nullable {
435
+ self.values.push(default_value(&fs.field_type));
436
+ } else {
437
+ self.values.push(FieldValue::Null);
438
+ }
439
+ } else {
440
+ self.values.push(FieldValue::Null);
441
+ }
442
+ }
443
+ }
444
+ // 显式传入 null 的主键:回填自动 id,保证 pk 值与行 id 一致
445
+ if let Some(pi) = pk_idx {
446
+ if let Some(v) = self.values.get_mut(values_start + pi) {
447
+ if *v == FieldValue::Null {
448
+ *v = FieldValue::Int(id as i64);
449
+ }
450
+ }
451
+ }
452
+ let idx = self.rows.len();
453
+ self.pk_index.insert(id, idx);
454
+ self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
455
+ ids.push(id);
456
+ }
457
+ ids
458
+ }
459
+
460
+ pub fn row_matches(&self, r: &RowStore, filter: &HashMap<String, serde_json::Value>) -> bool {
461
+ let conds = self.parse_filter(filter);
462
+ matches_filter(self.get_row_values(r), &conds)
463
+ }
464
+
465
+ /// 预解析过滤条件:字段名 → 行内下标 + 条件,避免逐行重复做 HashMap 查找。
466
+ /// 支持字段级操作符($eq/$ne/$gt/$gte/$lt/$lte/$in/$nin/$between/$exists/$like/$regex),
467
+ /// 一个字段多个操作符(如 {$gte:18,$lte:65})会展开成多条,需同时满足。
468
+ pub(crate) fn parse_filter<'a>(&self, filter: &'a HashMap<String, serde_json::Value>)
469
+ -> Vec<(usize, Cond<'a>)>
470
+ {
471
+ let mut out = Vec::with_capacity(filter.len());
472
+ for (k, v) in filter {
473
+ // 未知字段用哨兵下标,按「字段不存在」参与匹配(不再静默放行)
474
+ let pos = self.field_index.get(k).copied().unwrap_or(ABSENT_FIELD);
475
+ match v {
476
+ serde_json::Value::Object(map)
477
+ if !map.is_empty() && map.keys().all(|kk| kk.starts_with('$')) =>
478
+ {
479
+ for (op, target) in map {
480
+ out.push((pos, parse_op(op, target)));
481
+ }
482
+ }
483
+ other => out.push((pos, Cond::Eq(other))),
484
+ }
485
+ }
486
+ out
487
+ }
488
+
489
+ pub fn get_row(&self, id: u64) -> Option<Row> {
490
+ let idx = *self.pk_index.get(&id)?;
491
+ self.rows.get(idx).map(|r| self.to_row(r))
492
+ }
493
+
494
+ pub fn get_row_json(&self, id: u64) -> Option<String> {
495
+ let idx = *self.pk_index.get(&id)?;
496
+ self.rows.get(idx).map(|r| self.to_row_json_string(r))
497
+ }
498
+
499
+ pub fn get_rows_json(&self, ids: &[u64]) -> String {
500
+ let mut out = String::with_capacity(ids.len() * 128);
501
+ let mut tc = TimeCache::new();
502
+ out.push('[');
503
+ let mut first = true;
504
+ for &id in ids {
505
+ if let Some(&idx) = self.pk_index.get(&id) {
506
+ if let Some(rs) = self.rows.get(idx) {
507
+ if !first { out.push(','); }
508
+ first = false;
509
+ self.write_row_json_into(rs, &mut out, &mut tc);
510
+ }
511
+ }
512
+ }
513
+ out.push(']');
514
+ out
515
+ }
516
+
517
+ fn to_row_json_compact(&self, rs: &RowStore) -> String {
518
+ let vals = self.get_row_values(rs);
519
+ let mut out = String::with_capacity(64);
520
+ out.push('[');
521
+ out.push_str(itoa::Buffer::new().format(rs.id));
522
+ for v in vals {
523
+ out.push(',');
524
+ v.write_json(&mut out);
525
+ }
526
+ out.push(']');
527
+ out
528
+ }
529
+
530
+ fn to_row_json_string(&self, rs: &RowStore) -> String {
531
+ let mut out = String::with_capacity(160);
532
+ let mut tc = TimeCache::new();
533
+ self.write_row_json_into(rs, &mut out, &mut tc);
534
+ out
535
+ }
536
+
537
+ /// 直接把一行写成 JSON 追加到 `out`。
538
+ /// 相比先构造 `Row`(HashMap<String, Value>)再 serde 序列化,
539
+ /// 省掉了每个字段一次的字符串分配与一次完整的二次序列化。
540
+ pub fn write_row_json_into(&self, rs: &RowStore, out: &mut String, tc: &mut TimeCache) {
541
+ let vals = self.get_row_values(rs);
542
+ out.push_str(r#"{"id":"#);
543
+ out.push_str(itoa::Buffer::new().format(rs.id));
544
+ out.push_str(r#","fields":{"#);
545
+ for (i, name) in self.field_order.iter().enumerate() {
546
+ if i > 0 { out.push(','); }
547
+ write_json_string(out, name);
548
+ out.push(':');
549
+ match vals.get(i) {
550
+ Some(v) => v.write_json(out),
551
+ None => out.push_str("null"),
552
+ }
553
+ }
554
+ out.push_str(r#"},"created_at":""#);
555
+ tc.write(out, rs.created_at);
556
+ out.push_str(r#"","updated_at":""#);
557
+ tc.write(out, rs.updated_at);
558
+ out.push_str(r#""}"#);
559
+ }
560
+
561
+ pub fn update_row(&mut self, id: u64, data: HashMap<String, serde_json::Value>) -> bool {
562
+ self.update_row_ref(id, &data)
563
+ }
564
+
565
+ /// 借用版本:批量按 filter 更新时复用同一份 data,避免每行都克隆整个 HashMap。
566
+ pub fn update_row_ref(&mut self, id: u64, data: &HashMap<String, serde_json::Value>) -> bool {
567
+ let idx = match self.pk_index.get(&id) {
568
+ Some(i) => *i,
569
+ None => return false,
570
+ };
571
+ // Use raw index access to update values without borrow conflicts
572
+ if idx < self.rows.len() {
573
+ let row = &mut self.rows[idx];
574
+ let nv = row.num_values;
575
+ let s = row.values_start;
576
+ for (k, v) in data {
577
+ if let Some(&pos) = self.field_index.get(k) {
578
+ if pos < nv {
579
+ self.values[s + pos] = FieldValue::from_json(v.clone());
580
+ }
581
+ }
582
+ }
583
+ self.rows[idx].updated_at = now_millis();
584
+ true
585
+ } else {
586
+ false
587
+ }
588
+ }
589
+
590
+ pub fn update_rows(&mut self, batch: Vec<(u64, HashMap<String, serde_json::Value>)>) -> usize {
591
+ let mut count = 0;
592
+ let now = now_millis();
593
+ for (id, data) in batch {
594
+ if let Some(&idx) = self.pk_index.get(&id) {
595
+ if idx < self.rows.len() {
596
+ let row = &mut self.rows[idx];
597
+ let nv = row.num_values;
598
+ let s = row.values_start;
599
+ for (k, v) in data {
600
+ if let Some(&pos) = self.field_index.get(&k) {
601
+ if pos < nv {
602
+ self.values[s + pos] = FieldValue::from_json(v);
603
+ }
604
+ }
605
+ }
606
+ self.rows[idx].updated_at = now;
607
+ count += 1;
608
+ }
609
+ }
610
+ }
611
+ count
612
+ }
613
+
614
+ pub fn remove_row(&mut self, id: u64) -> bool {
615
+ let idx = match self.pk_index.get(&id) {
616
+ Some(i) => *i,
617
+ None => return false,
618
+ };
619
+ self.pk_index.remove(&id);
620
+ let last = self.rows.len() - 1;
621
+ if idx != last {
622
+ self.pk_index.insert(self.rows[last].id, idx);
623
+ }
624
+ self.rows.swap_remove(idx);
625
+ true
626
+ }
627
+
628
+ pub fn remove_rows(&mut self, ids: &[u64]) -> usize {
629
+ let mut count = 0;
630
+ let mut indices: Vec<(usize, u64)> = Vec::with_capacity(ids.len());
631
+ for &id in ids {
632
+ if let Some(&idx) = self.pk_index.get(&id) {
633
+ indices.push((idx, id));
634
+ count += 1;
635
+ }
636
+ }
637
+ indices.sort_unstable_by(|a, b| b.0.cmp(&a.0));
638
+ for (idx, id) in indices {
639
+ self.pk_index.remove(&id);
640
+ let last = self.rows.len() - 1;
641
+ if idx != last {
642
+ self.pk_index.insert(self.rows[last].id, idx);
643
+ }
644
+ self.rows.swap_remove(idx);
645
+ }
646
+ count
647
+ }
648
+
649
+ pub fn remove_row_by_idx(&mut self, idx: usize) {
650
+ let last = self.rows.len() - 1;
651
+ if idx < last {
652
+ let swapped_id = self.rows[last].id;
653
+ self.pk_index.insert(swapped_id, idx);
654
+ }
655
+ self.pk_index.remove(&self.rows[idx].id);
656
+ self.rows.swap_remove(idx);
657
+ }
658
+
659
+ pub fn count(&self) -> usize {
660
+ self.rows.len()
661
+ }
662
+
663
+ // ============================================================
664
+ // 磁盘持久化导出 / 导入(供 HybridEngine 使用)
665
+ // ============================================================
666
+
667
+ pub fn export_all(&self) -> String {
668
+ let mut out = String::with_capacity(64 + self.rows.len() * 96);
669
+ out.push_str(r#"{"next_id":"#);
670
+ out.push_str(itoa::Buffer::new().format(self.next_id));
671
+ out.push_str(r#","rows":["#);
672
+ for (i, rs) in self.rows.iter().enumerate() {
673
+ if i > 0 { out.push(','); }
674
+ out.push_str(r#"{"id":"#);
675
+ out.push_str(itoa::Buffer::new().format(rs.id));
676
+ out.push_str(r#","created_at":"#);
677
+ out.push_str(itoa::Buffer::new().format(rs.created_at));
678
+ out.push_str(r#","updated_at":"#);
679
+ out.push_str(itoa::Buffer::new().format(rs.updated_at));
680
+ out.push_str(r#","fields":{"#);
681
+ let vals = self.get_row_values(rs);
682
+ for (fi, name) in self.field_order.iter().enumerate() {
683
+ if fi > 0 { out.push(','); }
684
+ write_json_string(&mut out, name);
685
+ out.push(':');
686
+ if let Some(v) = vals.get(fi) {
687
+ v.write_json(&mut out);
688
+ } else {
689
+ out.push_str("null");
690
+ }
691
+ }
692
+ out.push_str("}}");
693
+ }
694
+ out.push_str("]}");
695
+ out
696
+ }
697
+
698
+ pub fn import_from_json(&mut self, json: &str) -> Result<(), String> {
699
+ let parsed: serde_json::Value = serde_json::from_str(json)
700
+ .map_err(|e| format!("invalid table file: {}", e))?;
701
+ let next_id = parsed.get("next_id").and_then(|v| v.as_u64()).unwrap_or(1);
702
+ self.next_id = next_id;
703
+ let rows = parsed.get("rows").and_then(|v| v.as_array()).ok_or("table file missing rows")?;
704
+ let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
705
+ self.schema.get(pk).and_then(|fs| if fs.auto_increment { Some(pk.to_string()) } else { None })
706
+ });
707
+ self.rows.reserve(rows.len());
708
+ self.values.reserve(rows.len() * self.field_order.len());
709
+ self.pk_index.reserve(rows.len());
710
+ for rv in rows {
711
+ let id = rv.get("id").and_then(|v| v.as_u64()).ok_or("row missing id")?;
712
+ let created_at = rv.get("created_at").and_then(|v| v.as_u64()).unwrap_or_else(now_millis);
713
+ let updated_at = rv.get("updated_at").and_then(|v| v.as_u64()).unwrap_or(created_at);
714
+ let fields = rv.get("fields").and_then(|v| v.as_object()).ok_or("row missing fields")?;
715
+ let values_start = self.values.len();
716
+ for name in &self.field_order {
717
+ if let Some(jv) = fields.get(name) {
718
+ self.values.push(FieldValue::from_json(jv.clone()));
719
+ } else if let Some(ref pk_key) = pk_auto_key {
720
+ if *pk_key == *name {
721
+ self.values.push(FieldValue::Int(id as i64));
722
+ } else {
723
+ self.values.push(FieldValue::Null);
724
+ }
725
+ } else {
726
+ self.values.push(FieldValue::Null);
727
+ }
728
+ }
729
+ let idx = self.rows.len();
730
+ self.pk_index.insert(id, idx);
731
+ self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at, updated_at });
732
+ }
733
+ Ok(())
734
+ }
735
+
736
+ /// 与 `find_rows` 语义一致,但直接把结果写成 JSON 字符串。
737
+ /// - 无排序时流式扫描,凑够 limit 立刻停止,不再先收集全表行指针
738
+ /// - 命中 limit 后仍多探一行,用于判断是否还有下一页(has_more)
739
+ /// 返回 (json, has_more)
740
+ pub fn find_rows_json(&self,
741
+ filter: &Option<HashMap<String, serde_json::Value>>,
742
+ limit: Option<usize>, offset: Option<usize>,
743
+ order_by: &Option<String>, order: &Option<String>) -> (String, bool) {
744
+ let o = offset.unwrap_or(0);
745
+ let cap = limit.unwrap_or(usize::MAX);
746
+ let conds = filter.as_ref().map(|f| self.parse_filter(f)).unwrap_or_default();
747
+
748
+ let mut out = String::with_capacity(128);
749
+ out.push('[');
750
+ let mut written = 0usize;
751
+ let mut has_more = false;
752
+ let mut tc = TimeCache::new();
753
+
754
+ let ordered = order_by.as_ref().and_then(|f| {
755
+ self.field_index.get(f).map(|&p| (p, order.as_deref() == Some("desc")))
756
+ });
757
+
758
+ match ordered {
759
+ Some((pos, desc)) => {
760
+ // 排序路径必须先收集全部匹配行
761
+ let mut matched: Vec<&RowStore> = self.rows.iter()
762
+ .filter(|r| matches_filter(self.get_row_values(r), &conds))
763
+ .collect();
764
+ matched.sort_by(|a, b| {
765
+ let cmp = cmp_field_values(
766
+ self.get_row_values(a).get(pos),
767
+ self.get_row_values(b).get(pos),
768
+ );
769
+ if desc { cmp.reverse() } else { cmp }
770
+ });
771
+ has_more = limit.is_some() && matched.len().saturating_sub(o) > cap;
772
+ for r in matched.into_iter().skip(o).take(cap) {
773
+ if written > 0 { out.push(','); }
774
+ self.write_row_json_into(r, &mut out, &mut tc);
775
+ written += 1;
776
+ }
777
+ }
778
+ None => {
779
+ let mut skipped = 0usize;
780
+ for r in &self.rows {
781
+ if !matches_filter(self.get_row_values(r), &conds) { continue; }
782
+ if skipped < o { skipped += 1; continue; }
783
+ if written >= cap { has_more = true; break; }
784
+ if written > 0 { out.push(','); }
785
+ self.write_row_json_into(r, &mut out, &mut tc);
786
+ written += 1;
787
+ }
788
+ }
789
+ }
790
+
791
+ out.push(']');
792
+ (out, has_more)
793
+ }
794
+
795
+ pub fn find_rows(&self, filter: &Option<HashMap<String, serde_json::Value>>,
796
+ limit: Option<usize>, offset: Option<usize>,
797
+ order_by: &Option<String>, order: &Option<String>) -> Vec<Row> {
798
+ let o = offset.unwrap_or(0);
799
+ let l = limit.unwrap_or(usize::MAX);
800
+ let conds = filter.as_ref().map(|f| self.parse_filter(f)).unwrap_or_default();
801
+
802
+ if let Some(field) = order_by.as_ref().filter(|f| self.field_index.contains_key(*f)) {
803
+ let pos = self.field_index[field];
804
+ let desc = order.as_deref() == Some("desc");
805
+ let mut results: Vec<&RowStore> = self.rows.iter()
806
+ .filter(|r| matches_filter(self.get_row_values(r), &conds))
807
+ .collect();
808
+ results.sort_by(|a, b| {
809
+ let cmp = cmp_field_values(
810
+ self.get_row_values(a).get(pos),
811
+ self.get_row_values(b).get(pos),
812
+ );
813
+ if desc { cmp.reverse() } else { cmp }
814
+ });
815
+ return results.into_iter().skip(o).take(l).map(|r| self.to_row(r)).collect();
816
+ }
817
+
818
+ // 无排序:流式扫描,凑够 limit 立刻停止,不先收集全表行指针
819
+ let mut out = Vec::with_capacity(l.min(self.rows.len()));
820
+ let mut skipped = 0usize;
821
+ for r in &self.rows {
822
+ if !matches_filter(self.get_row_values(r), &conds) { continue; }
823
+ if skipped < o { skipped += 1; continue; }
824
+ if out.len() >= l { break; }
825
+ out.push(self.to_row(r));
826
+ }
827
+ out
828
+ }
829
+ }
830
+
831
+ /// 过滤条件里出现 schema 之外的字段时用它作为哨兵下标:
832
+ /// `vals.get(SENTINEL)` 恒为 None,等价于「该字段不存在」,
833
+ /// 于是 {$exists:false}/{$ne:x} 等语义与 lib/table.js 的 `_matchRow` 一致,
834
+ /// 而 `{noSuchField: 5}` 会正确地匹配 0 行(旧实现会静默放行全部行)。
835
+ const ABSENT_FIELD: usize = usize::MAX;
836
+
837
+ /// 单字段查询条件(字段名已在解析阶段降为行内下标)
838
+ pub(crate) enum Cond<'a> {
839
+ Eq(&'a serde_json::Value),
840
+ Ne(&'a serde_json::Value),
841
+ Gt(&'a serde_json::Value),
842
+ Gte(&'a serde_json::Value),
843
+ Lt(&'a serde_json::Value),
844
+ Lte(&'a serde_json::Value),
845
+ In(&'a serde_json::Value),
846
+ Nin(&'a serde_json::Value),
847
+ /// 闭区间 [lo, hi]
848
+ Between(&'a serde_json::Value),
849
+ Exists(bool),
850
+ /// `$like`:SQL 风格通配(`%` = 任意多字符,`_` = 单字符),大小写不敏感。
851
+ /// 模式在解析阶段编译一次,逐行只做匹配。
852
+ Like(Box<regex_lite::Regex>),
853
+ /// `$regex`:JavaScript 风格正则,大小写敏感。
854
+ Regex(Box<regex_lite::Regex>),
855
+ /// 不认识的写法:永不匹配(与旧的等值语义一致,避免静默放行全部行)
856
+ Never,
857
+ }
858
+
859
+ /// 解析单个操作符。语义对齐 lib/table.js 的 `_matchOperator`。
860
+ pub(crate) fn parse_op<'a>(op: &str, target: &'a serde_json::Value) -> Cond<'a> {
861
+ match op {
862
+ "$eq" => Cond::Eq(target),
863
+ "$ne" => Cond::Ne(target),
864
+ "$gt" => Cond::Gt(target),
865
+ "$gte" => Cond::Gte(target),
866
+ "$lt" => Cond::Lt(target),
867
+ "$lte" => Cond::Lte(target),
868
+ "$in" => Cond::In(target),
869
+ "$nin" => Cond::Nin(target),
870
+ "$between" => Cond::Between(target),
871
+ "$exists" => Cond::Exists(target.as_bool().unwrap_or(true)),
872
+ // $like / $regex 目标必须是字符串;非法或编译失败的模式退化为「永不匹配」。
873
+ // regex-lite 为线性时间引擎,不存在灾难性回溯,无需 JS 侧 isSafeRegex 的长度拦截。
874
+ "$like" => match target.as_str() {
875
+ Some(p) => regex_lite::Regex::new(&like_to_regex(p))
876
+ .map(|re| Cond::Like(Box::new(re)))
877
+ .unwrap_or(Cond::Never),
878
+ None => Cond::Never,
879
+ },
880
+ "$regex" => match target.as_str() {
881
+ Some(p) => regex_lite::Regex::new(p)
882
+ .map(|re| Cond::Regex(Box::new(re)))
883
+ .unwrap_or(Cond::Never),
884
+ None => Cond::Never,
885
+ },
886
+ _ => Cond::Never,
887
+ }
888
+ }
889
+
890
+ /// 把 SQL LIKE 模式转成锚定的、大小写不敏感的正则:
891
+ /// 先转义正则元字符,再把 `%` 视作 `.*`、`_` 视作 `.`,
892
+ /// 与 lib/table.js 的 `$like` 实现逐字符对齐。
893
+ fn like_to_regex(pattern: &str) -> String {
894
+ let mut out = String::with_capacity(pattern.len() + 8);
895
+ out.push_str("(?i)^");
896
+ for ch in pattern.chars() {
897
+ match ch {
898
+ '%' => out.push_str(".*"),
899
+ '_' => out.push('.'),
900
+ '.' | '*' | '+' | '?' | '^' | '$' | '{' | '}' | '(' | ')' | '|' | '[' | ']' | '\\' => {
901
+ out.push('\\');
902
+ out.push(ch);
903
+ }
904
+ c => out.push(c),
905
+ }
906
+ }
907
+ out.push('$');
908
+ out
909
+ }
910
+
911
+ /// 把存储值转成 JS `String(value)` 的等价字符串(供 `$regex` 语义使用)。
912
+ fn field_value_to_string(v: &FieldValue) -> std::borrow::Cow<'_, str> {
913
+ use std::borrow::Cow;
914
+ match v {
915
+ FieldValue::Null => Cow::Borrowed("null"),
916
+ FieldValue::Bool(b) => Cow::Owned(b.to_string()),
917
+ FieldValue::Int(n) => Cow::Owned(n.to_string()),
918
+ FieldValue::Float(f) => Cow::Owned(f.to_string()),
919
+ FieldValue::String(s) => Cow::Borrowed(s),
920
+ }
921
+ }
922
+
923
+ #[inline]
924
+ pub(crate) fn matches_filter(vals: &[FieldValue], conds: &[(usize, Cond)]) -> bool {
925
+ // 最常见形态(单字段等值,如按主键点查)走直通,避免逐行做枚举分派
926
+ if let [(pos, Cond::Eq(t))] = conds {
927
+ return vals.get(*pos).map_or(false, |fv| fv.eq_json(t));
928
+ }
929
+ conds.iter().all(|(pos, c)| match_cond(vals.get(*pos), c))
930
+ }
931
+
932
+ #[inline]
933
+ fn match_cond(fv: Option<&FieldValue>, cond: &Cond) -> bool {
934
+ use std::cmp::Ordering;
935
+ let cmp = |t: &serde_json::Value, want: fn(Ordering) -> bool| {
936
+ fv.and_then(|v| cmp_to_json(v, t)).map_or(false, want)
937
+ };
938
+ match cond {
939
+ Cond::Never => false,
940
+ Cond::Exists(want) => fv.is_some() == *want,
941
+ Cond::Eq(t) => fv.map_or(false, |v| v.eq_json(t)),
942
+ Cond::Ne(t) => !fv.map_or(false, |v| v.eq_json(t)),
943
+ Cond::In(t) => match t.as_array() {
944
+ Some(arr) => fv.map_or(false, |v| arr.iter().any(|e| v.eq_json(e))),
945
+ None => false,
946
+ },
947
+ Cond::Nin(t) => match t.as_array() {
948
+ Some(arr) => !fv.map_or(false, |v| arr.iter().any(|e| v.eq_json(e))),
949
+ None => false,
950
+ },
951
+ Cond::Gt(t) => cmp(t, |o| o == Ordering::Greater),
952
+ Cond::Gte(t) => cmp(t, |o| o != Ordering::Less),
953
+ Cond::Lt(t) => cmp(t, |o| o == Ordering::Less),
954
+ Cond::Lte(t) => cmp(t, |o| o != Ordering::Greater),
955
+ Cond::Between(t) => match t.as_array() {
956
+ Some(arr) if arr.len() == 2 => {
957
+ cmp(&arr[0], |o| o != Ordering::Less) && cmp(&arr[1], |o| o != Ordering::Greater)
958
+ }
959
+ _ => false,
960
+ },
961
+ // $like 只对字符串字段生效(对齐 lib/table.js:非字符串直接 false)
962
+ Cond::Like(re) => matches!(fv, Some(FieldValue::String(s)) if re.is_match(s)),
963
+ // $regex 先把值字符串化再匹配(对齐 lib/table.js 的 String(value))
964
+ Cond::Regex(re) => fv.map_or(false, |v| re.is_match(&field_value_to_string(v))),
965
+ }
966
+ }
967
+
968
+ /// 存储值与 JSON 目标值比较;类型不可比或含 null/NaN 时返回 None(视为不匹配,
969
+ /// 对齐 JS 里 `value !== null && value > expected` 的写法)。
970
+ fn cmp_to_json(fv: &FieldValue, jv: &serde_json::Value) -> Option<std::cmp::Ordering> {
971
+ use serde_json::Value as J;
972
+ match (fv, jv) {
973
+ (FieldValue::Null, _) | (_, J::Null) => None,
974
+ (FieldValue::Int(a), J::Number(n)) => match n.as_i64() {
975
+ Some(b) => Some(a.cmp(&b)),
976
+ None => n.as_f64().and_then(|b| (*a as f64).partial_cmp(&b)),
977
+ },
978
+ (FieldValue::Float(a), J::Number(n)) => n.as_f64().and_then(|b| a.partial_cmp(&b)),
979
+ (FieldValue::Bool(a), J::Bool(b)) => Some(a.cmp(b)),
980
+ (FieldValue::String(a), J::String(b)) => Some(a.as_str().cmp(b.as_str())),
981
+ _ => None,
982
+ }
983
+ }
984
+
985
+ fn cmp_field_values(a: Option<&FieldValue>, b: Option<&FieldValue>) -> std::cmp::Ordering {
986
+ match (a, b) {
987
+ (Some(FieldValue::Int(na)), Some(FieldValue::Int(nb))) => na.cmp(nb),
988
+ (Some(FieldValue::Float(na)), Some(FieldValue::Float(nb))) => na.partial_cmp(nb).unwrap_or(std::cmp::Ordering::Equal),
989
+ (Some(FieldValue::String(sa)), Some(FieldValue::String(sb))) => sa.cmp(sb),
990
+ (Some(FieldValue::Bool(ba)), Some(FieldValue::Bool(bb))) => ba.cmp(bb),
991
+ (Some(a), Some(b)) => format!("{:?}", a).cmp(&format!("{:?}", b)),
992
+ (Some(_), None) => std::cmp::Ordering::Greater,
993
+ (None, Some(_)) => std::cmp::Ordering::Less,
994
+ (None, None) => std::cmp::Ordering::Equal,
995
+ }
996
+ }