jsql-neo 5.4.2 → 6.0.0-beta2

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 (39) hide show
  1. package/README.md +198 -14
  2. package/index.d.ts +29 -0
  3. package/index.js +12 -2
  4. package/lib/ast.js +214 -0
  5. package/lib/database.js +26 -60
  6. package/lib/mongo_server.js +1 -1
  7. package/lib/native_client.js +43 -66
  8. package/lib/plugin.js +227 -3
  9. package/lib/plugins/audit.js +37 -0
  10. package/lib/plugins/index.js +72 -0
  11. package/lib/plugins/logger.js +35 -0
  12. package/lib/plugins/metrics.js +38 -0
  13. package/lib/plugins/timestamps.js +43 -0
  14. package/lib/plugins/validation.js +128 -0
  15. package/lib/sql.js +1212 -84
  16. package/lib/wasm_client.js +41 -57
  17. package/native/jsql-neo-native.node +0 -0
  18. package/nativesrc/jsql-neo-core/Cargo.lock +8 -1
  19. package/nativesrc/jsql-neo-core/Cargo.toml +3 -1
  20. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +50 -13
  21. package/nativesrc/jsql-neo-core/src/engine/memory.rs +46 -23
  22. package/nativesrc/jsql-neo-core/src/engine/mod.rs +13 -1
  23. package/nativesrc/jsql-neo-core/src/engine/table.rs +338 -42
  24. package/nativesrc/jsql-neo-core/src/storage/wal.rs +245 -32
  25. package/nativesrc/jsql-neo-native/Cargo.lock +9 -2
  26. package/nativesrc/jsql-neo-native/Cargo.toml +9 -1
  27. package/nativesrc/jsql-neo-native/src/lib.rs +14 -2
  28. package/nativesrc/jsql-neo-wasm/Cargo.lock +9 -2
  29. package/nativesrc/jsql-neo-wasm/Cargo.toml +8 -1
  30. package/nativesrc/jsql-neo-wasm/src/lib.rs +18 -2
  31. package/package.json +4 -3
  32. package/test/sql-parser.test.js +450 -0
  33. package/wasm/browser.mjs +12 -0
  34. package/wasm/browser_bg.mjs +269 -248
  35. package/wasm/jsql_neo_wasm.d.ts +6 -0
  36. package/wasm/jsql_neo_wasm.js +248 -127
  37. package/wasm/jsql_neo_wasm_bg.wasm +0 -0
  38. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +22 -22
  39. package/wasm/package.json +1 -1
@@ -1,13 +1,20 @@
1
+ //! 预写日志(WAL)。
2
+ //!
3
+ //! 记录格式:`[len: u64 LE][crc32: u32 LE][bincode(WalEntry)]`,头部与载荷一次性写出,
4
+ //! 每条记录带 CRC 校验。重放时遇到截断或校验失败的记录即停止,把它视为「未完整落盘」,
5
+ //! 这样即使进程在写一半时被杀,也不会把半截数据当成有效记录。
6
+
1
7
  use serde::{Deserialize, Serialize};
2
- use std::io::{Read, Write};
8
+ use std::io::{Read, Seek, SeekFrom, Write};
3
9
 
4
- #[derive(Debug, Clone, Serialize, Deserialize)]
10
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11
+ #[repr(u8)]
5
12
  pub enum WalOp {
6
- CreateTable,
7
- DropTable,
8
- Insert,
9
- Update,
10
- Remove,
13
+ CreateTable = 0,
14
+ DropTable = 1,
15
+ Insert = 2,
16
+ Update = 3,
17
+ Remove = 4,
11
18
  }
12
19
 
13
20
  #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -17,40 +24,133 @@ pub struct WalEntry {
17
24
  pub data: Vec<u8>,
18
25
  }
19
26
 
