jsql-neo 6.3.2 → 6.3.4
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 +65 -25
- package/index.js +73 -6
- package/lib/btree.js +5 -41
- package/lib/database.js +247 -29
- package/lib/native_client.js +57 -21
- package/lib/sql.js +192 -30
- package/native/jsql-neo-native.node +0 -0
- package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +8 -1
- package/nativesrc/jsql-neo-native/src/lib.rs +274 -178
- package/package.json +4 -4
- package/test/mysql-compat-hijack.test.js +87 -0
- package/test/native-multi-instance.test.js +134 -0
- package/test/readme-audit.test.js +25 -0
- package/test/regress-5.1.0.js +32 -7
- package/test/sql-parser.test.js +114 -0
- package/test/wal-recovery.test.js +161 -0
|
@@ -5,27 +5,110 @@ use jsql_neo_core::types::{FieldSchema, TableDefinition, schema_order_preserving
|
|
|
5
5
|
use napi_derive::napi;
|
|
6
6
|
use napi::JsBuffer;
|
|
7
7
|
|
|
8
|
+
/// 每个 JS `Database` 实例对应一个独立引擎句柄。
|
|
9
|
+
///
|
|
10
|
+
/// 历史问题(≤6.3.3):整个进程只有一个 `thread_local! ENGINE`,
|
|
11
|
+
/// 于是 `new Database(dirA)` 之后再 `new Database(dirB)` 会把 dirA 的表
|
|
12
|
+
/// 全部 `clear()` 掉(见 `HybridEngine::open`),同进程无法并存两个库;
|
|
13
|
+
/// 且在引擎回调里重入调用会直接返回 "engine busy (reentrant call)"。
|
|
14
|
+
/// 对一个自称「嵌入式数据库」的库,这是硬伤。
|
|
15
|
+
///
|
|
16
|
+
/// 现在改为「句柄注册表」:句柄 0 是为向后兼容保留的默认实例,
|
|
17
|
+
/// 新实例从 1 开始分配,各自持有独立的 HybridEngine,互不影响。
|
|
8
18
|
thread_local! {
|
|
9
|
-
static
|
|
19
|
+
static ENGINES: RefCell<HashMap<u32, RefCell<HybridEngine>>> =
|
|
20
|
+
RefCell::new(HashMap::new());
|
|
21
|
+
static NEXT_HANDLE: RefCell<u32> = const { RefCell::new(1) };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// 保留的默认句柄,维持既有 `jsql_*` 无句柄 API 的行为。
|
|
25
|
+
const DEFAULT_HANDLE: u32 = 0;
|
|
26
|
+
|
|
27
|
+
/// 确保默认实例存在。
|
|
28
|
+
///
|
|
29
|
+
/// 旧版是 `thread_local! static ENGINE`,加载即有,因此无句柄 API 一直可用。
|
|
30
|
+
/// 改为注册表后必须显式创建句柄 0,否则 `jsqlOpen(...)` 这类旧调用会报
|
|
31
|
+
/// "no such database instance: 0" —— 那就是破坏性变更。
|
|
32
|
+
fn ensure_default_instance() {
|
|
33
|
+
ENGINES.with(|m| {
|
|
34
|
+
let mut map = m.borrow_mut();
|
|
35
|
+
map.entry(DEFAULT_HANDLE)
|
|
36
|
+
.or_insert_with(|| RefCell::new(HybridEngine::new()));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn no_instance(handle: u32) -> String {
|
|
41
|
+
format!(r#"{{"ok":false,"error":"no such database instance: {}"}}"#, handle)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// 创建一个新的独立引擎实例,返回其句柄。
|
|
45
|
+
#[napi]
|
|
46
|
+
pub fn jsql_instance_new() -> u32 {
|
|
47
|
+
NEXT_HANDLE.with(|h| {
|
|
48
|
+
let mut next = h.borrow_mut();
|
|
49
|
+
let handle = *next;
|
|
50
|
+
*next = next.wrapping_add(1);
|
|
51
|
+
if *next == DEFAULT_HANDLE {
|
|
52
|
+
*next = 1;
|
|
53
|
+
}
|
|
54
|
+
ENGINES.with(|m| {
|
|
55
|
+
m.borrow_mut()
|
|
56
|
+
.insert(handle, RefCell::new(HybridEngine::new()));
|
|
57
|
+
});
|
|
58
|
+
handle
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// 关闭并销毁一个实例,释放其占用的内存与文件句柄。
|
|
63
|
+
#[napi]
|
|
64
|
+
pub fn jsql_instance_free(handle: u32) -> String {
|
|
65
|
+
ENGINES.with(|m| {
|
|
66
|
+
let mut map = m.borrow_mut();
|
|
67
|
+
if let Some(cell) = map.remove(&handle) {
|
|
68
|
+
// 尽力刷盘,失败也不阻塞销毁
|
|
69
|
+
let _ = cell.borrow_mut().close();
|
|
70
|
+
r#"{"ok":true}"#.to_string()
|
|
71
|
+
} else {
|
|
72
|
+
no_instance(handle)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// 当前显式创建的实例数(诊断用,不含为向后兼容保留的默认实例 0)。
|
|
78
|
+
#[napi]
|
|
79
|
+
pub fn jsql_instance_count() -> i32 {
|
|
80
|
+
ENGINES.with(|m| {
|
|
81
|
+
m.borrow()
|
|
82
|
+
.keys()
|
|
83
|
+
.filter(|h| **h != DEFAULT_HANDLE)
|
|
84
|
+
.count() as i32
|
|
85
|
+
})
|
|
10
86
|
}
|
|
11
87
|
|
|
12
88
|
fn with_engine_mut<F>(f: F) -> String
|
|
13
89
|
where
|
|
14
90
|
F: FnOnce(&mut HybridEngine) -> String,
|
|
15
91
|
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Err(_) => r#"{"ok":false,"error":"engine busy (reentrant call)"}"#.to_string(),
|
|
19
|
-
})
|
|
92
|
+
ensure_default_instance();
|
|
93
|
+
with_engine_handle_mut(DEFAULT_HANDLE, f)
|
|
20
94
|
}
|
|
21
95
|
|
|
22
|
-
fn
|
|
96
|
+
fn with_engine_handle_mut<F>(handle: u32, f: F) -> String
|
|
23
97
|
where
|
|
24
|
-
F: FnOnce(&HybridEngine) -> String,
|
|
98
|
+
F: FnOnce(&mut HybridEngine) -> String,
|
|
25
99
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
100
|
+
ENGINES.with(|m| {
|
|
101
|
+
let map = m.borrow();
|
|
102
|
+
let cell = match map.get(&handle) {
|
|
103
|
+
Some(c) => c,
|
|
104
|
+
None => return no_instance(handle),
|
|
105
|
+
};
|
|
106
|
+
// 先取出结果再返回,确保 RefMut 在 map 之前被 drop
|
|
107
|
+
let out = match cell.try_borrow_mut() {
|
|
108
|
+
Ok(mut eng) => f(&mut eng),
|
|
109
|
+
Err(_) => r#"{"ok":false,"error":"engine busy (reentrant call)"}"#.to_string(),
|
|
110
|
+
};
|
|
111
|
+
out
|
|
29
112
|
})
|
|
30
113
|
}
|
|
31
114
|
|
|
@@ -36,114 +119,135 @@ fn safe_json_result(result: Result<String, String>) -> String {
|
|
|
36
119
|
}
|
|
37
120
|
}
|
|
38
121
|
|
|
122
|
+
/// 为每个操作生成「旧版无句柄」+「新版带句柄」两个入口。
|
|
123
|
+
/// 旧版走默认实例以保持向后兼容,新版按句柄路由到各自独立的引擎。
|
|
124
|
+
/// `$eng` 是引擎变量名,在 body 中直接引用。
|
|
125
|
+
macro_rules! napi_engine_fn {
|
|
126
|
+
($plain:ident, $h:ident, ($($arg:ident : $ty:ty),*) => |$eng:ident| $body:block) => {
|
|
127
|
+
#[napi]
|
|
128
|
+
pub fn $plain($($arg : $ty),*) -> String {
|
|
129
|
+
with_engine_mut(|$eng: &mut HybridEngine| $body)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#[napi]
|
|
133
|
+
pub fn $h(handle: u32, $($arg : $ty),*) -> String {
|
|
134
|
+
with_engine_handle_mut(handle, |$eng: &mut HybridEngine| $body)
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
39
139
|
#[napi]
|
|
40
140
|
pub fn jsql_open(dir: String, mode: String) -> String {
|
|
41
|
-
with_engine_mut(|eng|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
141
|
+
with_engine_mut(|eng| do_open(eng, &dir, &mode))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[napi]
|
|
145
|
+
pub fn jsql_open_h(handle: u32, dir: String, mode: String) -> String {
|
|
146
|
+
with_engine_handle_mut(handle, |eng| do_open(eng, &dir, &mode))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn do_open(eng: &mut HybridEngine, dir: &str, mode: &str) -> String {
|
|
150
|
+
let open_res = eng.open(dir, mode);
|
|
151
|
+
match open_res {
|
|
152
|
+
Ok(()) => {
|
|
153
|
+
let names = eng.list_tables();
|
|
154
|
+
let mut out = serde_json::Map::new();
|
|
155
|
+
out.insert("ok".into(), serde_json::Value::Bool(true));
|
|
156
|
+
out.insert("tables".into(), serde_json::Value::Array(
|
|
157
|
+
names.iter().map(|n| serde_json::Value::String(n.clone())).collect()
|
|
158
|
+
));
|
|
159
|
+
let mut schemas = serde_json::Map::new();
|
|
160
|
+
for n in &names {
|
|
161
|
+
if let Some(s) = eng.table_schema(n) {
|
|
162
|
+
let mut v = serde_json::Map::new();
|
|
163
|
+
for (k, fs) in &s {
|
|
164
|
+
if let Ok(fv) = serde_json::to_value(fs) {
|
|
165
|
+
v.insert(k.clone(), fv);
|
|
59
166
|
}
|
|
60
|
-
schemas.insert(n.clone(), serde_json::Value::Object(v));
|
|
61
167
|
}
|
|
168
|
+
schemas.insert(n.clone(), serde_json::Value::Object(v));
|
|
62
169
|
}
|
|
63
|
-
out.insert("schemas".into(), serde_json::Value::Object(schemas));
|
|
64
|
-
serde_json::to_string(&out).unwrap_or_else(|_| r#"{"ok":true}"#.to_string())
|
|
65
170
|
}
|
|
66
|
-
|
|
171
|
+
out.insert("schemas".into(), serde_json::Value::Object(schemas));
|
|
172
|
+
serde_json::to_string(&out).unwrap_or_else(|_| r#"{"ok":true}"#.to_string())
|
|
67
173
|
}
|
|
68
|
-
|
|
174
|
+
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e.replace('"', r#"\""#)),
|
|
175
|
+
}
|
|
69
176
|
}
|
|
70
177
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
178
|
+
napi_engine_fn!(jsql_flush_dirty, jsql_flush_dirty_h, () => |eng| {
|
|
179
|
+
match eng.flush_dirty() {
|
|
180
|
+
Ok(n) => format!(r#"{{"ok":true,"flushed":{}}}"#, n),
|
|
181
|
+
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e.replace('"', r#"\""#)),
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
napi_engine_fn!(jsql_evict, jsql_evict_h, () => |eng| {
|
|
186
|
+
match eng.evict_one() {
|
|
187
|
+
Some(name) => format!(r#"{{"ok":true,"evicted":"{}","remaining":{}}}"#, name, eng.mem_count()),
|
|
188
|
+
None => r#"{"ok":true,"evicted":null,"remaining":0}"#.to_string(),
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
napi_engine_fn!(jsql_close, jsql_close_h, () => |eng| {
|
|
193
|
+
eng.close();
|
|
194
|
+
r#"{"ok":true}"#.to_string()
|
|
195
|
+
});
|
|
80
196
|
|
|
81
197
|
#[napi]
|
|
82
|
-
pub fn
|
|
83
|
-
with_engine_mut(|eng|
|
|
84
|
-
match eng.evict_one() {
|
|
85
|
-
Some(name) => format!(r#"{{"ok":true,"evicted":"{}","remaining":{}}}"#, name, eng.mem_count()),
|
|
86
|
-
None => r#"{"ok":true,"evicted":null,"remaining":0}"#.to_string(),
|
|
87
|
-
}
|
|
88
|
-
})
|
|
198
|
+
pub fn jsql_create_table(name: String, schema_json: String) -> String {
|
|
199
|
+
with_engine_mut(|eng| do_create_table(eng, &name, &schema_json))
|
|
89
200
|
}
|
|
90
201
|
|
|
91
202
|
#[napi]
|
|
92
|
-
pub fn
|
|
93
|
-
|
|
94
|
-
eng.close();
|
|
95
|
-
r#"{"ok":true}"#.to_string()
|
|
96
|
-
})
|
|
203
|
+
pub fn jsql_create_table_h(handle: u32, name: String, schema_json: String) -> String {
|
|
204
|
+
with_engine_handle_mut(handle, |eng| do_create_table(eng, &name, &schema_json))
|
|
97
205
|
}
|
|
98
206
|
|
|
99
|
-
|
|
100
|
-
pub fn jsql_create_table(name: String, schema_json: String) -> String {
|
|
207
|
+
fn do_create_table(eng: &mut HybridEngine, name: &str, schema_json: &str) -> String {
|
|
101
208
|
use schema_order_preserving;
|
|
102
|
-
let mut d = serde_json::Deserializer::from_str(
|
|
209
|
+
let mut d = serde_json::Deserializer::from_str(schema_json);
|
|
103
210
|
let schema: Vec<(String, FieldSchema)> = match schema_order_preserving::deserialize(&mut d) {
|
|
104
211
|
Ok(s) => s,
|
|
105
212
|
Err(e) => return format!(r#"{{"ok":false,"error":"invalid schema: {}"}}"#, e),
|
|
106
213
|
};
|
|
107
|
-
let def = TableDefinition { name: name.
|
|
108
|
-
|
|
214
|
+
let def = TableDefinition { name: name.to_string(), schema };
|
|
215
|
+
match eng.create_table(def) {
|
|
109
216
|
Ok(()) => r#"{"ok":true}"#.to_string(),
|
|
110
217
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
111
|
-
}
|
|
218
|
+
}
|
|
112
219
|
}
|
|
113
220
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
221
|
+
napi_engine_fn!(jsql_add_column, jsql_add_column_h,
|
|
222
|
+
(table: String, name: String, fs_json: String) => |eng| {
|
|
223
|
+
let fs: FieldSchema = match serde_json::from_str(&fs_json) {
|
|
224
|
+
Ok(f) => f,
|
|
225
|
+
Err(e) => return format!(r#"{{"ok":false,"error":"invalid field schema: {}"}}"#, e),
|
|
226
|
+
};
|
|
227
|
+
match eng.add_column(&table, &name, fs) {
|
|
228
|
+
Ok(()) => r#"{"ok":true}"#.to_string(),
|
|
229
|
+
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
);
|
|
125
233
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
with_engine_mut(|eng| match eng.drop_table(&name) {
|
|
234
|
+
napi_engine_fn!(jsql_drop_table, jsql_drop_table_h, (name: String) => |eng| {
|
|
235
|
+
match eng.drop_table(&name) {
|
|
129
236
|
Ok(()) => r#"{"ok":true}"#.to_string(),
|
|
130
237
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
131
|
-
}
|
|
132
|
-
}
|
|
238
|
+
}
|
|
239
|
+
});
|
|
133
240
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
}
|
|
241
|
+
napi_engine_fn!(jsql_insert, jsql_insert_h, (table: String, data_json: String) => |eng| {
|
|
242
|
+
let data: Vec<HashMap<String, serde_json::Value>> = match serde_json::from_str(&data_json) {
|
|
243
|
+
Ok(d) => d,
|
|
244
|
+
Err(e) => return format!(r#"{{"error":"invalid data: {}"}}"#, e),
|
|
245
|
+
};
|
|
246
|
+
match eng.insert(&table, data) {
|
|
247
|
+
Ok(ids) => serde_json::to_string(&ids).unwrap_or_else(|_| "[]".to_string()),
|
|
248
|
+
Err(e) => format!(r#"{{"error":"{}"}}"#, e),
|
|
249
|
+
}
|
|
250
|
+
});
|
|
147
251
|
|
|
148
252
|
#[napi]
|
|
149
253
|
pub fn jsql_insert_buf(table: String, data: JsBuffer) -> String {
|
|
@@ -151,48 +255,53 @@ pub fn jsql_insert_buf(table: String, data: JsBuffer) -> String {
|
|
|
151
255
|
Ok(v) => v,
|
|
152
256
|
Err(_) => return r#"{"error":"invalid buffer"}"#.to_string(),
|
|
153
257
|
};
|
|
154
|
-
with_engine_mut(|eng|
|
|
155
|
-
let buf: &[u8] = buf_val.as_ref();
|
|
156
|
-
let res = eng.insert_buf(&table, buf)
|
|
157
|
-
.map(|ids| serde_json::to_string(&ids).unwrap_or_else(|_| "[]".to_string()));
|
|
158
|
-
safe_json_result(res.map_err(|e| e))
|
|
159
|
-
})
|
|
258
|
+
with_engine_mut(|eng| do_insert_buf(eng, &table, buf_val))
|
|
160
259
|
}
|
|
161
260
|
|
|
162
261
|
#[napi]
|
|
163
|
-
pub fn
|
|
164
|
-
|
|
262
|
+
pub fn jsql_insert_buf_h(handle: u32, table: String, data: JsBuffer) -> String {
|
|
263
|
+
let buf_val = match data.into_value() {
|
|
264
|
+
Ok(v) => v,
|
|
265
|
+
Err(_) => return r#"{"error":"invalid buffer"}"#.to_string(),
|
|
266
|
+
};
|
|
267
|
+
with_engine_handle_mut(handle, |eng| do_insert_buf(eng, &table, buf_val))
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fn do_insert_buf(eng: &mut HybridEngine, table: &str, buf_val: napi::JsBufferValue) -> String {
|
|
271
|
+
let buf: &[u8] = buf_val.as_ref();
|
|
272
|
+
let res = eng.insert_buf(table, buf)
|
|
273
|
+
.map(|ids| serde_json::to_string(&ids).unwrap_or_else(|_| "[]".to_string()));
|
|
274
|
+
safe_json_result(res)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
napi_engine_fn!(jsql_find, jsql_find_h,
|
|
278
|
+
(table: String, filter_json: String, limit: i32, offset: i32) => |eng| {
|
|
165
279
|
let filter: Option<HashMap<String, serde_json::Value>> =
|
|
166
280
|
if filter_json.is_empty() { None } else { serde_json::from_str(&filter_json).ok() };
|
|
167
281
|
match eng.find_json(&table, &filter, Some(limit as usize), Some(offset as usize), None, &None, &None) {
|
|
168
282
|
Ok((json, _, _)) => json,
|
|
169
283
|
Err(e) => format!(r#"{{"error":"{}"}}"#, e),
|
|
170
284
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
285
|
+
}
|
|
286
|
+
);
|
|
173
287
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
}
|
|
288
|
+
napi_engine_fn!(jsql_find_by_id, jsql_find_by_id_h, (table: String, id: i64) => |eng| {
|
|
289
|
+
match eng.find_by_id_json(&table, id as u64) {
|
|
290
|
+
Ok(Some(s)) => s,
|
|
291
|
+
Ok(None) => "null".to_string(),
|
|
292
|
+
Err(e) => format!(r#"{{"error":"{}"}}"#, e),
|
|
293
|
+
}
|
|
294
|
+
});
|
|
184
295
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
with_engine_ro(|eng| match eng.count(&table) {
|
|
296
|
+
napi_engine_fn!(jsql_count, jsql_count_h, (table: String) => |eng| {
|
|
297
|
+
match eng.count(&table) {
|
|
188
298
|
Ok(c) => format!("{}", c),
|
|
189
299
|
Err(e) => format!(r#"{{"error":"{}"}}"#, e),
|
|
190
|
-
}
|
|
191
|
-
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
192
302
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
with_engine_mut(|eng| {
|
|
303
|
+
napi_engine_fn!(jsql_update_by_id, jsql_update_by_id_h,
|
|
304
|
+
(table: String, id: i64, data_json: String) => |eng| {
|
|
196
305
|
let data: HashMap<String, serde_json::Value> = match serde_json::from_str(&data_json) {
|
|
197
306
|
Ok(d) => d,
|
|
198
307
|
Err(e) => return format!(r#"{{"ok":false,"error":"invalid data: {}"}}"#, e),
|
|
@@ -202,80 +311,67 @@ pub fn jsql_update_by_id(table: String, id: i64, data_json: String) -> String {
|
|
|
202
311
|
Ok(false) => r#"{"ok":false,"error":"not found"}"#.to_string(),
|
|
203
312
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
204
313
|
}
|
|
205
|
-
}
|
|
206
|
-
|
|
314
|
+
}
|
|
315
|
+
);
|
|
207
316
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
with_engine_mut(|eng| match eng.remove_by_id(&table, id as u64) {
|
|
317
|
+
napi_engine_fn!(jsql_remove_by_id, jsql_remove_by_id_h, (table: String, id: i64) => |eng| {
|
|
318
|
+
match eng.remove_by_id(&table, id as u64) {
|
|
211
319
|
Ok(true) => r#"{"ok":true}"#.to_string(),
|
|
212
320
|
Ok(false) => r#"{"ok":false,"error":"not found"}"#.to_string(),
|
|
213
321
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
214
|
-
}
|
|
215
|
-
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
216
324
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
}
|
|
325
|
+
napi_engine_fn!(jsql_remove_by_ids, jsql_remove_by_ids_h, (table: String, ids_json: String) => |eng| {
|
|
326
|
+
let ids: Vec<u64> = match serde_json::from_str(&ids_json) {
|
|
327
|
+
Ok(v) => v,
|
|
328
|
+
Err(e) => return format!(r#"{{"error":"invalid ids: {}"}}"#, e),
|
|
329
|
+
};
|
|
330
|
+
match eng.remove_by_ids(&table, &ids) {
|
|
331
|
+
Ok(n) => format!(r#"{{"ok":true,"count":{}}}"#, n),
|
|
332
|
+
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
333
|
+
}
|
|
334
|
+
});
|
|
230
335
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
}
|
|
336
|
+
napi_engine_fn!(jsql_update_by_ids, jsql_update_by_ids_h, (table: String, batch_json: String) => |eng| {
|
|
337
|
+
let batch: Vec<(u64, HashMap<String, serde_json::Value>)> = match serde_json::from_str(&batch_json) {
|
|
338
|
+
Ok(v) => v,
|
|
339
|
+
Err(e) => return format!(r#"{{"error":"invalid batch: {}"}}"#, e),
|
|
340
|
+
};
|
|
341
|
+
match eng.update_by_ids(&table, batch) {
|
|
342
|
+
Ok(n) => format!(r#"{{"ok":true,"count":{}}}"#, n),
|
|
343
|
+
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
344
|
+
}
|
|
345
|
+
});
|
|
244
346
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
})
|
|
257
|
-
}
|
|
347
|
+
napi_engine_fn!(jsql_find_by_ids, jsql_find_by_ids_h, (table: String, ids_json: String) => |eng| {
|
|
348
|
+
let ids: Vec<u64> = match serde_json::from_str(&ids_json) {
|
|
349
|
+
Ok(v) => v,
|
|
350
|
+
Err(e) => return format!(r#"{{"error":"invalid ids: {}"}}"#, e),
|
|
351
|
+
};
|
|
352
|
+
match eng.find_by_ids_json(&table, &ids) {
|
|
353
|
+
Ok(s) => s,
|
|
354
|
+
Err(e) => format!(r#"{{"error":"{}"}}"#, e),
|
|
355
|
+
}
|
|
356
|
+
});
|
|
258
357
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
with_engine_mut(|eng| match eng.begin_tx() {
|
|
358
|
+
napi_engine_fn!(jsql_begin_tx, jsql_begin_tx_h, () => |eng| {
|
|
359
|
+
match eng.begin_tx() {
|
|
262
360
|
Ok(tx_id) => serde_json::json!({ "ok": true, "txId": tx_id }).to_string(),
|
|
263
361
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
264
|
-
}
|
|
265
|
-
}
|
|
362
|
+
}
|
|
363
|
+
});
|
|
266
364
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
with_engine_mut(|eng| match eng.commit_tx(&tx_id) {
|
|
365
|
+
napi_engine_fn!(jsql_commit_tx, jsql_commit_tx_h, (tx_id: String) => |eng| {
|
|
366
|
+
match eng.commit_tx(&tx_id) {
|
|
270
367
|
Ok(()) => r#"{"ok":true}"#.to_string(),
|
|
271
368
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
272
|
-
}
|
|
273
|
-
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
274
371
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
with_engine_mut(|eng| match eng.rollback_tx(&tx_id) {
|
|
372
|
+
napi_engine_fn!(jsql_rollback_tx, jsql_rollback_tx_h, (tx_id: String) => |eng| {
|
|
373
|
+
match eng.rollback_tx(&tx_id) {
|
|
278
374
|
Ok(()) => r#"{"ok":true}"#.to_string(),
|
|
279
375
|
Err(e) => format!(r#"{{"ok":false,"error":"{}"}}"#, e),
|
|
280
|
-
}
|
|
281
|
-
}
|
|
376
|
+
}
|
|
377
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsql-neo",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.4",
|
|
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",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
],
|
|
47
47
|
"scripts": {
|
|
48
48
|
"postinstall": "node postinstall.js",
|
|
49
|
-
"test": "node test/smoke.js && node test/sql-parser.test.js && node test/readme-audit.test.js && node test/query-opt.test.js",
|
|
49
|
+
"test": "node test/smoke.js && node test/sql-parser.test.js && node test/readme-audit.test.js && node test/query-opt.test.js && node test/wal-recovery.test.js && node test/mysql-compat-hijack.test.js",
|
|
50
50
|
"test:parser": "node test/sql-parser.test.js",
|
|
51
51
|
"test:btree": "node test/btree.test.js",
|
|
52
52
|
"test:native": "node test/native.test.js",
|
|
53
53
|
"test:regress": "node test/regress-5.1.0.js",
|
|
54
|
-
"test:all": "node test/smoke.js && node test/sql-parser.test.js && node test/readme-audit.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",
|
|
55
|
-
"test:coverage": "c8 --all --include='lib/**' --exclude='lib/
|
|
54
|
+
"test:all": "node test/smoke.js && node test/sql-parser.test.js && node test/readme-audit.test.js && node test/native.test.js && node test/native-multi-instance.test.js && node test/wal-recovery.test.js && node test/mysql-compat-hijack.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",
|
|
55
|
+
"test:coverage": "c8 --all --include='lib/**' --exclude='lib/web_ui.js' --reporter=text --reporter=lcov node test/coverage.js",
|
|
56
56
|
"test:orms": "node examples/orms/run-all.js"
|
|
57
57
|
},
|
|
58
58
|
"keywords": [
|