27
+ /// 记录头长度:len(u64) + crc32(u32)
28
+ const HEADER_LEN: usize = 12;
29
+ /// 单条载荷上限,避免损坏的长度字段触发超大内存分配
30
+ const MAX_PAYLOAD: usize = 64 * 1024 * 1024;
31
+
32
+ fn io_err<E: std::fmt::Display>(e: E) -> std::io::Error {
33
+ std::io::Error::new(std::io::ErrorKind::Other, e.to_string())
34
+ }
35
+
36
+ /// 顺序重放,回调所有 `tx_id > after_tx` 的完整记录。
37
+ /// 返回成功解析的字节数(尾部残缺部分不计入)。
38
+ fn replay<R: Read>(reader: &mut R, after_tx: u64, mut on_entry: impl FnMut(WalEntry)) -> std::io::Result<u64> {
39
+ let mut valid = 0u64;
40
+ let mut header = [0u8; HEADER_LEN];
41
+ loop {
42
+ if reader.read_exact(&mut header).is_err() {
43
+ break;
44
+ }
45
+ let len = u64::from_le_bytes(header[..8].try_into().unwrap()) as usize;
46
+ let crc = u32::from_le_bytes(header[8..].try_into().unwrap());
47
+ if len == 0 || len > MAX_PAYLOAD {
48
+ break;
49
+ }
50
+ let mut body = vec![0u8; len];
51
+ if reader.read_exact(&mut body).is_err() {
52
+ break;
53
+ }
54
+ if crc32fast::hash(&body) != crc {
55
+ break;
56
+ }
57
+ match bincode::deserialize::<WalEntry>(&body) {
58
+ Ok(entry) => {
59
+ valid += (HEADER_LEN + len) as u64;
60
+ if entry.tx_id > after_tx {
61
+ on_entry(entry);
62
+ }
63
+ }
64
+ Err(_) => break,
65
+ }
66
+ }
67
+ Ok(valid)
68
+ }
69
+
20
70
  pub struct WalWriter {
21
- path: String,
22
- file: Option<std::fs::File>,
71
+ file: std::fs::File,
23
72
  next_tx: u64,
73
+ pending: u32,
74
+ sync_interval: u32,
75
+ buf: Vec<u8>,
24
76
  }
25
77
 
26
78
  impl WalWriter {
27
79
  pub fn open(path: &str) -> std::io::Result<Self> {
28
- let file = std::fs::OpenOptions::new()
80
+ let mut file = std::fs::OpenOptions::new()
29
81
  .create(true)
82
+ .read(true)
30
83
  .append(true)
31
84
  .open(path)?;
32
- Ok(Self { path: path.to_string(), file: Some(file), next_tx: 1 })
85
+ // 从已有文件恢复 next_tx:否则重启后 tx_id 会从 1 重新开始,
86
+ // 与历史记录撞号,`replay_after` 的增量语义就废了。
87
+ let mut max_tx = 0u64;
88
+ replay(&mut file, 0, |e| {
89
+ if e.tx_id > max_tx {
90
+ max_tx = e.tx_id;
91
+ }
92
+ })?;
93
+ file.seek(SeekFrom::End(0))?;
94
+ Ok(Self {
95
+ file,
96
+ next_tx: max_tx + 1,
97
+ pending: 0,
98
+ sync_interval: 1,
99
+ buf: Vec::with_capacity(256),
100
+ })
101
+ }
102
+
103
+ /// 每写多少条记录 fsync 一次。1 = 每条都落盘(默认,最安全);调大换取吞吐。
104
+ pub fn set_sync_interval(&mut self, n: u32) {
105
+ self.sync_interval = n.max(1);
106
+ }
107
+
108
+ pub fn next_tx(&self) -> u64 {
109
+ self.next_tx
33
110
  }
34
111
 
35
- #[allow(dead_code)]
36
112
  pub fn append(&mut self, op: WalOp, data: Vec<u8>) -> std::io::Result<u64> {
37
113
  let tx_id = self.next_tx;
114
+ let body = bincode::serialize(&WalEntry { tx_id, op, data }).map_err(io_err)?;
115
+ if body.len() > MAX_PAYLOAD {
116
+ return Err(std::io::Error::new(
117
+ std::io::ErrorKind::InvalidInput,
118
+ "wal record too large",
119
+ ));
120
+ }
121
+ // 头 + 载荷一次 write_all,避免两次写之间被打断留下半截头部
122
+ self.buf.clear();
123
+ self.buf.extend_from_slice(&(body.len() as u64).to_le_bytes());
124
+ self.buf.extend_from_slice(&crc32fast::hash(&body).to_le_bytes());
125
+ self.buf.extend_from_slice(&body);
126
+ self.file.write_all(&self.buf)?;
127
+
38
128
  self.next_tx += 1;
39
- let entry = WalEntry { tx_id, op, data };
40
- let buf = bincode::serialize(&entry).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
41
- if let Some(ref mut file) = self.file {
42
- let len = buf.len() as u64;
43
- file.write_all(&len.to_le_bytes())?;
44
- file.write_all(&buf)?;
45
- file.flush()?;
129
+ self.pending += 1;
130
+ if self.pending >= self.sync_interval {
131
+ self.sync()?;
46
132
  }
47
133
  Ok(tx_id)
48
134
  }
49
135
 
136
+ /// 真正落盘。`File::flush` 是空操作,WAL 必须走 `sync_data` 才算持久化。
137
+ pub fn sync(&mut self) -> std::io::Result<()> {
138
+ self.file.sync_data()?;
139
+ self.pending = 0;
140
+ Ok(())
141
+ }
142
+
143
+ /// `sync` 的别名
50
144
  pub fn flush(&mut self) -> std::io::Result<()> {
51
- if let Some(ref mut file) = self.file {
52
- file.flush()?;
53
- }
145
+ self.sync()
146
+ }
147
+
148
+ /// 清空日志(快照落盘后调用),避免 WAL 无限增长
149
+ pub fn truncate(&mut self) -> std::io::Result<()> {
150
+ self.file.set_len(0)?;
151
+ self.file.seek(SeekFrom::Start(0))?;
152
+ self.file.sync_data()?;
153
+ self.pending = 0;
54
154
  Ok(())
55
155
  }
56
156
  }
@@ -64,23 +164,136 @@ impl WalReader {
64
164
  Self { path: path.to_string() }
65
165
  }
66
166
 
67
- pub fn replay_after(&self, _after_tx: u64) -> std::io::Result<Vec<WalEntry>> {
167
+ /// 增量重放:只返回 `tx_id > after_tx` 的记录。
168
+ pub fn replay_after(&self, after_tx: u64) -> std::io::Result<Vec<WalEntry>> {
68
169
  let file = match std::fs::File::open(&self.path) {
69
170
  Ok(f) => f,
70
171
  Err(_) => return Ok(Vec::new()),
71
172
  };
72
173
  let mut reader = std::io::BufReader::new(file);
73
174
  let mut entries = Vec::new();
74
- loop {
75
- let mut len_buf = [0u8; 8];
76
- if reader.read_exact(&mut len_buf).is_err() { break; }
77
- let len = u64::from_le_bytes(len_buf) as usize;
78
- let mut buf = vec![0u8; len];
79
- if reader.read_exact(&mut buf).is_err() { break; }
80
- if let Ok(entry) = bincode::deserialize::<WalEntry>(&buf) {
81
- entries.push(entry);
175
+ replay(&mut reader, after_tx, |e| entries.push(e))?;
176
+ Ok(entries)
177
+ }
178
+
179
+ /// 重放全部记录
180
+ pub fn replay_all(&self) -> std::io::Result<Vec<WalEntry>> {
181
+ self.replay_after(0)
182
+ }
183
+ }
184
+
185
+ #[cfg(test)]
186
+ mod tests {
187
+ use super::*;
188
+
189
+ fn tmp_path(tag: &str) -> String {
190
+ let mut p = std::env::temp_dir();
191
+ p.push(format!("jsql-wal-{}-{}.log", tag, std::process::id()));
192
+ let s = p.to_string_lossy().to_string();
193
+ let _ = std::fs::remove_file(&s);
194
+ s
195
+ }
196
+
197
+ #[test]
198
+ fn append_replay_roundtrip() {
199
+ let path = tmp_path("rt");
200
+ {
201
+ let mut w = WalWriter::open(&path).unwrap();
202
+ for i in 0..5u8 {
203
+ assert_eq!(w.append(WalOp::Insert, vec![i]).unwrap(), i as u64 + 1);
82
204
  }
205
+ w.sync().unwrap();
83
206
  }
84
- Ok(entries)
207
+ let entries = WalReader::open(&path).replay_all().unwrap();
208
+ assert_eq!(entries.len(), 5);
209
+ assert_eq!(entries[3].tx_id, 4);
210
+ assert_eq!(entries[3].op, WalOp::Insert);
211
+ assert_eq!(entries[3].data, vec![3u8]);
212
+ let _ = std::fs::remove_file(&path);
213
+ }
214
+
215
+ #[test]
216
+ fn replay_after_is_incremental() {
217
+ let path = tmp_path("inc");
218
+ {
219
+ let mut w = WalWriter::open(&path).unwrap();
220
+ for i in 0..5u8 {
221
+ w.append(WalOp::Update, vec![i]).unwrap();
222
+ }
223
+ w.sync().unwrap();
224
+ }
225
+ let tail = WalReader::open(&path).replay_after(3).unwrap();
226
+ assert_eq!(tail.len(), 2);
227
+ assert!(tail.iter().all(|e| e.tx_id > 3));
228
+ let _ = std::fs::remove_file(&path);
229
+ }
230
+
231
+ #[test]
232
+ fn next_tx_is_recovered_after_reopen() {
233
+ let path = tmp_path("recover");
234
+ {
235
+ let mut w = WalWriter::open(&path).unwrap();
236
+ w.append(WalOp::Insert, vec![1]).unwrap();
237
+ w.append(WalOp::Insert, vec![2]).unwrap();
238
+ w.sync().unwrap();
239
+ }
240
+ let mut w = WalWriter::open(&path).unwrap();
241
+ assert_eq!(w.next_tx(), 3, "tx_id 必须接着历史最大值,不能回退到 1");
242
+ assert_eq!(w.append(WalOp::Remove, vec![3]).unwrap(), 3);
243
+ assert_eq!(WalReader::open(&path).replay_all().unwrap().len(), 3);
244
+ let _ = std::fs::remove_file(&path);
245
+ }
246
+
247
+ #[test]
248
+ fn truncated_tail_is_dropped() {
249
+ let path = tmp_path("torn");
250
+ {
251
+ let mut w = WalWriter::open(&path).unwrap();
252
+ w.append(WalOp::Insert, vec![7]).unwrap();
253
+ w.append(WalOp::Insert, vec![8]).unwrap();
254
+ w.sync().unwrap();
255
+ }
256
+ // 模拟写一半被杀:尾部只剩半截垃圾
257
+ {
258
+ use std::io::Write as _;
259
+ let mut f = std::fs::OpenOptions::new().append(true).open(&path).unwrap();
260
+ f.write_all(&[9u8, 9, 9]).unwrap();
261
+ f.sync_all().unwrap();
262
+ }
263
+ assert_eq!(WalReader::open(&path).replay_all().unwrap().len(), 2);
264
+ let _ = std::fs::remove_file(&path);
265
+ }
266
+
267
+ #[test]
268
+ fn crc_mismatch_stops_replay() {
269
+ let path = tmp_path("crc");
270
+ {
271
+ let mut w = WalWriter::open(&path).unwrap();
272
+ w.append(WalOp::Insert, vec![1]).unwrap();
273
+ w.append(WalOp::Insert, vec![2]).unwrap();
274
+ w.sync().unwrap();
275
+ }
276
+ let len = std::fs::metadata(&path).unwrap().len();
277
+ {
278
+ use std::io::{Seek, SeekFrom, Write as _};
279
+ let mut f = std::fs::OpenOptions::new().write(true).open(&path).unwrap();
280
+ f.seek(SeekFrom::Start(len - 1)).unwrap();
281
+ f.write_all(&[0xFF]).unwrap();
282
+ f.sync_all().unwrap();
283
+ }
284
+ assert_eq!(WalReader::open(&path).replay_all().unwrap().len(), 1, "校验失败记录应丢弃");
285
+ let _ = std::fs::remove_file(&path);
286
+ }
287
+
288
+ #[test]
289
+ fn truncate_resets_log_but_keeps_tx_monotonic() {
290
+ let path = tmp_path("trunc");
291
+ let mut w = WalWriter::open(&path).unwrap();
292
+ w.append(WalOp::Insert, vec![1]).unwrap();
293
+ w.truncate().unwrap();
294
+ assert_eq!(WalReader::open(&path).replay_all().unwrap().len(), 0);
295
+ // tx_id 不能回落:否则消费方持有的水位线(replay_after(T))会看不到新记录
296
+ assert_eq!(w.append(WalOp::DropTable, vec![]).unwrap(), 2);
297
+ let _ = std::fs::remove_file(&path);
85
298
  }
86
299
  }
@@ -205,12 +205,13 @@ dependencies = [
205
205
 
206
206
  [[package]]
207
207
  name = "jsql-neo-core"
208
- version = "0.1.0"
208
+ version = "5.6.0"
209
209
  dependencies = [
210
210
  "bincode",
211
211
  "chrono",
212
212
  "crc32fast",
213
213
  "itoa",
214
+ "regex-lite",
214
215
  "rustc-hash",
215
216
  "ryu",
216
217
  "serde",
@@ -219,7 +220,7 @@ dependencies = [
219
220
 
220
221
  [[package]]
221
222
  name = "jsql-neo-native"
222
- version = "0.1.0"
223
+ version = "5.6.0"
223
224
  dependencies = [
224
225
  "jsql-neo-core",
225
226
  "napi",
@@ -375,6 +376,12 @@ dependencies = [
375
376
  "regex-syntax",
376
377
  ]
377
378
 
379
+ [[package]]
380
+ name = "regex-lite"
381
+ version = "0.1.9"
382
+ source = "registry+https://github.com/rust-lang/crates.io-index"
383
+ checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
384
+
378
385
  [[package]]
379
386
  name = "regex-syntax"
380
387
  version = "0.8.11"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "jsql-neo-native"
3
- version = "5.4.0"
3
+ version = "6.0.0"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -14,3 +14,11 @@ serde_json = "1"
14
14
 
15
15
  [build-dependencies]
16
16
  napi-build = "2"
17
+
18
+ # 体积更小 + 运行更快:全图 LTO 去掉跨 crate 死代码,单 codegen-unit 换取更好的内联与优化,
19
+ # strip 去掉符号表(不影响 napi 动态导出符号与 dlopen 加载)。
20
+ [profile.release]
21
+ opt-level = 3
22
+ lto = "fat"
23
+ codegen-units = 1
24
+ strip = "symbols"
@@ -111,6 +111,18 @@ pub fn jsql_create_table(name: String, schema_json: String) -> String {
111
111
  })
112
112
  }
113
113
 
114
+ #[napi]
115
+ pub fn jsql_add_column(table: String, name: String, fs_json: String) -> String {
116
+ let fs: FieldSchema = match serde_json::from_str(&fs_json) {
117
+ Ok(f) => f,
118
+ Err(e) => return format!(r#"{{"ok":false,"error":"invalid field schema: {}"}}"#, e),
119
+ };
120
+ with_engine_mut(|eng| match eng.add_column(&table, &name, fs) {
121
+ Ok(()) => r#"{"ok":true}"#.to_string(),
122
+ Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
123
+ })
124
+ }
125
+
114
126
  #[napi]
115
127
  pub fn jsql_drop_table(name: String) -> String {
116
128
  with_engine_mut(|eng| match eng.drop_table(&name) {
@@ -152,8 +164,8 @@ pub fn jsql_find(table: String, filter_json: String, limit: i32, offset: i32) ->
152
164
  with_engine_ro(|eng| {
153
165
  let filter: Option<HashMap<String, serde_json::Value>> =
154
166
  if filter_json.is_empty() { None } else { serde_json::from_str(&filter_json).ok() };
155
- match eng.find(&table, &filter, Some(limit as usize), Some(offset as usize), None, &None, &None) {
156
- Ok((rows, _, _)) => serde_json::to_string(&rows).unwrap_or_else(|_| "[]".to_string()),
167
+ match eng.find_json(&table, &filter, Some(limit as usize), Some(offset as usize), None, &None, &None) {
168
+ Ok((json, _, _)) => json,
157
169
  Err(e) => format!(r#"{{"error":"{}"}}"#, e),
158
170
  }
159
171
  })
@@ -171,13 +171,14 @@ dependencies = [
171
171
 
172
172
  [[package]]
173
173
  name = "jsql-neo-core"
174
- version = "5.4.0"
174
+ version = "6.0.0"
175
175
  dependencies = [
176
176
  "bincode",
177
177
  "chrono",
178
178
  "crc32fast",
179
179
  "itoa",
180
180
  "js-sys",
181
+ "regex-lite",
181
182
  "rustc-hash",
182
183
  "ryu",
183
184
  "serde",
@@ -187,7 +188,7 @@ dependencies = [
187
188
 
188
189
  [[package]]
189
190
  name = "jsql-neo-wasm"
190
- version = "5.4.0"
191
+ version = "6.0.0"
191
192
  dependencies = [
192
193
  "js-sys",
193
194
  "jsql-neo-core",
@@ -252,6 +253,12 @@ dependencies = [
252
253
  "proc-macro2",
253
254
  ]
254
255
 
256
+ [[package]]
257
+ name = "regex-lite"
258
+ version = "0.1.9"
259
+ source = "registry+https://github.com/rust-lang/crates.io-index"
260
+ checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
261
+
255
262
  [[package]]
256
263
  name = "rustc-hash"
257
264
  version = "2.1.3"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "jsql-neo-wasm"
3
- version = "5.4.0"
3
+ version = "6.0.0"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -13,3 +13,10 @@ serde_json = "1"
13
13
 
14
14
  [dependencies.js-sys]
15
15
  version = "0.3"
16
+
17
+ # wasm 产物:LTO + 单 codegen-unit 显著减小体积,strip 去掉符号表
18
+ [profile.release]
19
+ opt-level = 3
20
+ lto = "fat"
21
+ codegen-units = 1
22
+ strip = "symbols"
@@ -34,6 +34,22 @@ pub fn jsql_create_table(name: &str, schema_json: &str) -> String {
34
34
  })
35
35
  }
36
36
 
37
+ /// 运行期加列:扩展表结构并为已有行回填默认值。
38
+ /// fs_json 为单个 FieldSchema 的 JSON,例如 {"type":"integer","nullable":true,"default":0}
39
+ #[wasm_bindgen]
40
+ pub fn jsql_add_column(table: &str, name: &str, fs_json: &str) -> String {
41
+ ENGINE.with(|eng| {
42
+ let fs: FieldSchema = match serde_json::from_str(fs_json) {
43
+ Ok(f) => f,
44
+ Err(e) => return format!(r#"{{"ok":false,"error":"invalid field schema: {}"}}"#, e),
45
+ };
46
+ match eng.borrow_mut().add_column(table, name, fs) {
47
+ Ok(()) => r#"{"ok":true}"#.to_string(),
48
+ Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
49
+ }
50
+ })
51
+ }
52
+
37
53
  #[wasm_bindgen]
38
54
  pub fn jsql_drop_table(name: &str) -> String {
39
55
  ENGINE.with(|eng| match eng.borrow_mut().drop_table(name) {
@@ -328,8 +344,8 @@ pub fn jsql_find(table: &str, filter_json: &str, limit: usize, offset: usize) ->
328
344
  ENGINE.with(|eng| {
329
345
  let filter: Option<std::collections::HashMap<String, serde_json::Value>> =
330
346
  if filter_json.is_empty() { None } else { serde_json::from_str(filter_json).ok() };
331
- match eng.borrow().find(table, &filter, Some(limit), Some(offset), None, &None, &None) {
332
- Ok((rows, _, _)) => serde_json::to_string(&rows).unwrap_or_else(|_| "[]".to_string()),
347
+ match eng.borrow().find_json(table, &filter, Some(limit), Some(offset), None, &None, &None) {
348
+ Ok((json, _, _)) => json,
333
349
  Err(e) => format!(r#"{{"error":"{}"}}"#, e),
334
350
  }
335
351
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsql-neo",
3
- "version": "5.4.2",
3
+ "version": "6.0.0-beta2",
4
4
  "description": "JSQL-NEO — Rust-powered embedded database with WASM, REST API, B-Tree indexes, WAL, crash recovery",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -45,11 +45,12 @@
45
45
  ],
46
46
  "scripts": {
47
47
  "postinstall": "node postinstall.js",
48
- "test": "node test/smoke.js",
48
+ "test": "node test/smoke.js && node test/sql-parser.test.js",
49
+ "test:parser": "node test/sql-parser.test.js",
49
50
  "test:btree": "node test/btree.test.js",
50
51
  "test:native": "node test/native.test.js",
51
52
  "test:regress": "node test/regress-5.1.0.js",
52
- "test:all": "node test/smoke.js && node test/native.test.js && node test/wasm.test.js && node test/coverage.js && node test/regress-5.1.0.js && node test/join.test.js && node test/btree.test.js",
53
+ "test:all": "node test/smoke.js && node test/sql-parser.test.js && node test/native.test.js && node test/wasm.test.js && node test/coverage.js && node test/regress-5.1.0.js && node test/join.test.js && node test/btree.test.js",
53
54
  "test:coverage": "c8 --all --include='lib/**' --exclude='lib/mysql_server.js' --exclude='lib/native_client.js' --exclude='lib/wasm_client.js' --exclude='lib/mysql_compat.js' --exclude='lib/nedb_compat.js' --exclude='lib/plugin.js' --reporter=text --reporter=lcov node test/coverage.js",
54
55
  "test:orms": "node examples/orms/run-all.js"
55
56
  },